Files
sx/examples/1143-diagnostics-typed-module-const-mismatch.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

36 lines
1.9 KiB
Plaintext

// A typed module-level constant whose initializer does not match its
// annotation is a compile-time type error — not a silently-accepted const.
// Each declaration below pairs an initializer with an incompatible annotation,
// so the compiler must emit a `type mismatch` diagnostic at the initializer and
// abort (exit 1) rather than registering a usable const.
//
// Regression (issue 0088): `N : string : 4` was accepted; `print(N)` then
// segfaulted (an integer emitted as a `string` const → a bogus pointer) and
// `[N]s64` folded `N` to 4. The fix rejects the declaration at the root. The
// validation is type-based, so a const-EXPRESSION initializer (`E : string :
// M + 2`, `V : string : -M`) is rejected just like a literal — not skipped
// because its node kind isn't a literal (issue 0088, attempt 2).
//
// The mixed-numeric pair (`s64 : M + 0.5`, `s64 : 0.5 + M`) is rejected in BOTH
// operand orders: arithmetic binary-op inference now promotes int+float to the
// float result (`Lowering.arithResultType`), so an s64 annotation no longer
// matches a float-producing initializer regardless of which operand is the
// float (issue 0088, attempt 3 — the inferExprType promotion root fix).
#import "modules/std.sx";
M :: 2;
N : string : 4; // integer literal where a string is annotated
F : s64 : "x"; // string literal where an integer is annotated
B : s64 : true; // boolean literal where an integer is annotated
G : s64 : 1.5; // float literal where an integer is annotated
E : string : M + 2; // integer EXPRESSION where a string is annotated
V : string : -M; // integer (unary) expression where a string is annotated
BAD : s64 : M + 0.5; // mixed int+float (int LHS) → f64, rejected vs s64
BAD2 : s64 : 0.5 + M; // mixed float+int (float LHS) → f64, rejected vs s64 — order-independent
main :: () {
print("unreachable\n");
}