Issue 0095: a typed local/param/field silently TRUNCATED a float initializer to an integer annotation (`y : s64 = 1.5` → 1) with no diagnostic. Agra ruled the UNIFIED rule (Option B): an implicit float→int in a typed binding behaves like the array-dimension rule — - an INTEGRAL compile-time float FOLDS to its int (`4.0` → 4, `-2.0` → -2); - a NON-integral float is a COMPILE ERROR (`1.5`, `4.5`); - explicit `xx` / `cast(T)` ALWAYS truncates (the escape hatch). Applied consistently to typed local / param-default / field-default, typed module CONST, and array dim — all reusing the single `program_index.floatToIntExact` / `evalConstIntExpr` facility (no second integral check). - `Builder.constFloatInfo` reads a compile-time `const_float` back from its Ref (value + span). - `coerceToType` is now the IMPLICIT path: its `.float_to_int` arm folds an integral const-float to `constInt`, else emits the narrowing diagnostic. `coerceExplicit` is the raw truncating path; `xx` (lowerXX) and `cast(T)` route through it so the escape still truncates. - Field-default lowering (struct-literal pad, named-field default, buildDefaultValue) now coerces the default to the field type at the IR level (was silently bit-coerced by emitStructInit). - Const path: `typedConstInitFits` accepts an integral float (literal or a `M + 2.0`-style expression folding via `evalComptimeInt`); `emitModuleConst` / `constExprValue` / `globalInitValue` fold an integral float to its int and reject a non-integral one — relaxing F0.7's blanket float rejection. Tests: examples/0168 (positive: local/field/param/const fold, xx/cast truncate), examples/1146 (negative: local/param/field error), integral-float const cases added to examples/0162; non-integral const cases in 1143 stay errors. specs.md + readme.md document the unified rule, cross-referencing the array-dim rule. issues/0095 marked RESOLVED.
27 lines
1.0 KiB
Plaintext
27 lines
1.0 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). The fix is the integral-fold /
|
|
// non-integral-error rule shared with the array-dimension path.
|
|
//
|
|
// 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.
|
|
#import "modules/std.sx";
|
|
|
|
Bad :: struct {
|
|
f : s64 = 3.5; // non-integral field default → error
|
|
}
|
|
|
|
badDefault :: (x : s64 = 2.5) -> s64 { return x; } // non-integral param default → error
|
|
|
|
main :: () {
|
|
y : s64 = 1.5; // non-integral local initializer → error
|
|
b := Bad.{};
|
|
print("{}\n", b.f);
|
|
print("{}\n", badDefault());
|
|
print("{}\n", y);
|
|
}
|