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:
@@ -95,6 +95,28 @@
|
||||
> path. This relaxes the F0.4 `examples/1132` wording (a non-integral float const
|
||||
> dim now reports the precise "non-integral float" message; it still errors).
|
||||
>
|
||||
> **Completion (F0.11 attempt 5)** — attempts 1–4 unified all five sites for
|
||||
> literal / int-const-expr / float-const-leaf forms, but `evalConstFloatExpr` still
|
||||
> LAGGED `evalConstIntExpr`: the int evaluator resolves a numeric-limit field-access
|
||||
> leaf (`f64.true_min`, `f64.max`) via `type_resolver.integerLimitFor`, but the
|
||||
> float evaluator had no parallel arm, so `y : s64 = f64.true_min + 0.5` (= 0.5)
|
||||
> truncated silently to 0 (the direct `f64.true_min` already errored via the IR-level
|
||||
> `constFloatInfo` path, but the *expression* form escaped). Closed by bringing the
|
||||
> two evaluators to PARITY:
|
||||
> - `evalConstFloatExpr` gains a `.field_access` arm that resolves a builtin FLOAT
|
||||
> numeric-limit accessor through `type_resolver.TypeResolver.floatLimitFor` (the
|
||||
> SAME facility `lowerNumericLimit` uses) — the float twin of the int evaluator's
|
||||
> `integerLimitFor` arm. Integer limits / `<pack>.len` are still resolved by the
|
||||
> int delegation, so only the float-limit case lands here.
|
||||
> - The audit also surfaced a missing `%` arm: the int evaluator folds `.mod` but
|
||||
> the float one did not, so `y : s64 = 5.5 % 2.0` (= 1.5) truncated silently to 1.
|
||||
> `evalConstFloatExpr` now handles `.mod` via `@rem` (matching `evalConstIntExpr`
|
||||
> and codegen's `frem`; `6.0 % 4.0` folds to 2 via the int delegation, `5.5 % 2.0`
|
||||
> = 1.5 is rejected). The two evaluators are now at full leaf/operator parity, so
|
||||
> no compile-time-const float shape escapes the rule at one site while folding at
|
||||
> another. (A comptime-fn returning float is a genuinely new form for BOTH and is
|
||||
> out of scope.)
|
||||
>
|
||||
> Regression tests: `examples/0168-types-integral-float-to-int.sx` (positive —
|
||||
> local/field/param/const fold, integral int-const-EXPRESSION (`M + 2.0`) AND
|
||||
> float-const-LEAF (`F + 1.5`, `F : f64 : 2.5`) fold at local/field/param/const,
|
||||
@@ -110,9 +132,15 @@
|
||||
> len 4; 1146 adds `[F + 0.25]s64` erroring; `examples/1132` now expects the
|
||||
> precise non-integral-float dim wording. Unit:
|
||||
> `program_index.test.zig` "evalConstFloatExpr folds comptime float expressions"
|
||||
> (covers the float-const leaf: `F` → 2.5, `F + 0.25` → 2.75, `F + 1.5` → 4.0) and
|
||||
> "foldCountI64 / foldDimU32 fold an integral float count, reject a non-integral
|
||||
> one" (the count fold + the `non_integral_float` / `below_min` distinction).
|
||||
> (covers the float-const leaf: `F` → 2.5, `F + 0.25` → 2.75, `F + 1.5` → 4.0;
|
||||
> attempt 5 adds the numeric-limit leaf `f64.max`/`f64.true_min`/`f32.epsilon`,
|
||||
> `f64.max - f64.max` → 0, `f64.true_min + 0.5` → 0.5, and the `%` arm `5.5 % 2.0`
|
||||
> → 1.5 / `% 0.0` → null) and "foldCountI64 / foldDimU32 fold an integral float
|
||||
> count, reject a non-integral one" (the count fold + the `non_integral_float` /
|
||||
> `below_min` distinction). Attempt 5 also extends 0168 (positive: `f64.max -
|
||||
> f64.max` → 0, `6.0 % 4.0` → 2, integer-limit `s8.max`/`[u8.max]` unregressed,
|
||||
> `xx` escapes for both new forms) and 1146 (negative: `f64.true_min + 0.5` and
|
||||
> `5.5 % 2.0` error at a binding site).
|
||||
|
||||
## Symptom
|
||||
A typed LOCAL (and likely typed param/field) silently truncates a floating-point
|
||||
|
||||
Reference in New Issue
Block a user