Files
sx/examples/0162-types-typed-module-const-roundtrip.sx
agra b69ec43ba3 fix(ir): infer mixed int+float arithmetic as the promoted float [F0.7]
`ExprTyper.inferType`'s binary-op arm inferred every non-comparison op
from the LHS alone, so `M + 0.5` (s64 + f64) statically typed as s64
while `0.5 + M` typed as f64 — operand-order-dependent. The value path
(`lowerBinaryOp`) already promoted int×float → float, so static
inference disagreed with the value: `M + 0.5` formatted as a truncated
int and a typed const `BAD : s64 : M + 0.5` was accepted+truncated
(issue 0088 mixed-numeric escape).

Extract the value path's inline promotion into a shared
`Lowering.arithResultType(lhs, rhs)` and reuse it at both sites, so
arithmetic / bitwise / shift inference reports exactly the type the
lowered value carries — int LHS × float RHS → the float, order-
independent. The value-path behavior is unchanged (the block is moved
verbatim into the helper), so no IR shifts; the suite stays green. The
typed-const validation reuses `inferExprType`, so this auto-closes the
escape with no change to the validation logic.

- examples/1143: BAD/BAD2 (`s64 : M + 0.5`, `s64 : 0.5 + M`) rejected
  in both operand orders.
- examples/0162: MF/MFR (`f64 : M + 0.5`, `f64 : 0.5 + M`) fold to 2.5.
- examples/0163 (new): pins the inference fix in a value context
  (`print("{}", n + 0.5)` formats the float, both orders, +-*/, f32).
- expr_typer.test.zig: arithResultType + mixed-arithmetic inference.
- specs.md / readme.md: document the numeric-promotion rule.
- issues/0088: RESOLVED banner notes the inferExprType root fix.
2026-06-05 08:23:59 +03:00

59 lines
2.2 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)
//
// 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;
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);
}