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.
42 lines
2.3 KiB
Plaintext
42 lines
2.3 KiB
Plaintext
// Unified float→int narrowing rule (F0.11), NEGATIVE side: a NON-INTEGRAL float
|
|
// implicitly narrowing to an integer-typed binding is a COMPILE ERROR — not a
|
|
// silent truncation. The rule fires at a typed LOCAL initializer, a function
|
|
// PARAM default, and a struct FIELD default; each emits a narrowing diagnostic
|
|
// at the offending float and aborts (exit 1). It fires whether the float is a
|
|
// LITERAL (`1.5`), an INT-const-expression (`M + 0.5`, with `M :: 2`), or a
|
|
// FLOAT-const-leaf expression (`F + 0.25`, with `F : f64 : 2.5`, = 2.75) — all
|
|
// three are the core of issue 0095, which previously slipped through and
|
|
// truncated to 2. The fix is the integral-fold / non-integral-error rule shared
|
|
// with the array-dimension path, applied to ANY compile-time-constant float
|
|
// expression (literal, int-const leaf, float-const leaf, and combinations).
|
|
//
|
|
// The escape hatch stays open: `y : s64 = xx 1.5` (or `cast(s64) 1.5`)
|
|
// truncates with no error — exercised on the POSITIVE side (example 0168).
|
|
//
|
|
// Regression (issue 0095): `y : s64 = 1.5` silently truncated to 1,
|
|
// `y : s64 = M + 0.5` to 2, and `y : s64 = F + 0.25` (float-const leaf) to 2.
|
|
#import "modules/std.sx";
|
|
|
|
M :: 2; // int module const, for the INT-const-EXPRESSION cases
|
|
F : f64 : 2.5; // float module const, for the FLOAT-const-LEAF cases
|
|
|
|
Bad :: struct {
|
|
f : s64 = 3.5; // non-integral float LITERAL field default → error
|
|
fe : s64 = M + 0.5; // non-integral int-const-EXPR field default → error
|
|
ff : s64 = F + 0.25; // non-integral float-const-LEAF field default → error
|
|
}
|
|
|
|
badLit :: (x : s64 = 2.5) -> s64 { return x; } // non-integral LITERAL param default → error
|
|
badExpr :: (x : s64 = M + 0.5) -> s64 { return x; } // non-integral int-const-EXPR param default → error
|
|
badFlt :: (x : s64 = F + 0.25) -> s64 { return x; } // non-integral float-const-LEAF param default → error
|
|
|
|
main :: () {
|
|
y : s64 = 1.5; // non-integral float LITERAL local → error
|
|
ye : s64 = M + 0.5; // non-integral int-const-EXPRESSION local → error
|
|
yf : s64 = F + 0.25; // non-integral float-const-LEAF local → error
|
|
b := Bad.{};
|
|
print("{} {} {}\n", b.f, b.fe, b.ff);
|
|
print("{} {} {}\n", badLit(), badExpr(), badFlt());
|
|
print("{} {} {}\n", y, ye, yf);
|
|
}
|