49a36bb4929f0bc2f0b9d87ff8331ee41d2cfdfd
6 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e442cdf5e7 |
fix(ir): float / folds as FLOAT division under the unified narrowing rule — int folder refuses a float-operand / [F0.11]
The shared compile-time integer folder (`evalConstIntExpr`) accepts an integral float literal/const as an integer leaf (`[4.0]` → 4) and then applied INTEGER arithmetic to the whole expression — so `5.0 / 2.0` folded as `divTrunc(5,2)` = 2 instead of float division (`2.5`). The bug fired at all FIVE unified-rule sites (typed local, field default, param default, typed const, array dimension), because the typed sites evaluate through `evalConstFloatExpr` (which delegates the node to the int folder) and the count sites through `foldCountI64` (int folder first). Fix at the single root: `evalConstIntExpr`'s `.div` arm refuses to fold a division whose lhs/rhs is float-valued (`isFloatValuedExpr`), so the value surfaces through `evalConstFloatExpr` + the unified rule — an integral quotient (`6.0 / 2.0` → 3) folds, a non-integral one (`5.0 / 2.0` = 2.5, mixed `5 / 2.0`, float-const `F / G`) errors. Genuine integer `/` (`5 / 2` → 2) is unchanged; `*`/`+`/`-` need no guard (they agree between int and float for the integral operands the int folder ever sees). `isFloatValuedExpr` judges a const-leaf by VALUE (`moduleConstIsFloatTyped` recurses into the const's value with the existing cycle-guard frame), so an untyped float-EXPRESSION const (`ME :: 4.0 + 1.0`, placeholder type s64) is caught at both the count path and — via `foldComptimeFloatInit`'s guard — the typed-binding path. A backtick RAW receiver (`` `f64.epsilon ``) is a field read, not a float limit (is_raw check, issues 0092/0093). Regression: examples/1147 (negative — `5.0 / 2.0` errors at all five sites plus untyped float-EXPR const div); 0168 extended (positive — `6.0 / 2.0`, `12.0 / 4.0`, `[6.0/2.0]`, `xx (5.0/2.0)` → 2); unit tests "the int folder refuses a FLOAT division" and "moduleConstIsFloatTyped judges a const by VALUE". specs.md + readme.md state the float-`/` rule. |
||
|
|
74f675ac0b |
fix(ir): evalConstFloatExpr reaches parity with evalConstIntExpr — numeric-limit float leaves + float % fold under the unified rule [F0.11]
The compile-time float evaluator lagged the integer one: it had no numeric-limit field-access arm, so `y : s64 = f64.true_min + 0.5` (=0.5) silently truncated to 0 even though the direct `f64.true_min` already errored; the arm-by-arm audit also found a missing `%` arm, so `y : s64 = 5.5 % 2.0` (=1.5) silently truncated to 1. Bring evalConstFloatExpr to PARITY with evalConstIntExpr: - Add a `.field_access` arm resolving a builtin FLOAT numeric-limit accessor (`f64.max`, `f32.epsilon`, `f64.true_min`, …) via the SAME `type_resolver.floatLimitFor` that `lowerNumericLimit` uses — the float twin of the int evaluator's `integerLimitFor` arm. - Add a `.mod` arm via `@rem` (matching evalConstIntExpr and codegen's `frem`): `6.0 % 4.0` folds to 2 (via int delegation), `5.5 % 2.0` = 1.5 is rejected. The two evaluators now share every leaf/operator shape, so no compile-time-const float form escapes the unified float→int rule at one site while folding at another. All five sites (local/field/param/const/ array-dim) stay consistent. Regression: 0168 (positive) adds `f64.max - f64.max` → 0, `6.0 % 4.0` → 2, integer-limit `s8.max`/`[u8.max]` unregressed, `xx` escapes for both new forms; 1146 (negative) adds `f64.true_min + 0.5` and `5.5 % 2.0` erroring at a binding site; program_index.test.zig covers the floatLimitFor arm and the `%` arm. specs.md + readme.md state the parity. issues/0095 RESOLVED banner gains the attempt-5 entry. |
||
|
|
b73363ca4c |
fix(ir): array-dim/count path joins the unified float→int rule — all 5 sites consistent [F0.11]
The compile-time count fold (array dimension / Vector lane / value-param) was
integer-only: it folded a DIRECT integral float literal (`[4.0]`, `[N]` with
`N : f64 : 4.0`) but rejected an INTEGRAL expression built from a non-integral
float-const leaf (`[F + 1.5]` = 4.0, `F : f64 : 2.5`) — and a const folded from
one (`[K]` with `K : s64 : F + 1.5`) — as "must be a compile-time integer
constant". This was the last of issue 0095's five narrowing sites (local /
field / param / const / array-dim) still diverging.
Route the count fold through the SAME compile-time float evaluation the other
four sites use:
- New `program_index.foldCountI64` — the single int-or-integral-float count
fold: `evalConstIntExpr` first, then (only on failure) `evalConstFloatExpr` +
`floatToIntExact`. `foldDimU32` (dim/lane/u32 value-param), the non-u32
value-param gate, and `emitModuleConst`'s integer-const materialization all
delegate to it, so a const's emitted value and its use as a count come from
one fold (no parallel integral check, no two-resolver divergence — issue 0083).
- New `DimU32.non_integral_float` variant carries a non-integral float dim to a
distinct, accurate diagnostic ("array dimension must be an integer, but '2.75'
is a non-integral float") — the cast-escape advice the binding sites give does
not apply in a count position, so the dim wording omits it. `reportDimError`,
the Vector-lane resolver, and the top-level array-alias diagnostic all handle
the new variant, so the DIRECT and type-ALIAS forms emit the identical message.
- `type_bridge.StatelessInner.lookupFloatName` (via `moduleConstFloat`) is the
float twin of its `lookupDimName`, so the registration-time alias path folds a
float-const-leaf dimension to the SAME count as the stateful direct path.
`inline for` range bounds are spec endpoints, not counts (specs.md §2), so they
keep the int-only fold deliberately (no silent-truncation bug there).
Relaxes the F0.4 `examples/1132` wording: a non-integral float const dim now
reports the precise "non-integral float" message (it still errors).
Regression: 0168 (positive — `[F + 1.5]s64`, `[KF]s64`, alias `ArrFE` all fold
to len 4), 1146 (negative — `[F + 0.25]s64` errors), 1132 (precise wording), and
a `foldCountI64`/`foldDimU32` unit test. issues/0095 marked RESOLVED (attempt 4).
specs.md + readme.md state the unified rule across all five sites.
|
||
|
|
b6d66d9c56 |
fix(ir): complete const-float evaluator — resolve float-const leaves too [F0.11]
Completes issue 0095: a non-integral float→int narrowing via a FLOAT-const leaf (`F : f64 : 2.5; y : s64 = F + 0.25` = 2.75) silently truncated to 2. `evalConstFloatExpr` delegated only INTEGER leaves to `evalConstIntExpr` and had no float-const leaf arm, so the unified rule never saw the value. - program_index.zig: add `moduleConstFloat`/`moduleConstFloatFramed` — the f64 twin of `moduleConstInt` (same `isCountableConstType` gate, same cyclic- definition frame), recovering a numeric module const's value via `evalConstFloatExpr`. Add `lookupFloatName` to `ModuleConstCtx` and the `.identifier`/`.type_expr` leaf arms to `evalConstFloatExpr` that call it. Integer / integral-float leaves keep resolving through the existing `evalConstIntExpr` delegation, so the unified rule now applies to ANY compile-time-constant float expression — literal, int-const leaf, float-const leaf, and combinations — at every binding site. - lower.zig: add `Lowering.lookupFloatName` delegating to `moduleConstFloat`. Route `typedConstInitFits`' integral-fold check through `evalConstFloatExpr` + `floatToIntExact` (the SAME facility `foldComptimeFloatInit` uses) instead of the int-only `evalComptimeInt`, which folded leaf-by-leaf in i64 and so rejected an integral SUM built from a non-integral float leaf (`K : s64 : F + 1.5` = 4.0 now folds; `K : s64 : F + 0.25` errors). A LOCAL `::` const leaf is a scope ref (not in the const tables) so neither the int nor float evaluator folds it — float now matches int exactly there. Regression: examples/1146 (negative) + 0168 (positive) extended with float-const-leaf cases at local/field/param/const; unit test in program_index.test.zig covers the leaf resolution (F→2.5, F+0.25→2.75, F+1.5→4.0). specs.md + readme.md state the rule covers any compile-time-const float expression incl. float-typed const leaves. issues/0095 banner updated. Gate: zig build + zig build test green; 447 examples pass, 0 failed. |
||
|
|
43d44fff75 |
fix(ir): narrow non-integral const-float EXPRESSIONS at typed local/field/param; align const message [F0.11]
Completes issue 0095 (attempt 2). The attempt-1 coerce arm only caught a direct `const_float` literal, so a non-integral const-folded float EXPRESSION still truncated silently at a typed local / field default / param default: M :: 2; local : s64 = M + 0.5; // → 2 (silent truncation — BUG; now ERRORS) fld : s64 = M + 0.5; // field default — same take(x : s64 = M + 0.5) // param default — same while the typed-CONST site already errored. The integral expression (`M + 2.0` → 4) folded but the runtime/explicit-cast paths must stay untouched. Fix: - New `program_index.evalConstFloatExpr` — the f64 counterpart to `evalConstIntExpr`, delegating every integer subtree back to it (no parallel integer logic) and adding only the float literal / unary-negate / `+ - * /` arms. Pure (no diagnostics, no resolution side effects). - `Lowering.foldComptimeFloatInit` applies the unified rule to a typed-binding initializer EXPRESSION: an integral comptime float folds to its `constInt`, a non-integral one errors, a genuine runtime float / `xx`-cast falls through to the normal path. It runs `evalConstFloatExpr` FIRST (pure) so a `$pack[i]` argument is never spuriously type-resolved outside an active binding, then gates on `isFloat(inferExprType)` so a plain comptime int is left alone. Wired into the typed-local path, the three struct field-default sites (via a shared `lowerCoercedDefault`), and the call-argument loop (covers expanded param defaults). - One `Lowering.diagNonIntegralNarrow` now emits the narrowing wording at all five sites (coerce arm, global init, const-expr value, the typed-binding sites, and the typed-const path). The typed-CONST non-integral diagnostic therefore reads "cannot implicitly narrow non-integral float …" instead of the stale "initializer is a float literal / floating-point expression". Tests: examples/1146 (negative) extended with non-integral const-EXPRESSION cases at local/field/param; examples/0168 (positive) extended with integral const-EXPRESSION folds and `xx (M + 0.5)` truncation; examples/1143 reconciled to the aligned const message (G/BAD/BAD2 stay errors); unit test `evalConstFloatExpr folds comptime float expressions`. Full gate green (447). |
||
|
|
4c12e1de38 |
fix(ir): unify float→int narrowing — integral folds, non-integral errors [F0.11]
Issue 0095: a typed local/param/field silently TRUNCATED a float initializer to an integer annotation (`y : s64 = 1.5` → 1) with no diagnostic. Agra ruled the UNIFIED rule (Option B): an implicit float→int in a typed binding behaves like the array-dimension rule — - an INTEGRAL compile-time float FOLDS to its int (`4.0` → 4, `-2.0` → -2); - a NON-integral float is a COMPILE ERROR (`1.5`, `4.5`); - explicit `xx` / `cast(T)` ALWAYS truncates (the escape hatch). Applied consistently to typed local / param-default / field-default, typed module CONST, and array dim — all reusing the single `program_index.floatToIntExact` / `evalConstIntExpr` facility (no second integral check). - `Builder.constFloatInfo` reads a compile-time `const_float` back from its Ref (value + span). - `coerceToType` is now the IMPLICIT path: its `.float_to_int` arm folds an integral const-float to `constInt`, else emits the narrowing diagnostic. `coerceExplicit` is the raw truncating path; `xx` (lowerXX) and `cast(T)` route through it so the escape still truncates. - Field-default lowering (struct-literal pad, named-field default, buildDefaultValue) now coerces the default to the field type at the IR level (was silently bit-coerced by emitStructInit). - Const path: `typedConstInitFits` accepts an integral float (literal or a `M + 2.0`-style expression folding via `evalComptimeInt`); `emitModuleConst` / `constExprValue` / `globalInitValue` fold an integral float to its int and reject a non-integral one — relaxing F0.7's blanket float rejection. Tests: examples/0168 (positive: local/field/param/const fold, xx/cast truncate), examples/1146 (negative: local/param/field error), integral-float const cases added to examples/0162; non-integral const cases in 1143 stay errors. specs.md + readme.md document the unified rule, cross-referencing the array-dim rule. issues/0095 marked RESOLVED. |