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.
This commit is contained in:
13
specs.md
13
specs.md
@@ -897,7 +897,9 @@ be an integer, but '4.5' is a non-integral float"). This holds however the float
|
||||
is written — a literal (`4.0`), a float-typed const (`N : f64 : 4.0`), or a
|
||||
const **expression** whose value is integral, including one built from a
|
||||
non-integral float-const leaf (`F : f64 : 2.5; [F + 1.5]s64` ≡ `[4]s64`, and
|
||||
likewise through a const, `K : s64 : F + 1.5; [K]s64`). A count and a typed
|
||||
likewise through a const, `K : s64 : F + 1.5; [K]s64`), a builtin float
|
||||
numeric-limit accessor (`[f64.max - f64.max]s64` → length 0), or a float `%`. A
|
||||
count and a typed
|
||||
binding's float→integer initializer share the *same* compile-time float
|
||||
evaluation, so they agree at every site — direct, through a const, or via a type
|
||||
alias (see "Implicit float → integer", §2 Type Conversions).
|
||||
@@ -1437,10 +1439,15 @@ array dimension / lane count uses (see "Array dimensions are integral", §2):
|
||||
`n : s64 = -2.0` ≡ `-2`, `y : s64 = M + 2.0` → 4 (`M :: 2`). A const expression
|
||||
here is *any* compile-time-constant float expression — an integer-const leaf
|
||||
(`M + 2.0`), a float-typed const leaf (`F : f64 : 2.5; y : s64 = F + 1.5` → 4),
|
||||
or any combination of them.
|
||||
a builtin float numeric-limit accessor (`f64.max - f64.max` → 0), a float `%`
|
||||
(`6.0 % 4.0` → 2), or any combination of them. The compile-time float evaluator
|
||||
recognises every leaf/operator shape the integer evaluator does (literal, named
|
||||
const, numeric-limit accessor, `+ - * / %`, unary negate), so no constant float
|
||||
form folds at one site while truncating at another.
|
||||
- A **non-integral** compile-time float — literal OR const expression — is a
|
||||
**compile error** with one uniform wording at every site:
|
||||
`y : s64 = 1.5`, `y : s64 = M + 0.5`, and `y : s64 = F + 0.25` (= 2.75) all →
|
||||
`y : s64 = 1.5`, `y : s64 = M + 0.5`, `y : s64 = F + 0.25` (= 2.75),
|
||||
`y : s64 = f64.true_min + 0.5` (= 0.5), and `y : s64 = 5.5 % 2.0` (= 1.5) all →
|
||||
"cannot implicitly narrow non-integral float '…' to 's64'; use an explicit
|
||||
cast (`xx`/`cast`)".
|
||||
- This applies uniformly to a typed **local**, a function **param default**, a
|
||||
|
||||
Reference in New Issue
Block a user