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.
69 lines
2.8 KiB
Plaintext
69 lines
2.8 KiB
Plaintext
// Valid typed module-level constants compile, fold, and print correctly across
|
|
// every initializer/annotation pairing the registrar accepts:
|
|
// - integer literal → integer (`K : s64 : 4`) — usable as an array count too
|
|
// - integer literal → float (`W : f32 : 800`)
|
|
// - float literal → float (`PI : f32 : 3.14159`)
|
|
// - string literal → string (`S : string : "hi"`)
|
|
// - null → pointer (`P : *void : null`)
|
|
// - integer EXPRESSION → integer (`KE : s64 : M + 2`) — usable as a count too
|
|
// - integer EXPRESSION → float (`WE : f32 : M + 2`)
|
|
// - MIXED int+float EXPRESSION → float (`MF : f64 : M + 0.5`, both operand orders)
|
|
// - INTEGRAL float literal → integer (`KF : s64 : 4.0` → 4) — folds under the
|
|
// unified narrowing rule (F0.11), usable as a count too
|
|
// - INTEGRAL float EXPRESSION → integer (`KFE : s64 : M + 2.0` → 4)
|
|
//
|
|
// Companion to the negative example 1143: the issue-0088 fix rejects a typed
|
|
// const whose initializer mismatches its annotation, and these correctly-typed
|
|
// consts must keep working (no over-rejection) — including const-EXPRESSION
|
|
// initializers, whose type-based validation (attempt 2) must accept a correctly
|
|
// typed expression even though it isn't a literal.
|
|
//
|
|
// `MF`/`MFR` pin the attempt-3 inferExprType promotion fix: a mixed int+float
|
|
// arithmetic expression infers as the float result regardless of operand order,
|
|
// so it matches an `f64` annotation (and folds to 2.5, not a truncated 2).
|
|
#import "modules/std.sx";
|
|
|
|
M :: 2;
|
|
|
|
K : s64 : 4;
|
|
W : f32 : 800;
|
|
PI : f32 : 3.14159;
|
|
S : string : "hi";
|
|
P : *void : null;
|
|
KE : s64 : M + 2;
|
|
WE : f32 : M + 2;
|
|
MF : f64 : M + 0.5;
|
|
MFR : f64 : 0.5 + M;
|
|
KF : s64 : 4.0; // integral float literal → folds to 4
|
|
KFE : s64 : M + 2.0; // integral float expression → folds to 4
|
|
|
|
main :: () {
|
|
// Integer const: prints AND drives an array dimension (len 4).
|
|
a : [K]s64 = ---;
|
|
a[0] = 10;
|
|
a[3] = 40;
|
|
print("K={} len={} a0={} a3={}\n", K, a.len, a[0], a[3]);
|
|
|
|
// Integer-into-float and float consts print as floats.
|
|
print("W={} PI={}\n", W, PI);
|
|
|
|
// String const prints its text.
|
|
print("S={}\n", S);
|
|
|
|
// Null pointer const is null.
|
|
print("P_is_null={}\n", P == null);
|
|
|
|
// Integer const-EXPRESSION: prints AND drives an array dimension (len 4).
|
|
b : [KE]s64 = ---;
|
|
print("KE={} len={} WE={}\n", KE, b.len, WE);
|
|
|
|
// Mixed int+float const-EXPRESSION folds to the promoted float (2.5),
|
|
// operand-order-independent.
|
|
print("MF={} MFR={}\n", MF, MFR);
|
|
|
|
// Integral float const (literal + expression): folds to its integer under
|
|
// the unified narrowing rule; `KF` also drives an array dimension (len 4).
|
|
cc : [KF]s64 = ---;
|
|
print("KF={} len={} KFE={}\n", KF, cc.len, KFE);
|
|
}
|