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.
This commit is contained in:
agra
2026-06-05 19:26:22 +03:00
parent 74f675ac0b
commit e442cdf5e7
13 changed files with 393 additions and 18 deletions

View File

@@ -130,8 +130,10 @@ array dimension uses: an **integral** compile-time float folds to its integer, a
or *any* compile-time-constant float expression — including one that references a
float-typed const (`F : f64 : 2.5; y : s64 = F + 1.5` → `4`), a builtin float
numeric-limit accessor (`f64.max - f64.max` → `0`, while `f64.true_min + 0.5`
errors), or a float `%` (`6.0 % 4.0` → `2`, while `5.5 % 2.0` = `1.5` errors): the
compile-time float evaluator recognises every leaf shape the integer one does, so
errors), a float `%` (`6.0 % 4.0` → `2`, while `5.5 % 2.0` = `1.5` errors), or a
float `/` (`6.0 / 2.0` → `3`, while `5.0 / 2.0` = `2.5` errors — a float `/` is
always float division, never integer truncation, even with integral operands):
the compile-time float evaluator recognises every leaf shape the integer one does, so
no constant float form escapes the rule at one site while folding at another — and
is uniform
across a typed local, a parameter default, a struct field default, a call