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.
This commit is contained in:
agra
2026-06-05 08:23:59 +03:00
parent 454ea06bd4
commit b69ec43ba3
14 changed files with 218 additions and 52 deletions

View File

@@ -3,3 +3,4 @@ W=800.000000 PI=3.141590
S=hi
P_is_null=true
KE=4 len=4 WE=4.000000
MF=2.500000 MFR=2.500000

View File

@@ -0,0 +1 @@
0

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,5 @@
add: 2.500000 2.500000
mul: 3.000000 3.000000
sub: 1.500000 div: 0.500000
f32: 2.500000
int: 5

View File

@@ -1,35 +1,47 @@
error: type mismatch: constant 'N' is declared 'string' but its initializer is an integer literal
--> examples/1143-diagnostics-typed-module-const-mismatch.sx:18:14
--> examples/1143-diagnostics-typed-module-const-mismatch.sx:24:15
|
18 | N : string : 4; // integer literal where a string is annotated
| ^
24 | N : string : 4; // integer literal where a string is annotated
| ^
error: type mismatch: constant 'F' is declared 's64' but its initializer is a string literal
--> examples/1143-diagnostics-typed-module-const-mismatch.sx:19:14
--> examples/1143-diagnostics-typed-module-const-mismatch.sx:25:15
|
19 | F : s64 : "x"; // string literal where an integer is annotated
| ^^^
25 | F : s64 : "x"; // string literal where an integer is annotated
| ^^^
error: type mismatch: constant 'B' is declared 's64' but its initializer is a boolean literal
--> examples/1143-diagnostics-typed-module-const-mismatch.sx:20:14
--> examples/1143-diagnostics-typed-module-const-mismatch.sx:26:15
|
20 | B : s64 : true; // boolean literal where an integer is annotated
| ^^^^
26 | B : s64 : true; // boolean literal where an integer is annotated
| ^^^^
error: type mismatch: constant 'G' is declared 's64' but its initializer is a float literal
--> examples/1143-diagnostics-typed-module-const-mismatch.sx:21:14
--> examples/1143-diagnostics-typed-module-const-mismatch.sx:27:15
|
21 | G : s64 : 1.5; // float literal where an integer is annotated
| ^^^
27 | G : s64 : 1.5; // float literal where an integer is annotated
| ^^^
error: type mismatch: constant 'E' is declared 'string' but its initializer is an integer expression
--> examples/1143-diagnostics-typed-module-const-mismatch.sx:22:14
--> examples/1143-diagnostics-typed-module-const-mismatch.sx:28:15
|
22 | E : string : M + 2; // integer EXPRESSION where a string is annotated
| ^^^^^
28 | E : string : M + 2; // integer EXPRESSION where a string is annotated
| ^^^^^
error: type mismatch: constant 'V' is declared 'string' but its initializer is an integer expression
--> examples/1143-diagnostics-typed-module-const-mismatch.sx:23:14
--> examples/1143-diagnostics-typed-module-const-mismatch.sx:29:15
|
23 | V : string : -M; // integer (unary) expression where a string is annotated
| ^^
29 | V : string : -M; // integer (unary) expression where a string is annotated
| ^^
error: type mismatch: constant 'BAD' is declared 's64' but its initializer is a floating-point expression
--> examples/1143-diagnostics-typed-module-const-mismatch.sx:30:15
|
30 | BAD : s64 : M + 0.5; // mixed int+float (int LHS) → f64, rejected vs s64
| ^^^^^^^
error: type mismatch: constant 'BAD2' is declared 's64' but its initializer is a floating-point expression
--> examples/1143-diagnostics-typed-module-const-mismatch.sx:31:15
|
31 | BAD2 : s64 : 0.5 + M; // mixed float+int (float LHS) → f64, rejected vs s64 — order-independent
| ^^^^^^^