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:
19
specs.md
19
specs.md
@@ -898,7 +898,10 @@ 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 builtin float
|
||||
numeric-limit accessor (`[f64.max - f64.max]s64` → length 0), or a float `%`. A
|
||||
numeric-limit accessor (`[f64.max - f64.max]s64` → length 0), a float `%`, or a
|
||||
float `/` whose quotient is integral (`[6.0 / 2.0]s64` ≡ `[3]s64`; a non-integral
|
||||
quotient like `[5.0 / 2.0]s64` = 2.5 is rejected — a float `/` is always float
|
||||
division, never integer truncation, even when both operands are integral). 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
|
||||
@@ -1440,14 +1443,18 @@ array dimension / lane count uses (see "Array dimensions are integral", §2):
|
||||
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),
|
||||
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.
|
||||
(`6.0 % 4.0` → 2), or a float `/` whose quotient is integral (`6.0 / 2.0` → 3),
|
||||
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 float `/` is always FLOAT
|
||||
division even when both operands are integral — `6.0 / 2.0` is `3.0` (folds),
|
||||
but `5.0 / 2.0` is `2.5` (errors) — never integer truncating division.
|
||||
- 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`, `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 →
|
||||
`y : s64 = f64.true_min + 0.5` (= 0.5), `y : s64 = 5.5 % 2.0` (= 1.5), and
|
||||
`y : s64 = 5.0 / 2.0` (= 2.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