fix(ir): route every comptime-int through the shared evaluator (0083)
Attempts 1–4 fixed the array-dimension paths but the same length-0 fabrication class survived on every other site that resolves a compile-time integer. Unify them all on the single shared `program_index.evalConstIntExpr` so they cannot diverge: - All three Vector lane resolvers (resolveTypeCallWithBindings, resolveParameterizedWithBindings, resolveArrayLiteralType) and both generic value-param binders (instantiateGenericStruct, instantiateTypeFunction) hand-rolled an `else => 0` switch. A module-const lane `Vector(N, f32)` fabricated a 0-lane `<0 x float>` (LLVM "huge alignment" abort); a value-param `Vec(N, f32)` fabricated a 0 binding / wrong mangled name. They now fold through the shared evaluator and emit a clean diagnostic + `.unresolved` on a non-const operand (resolveVectorLane / resolveValueParamArg) — never 0. - evalComptimeInt (inline-for bounds) delegated to the shared evaluator, so `inline for 0..M` / `0..(M+1)` fold like array dims. The `<pack>.len` leaf moved into the shared folder via a new `ctx.lookupPackLen`. - The unknown-type semantic checker no longer walks a value-param position (`Vector(N, …)` / `Vec(N, …)`) as a type name (was reporting "unknown type 'N'"). - The parameterized-type-arg parser and the function-body lookahead (hasFnBodyAfterArrow) accept a const-EXPRESSION in a value position, so `Vector(M + 1, f32)` and `[M + 1]T` parse as a return type too (the latter a pre-existing array-dim sibling that the same heuristic broke). Regressions: examples/1501 (named-const + const-expr lane, direct + alias, 3/4-lane reads), 1502 (runtime lane clean-halts, exit 1, no LLVM crash), 0207 (Vec(N)/Vec(M+1) == Vec(3) instantiation), 0610 (inline-for const bounds). Shared-evaluator unit test extended with the pack-len arm. zig build && zig build test && bash tests/run_examples.sh: 395 passed, 0 failed.
This commit is contained in:
29
examples/0207-generics-value-param-const.sx
Normal file
29
examples/0207-generics-value-param-const.sx
Normal file
@@ -0,0 +1,29 @@
|
||||
// A generic value parameter (`$K: u32`) bound from a named const or a
|
||||
// constant-foldable expression resolves to the SAME monomorphised instantiation
|
||||
// as the literal form: `Vec(N, f32)` (N a module const) and `Vec(M + 1, f32)`
|
||||
// (a const expression) are both `Vec(3, f32)`. The struct-copy assignment is the
|
||||
// proof — it type-checks only because the two spellings name one instantiation.
|
||||
//
|
||||
// Regression (issue 0083): the value-param binder hand-rolled an `else => 0`
|
||||
// switch, so a named-const value arg either fabricated a 0 binding under a wrong
|
||||
// mangled name or was rejected outright as "unknown type 'N'". It now folds
|
||||
// through the shared const-int evaluator (`program_index.evalConstIntExpr`).
|
||||
#import "modules/std.sx";
|
||||
|
||||
N :: 3;
|
||||
M :: 2;
|
||||
|
||||
Vec :: struct ($K: u32, $T: Type) { data: [K]T; }
|
||||
|
||||
main :: () {
|
||||
a : Vec(N, f32) = ---; // named-const value param
|
||||
a.data[0] = 10.0; a.data[1] = 20.0; a.data[2] = 30.0;
|
||||
print("named: len={} a0={} a2={}\n", a.data.len, a.data[0], a.data[2]);
|
||||
|
||||
e : Vec(M + 1, f32) = ---; // const-expr value param (M + 1 == 3)
|
||||
e.data[0] = 1.0; e.data[2] = 9.0;
|
||||
print("expr: len={} e2={}\n", e.data.len, e.data[2]);
|
||||
|
||||
b : Vec(3, f32) = a; // same instantiation → struct copy type-checks
|
||||
print("copy: len={} b2={}\n", b.data.len, b.data[2]);
|
||||
}
|
||||
23
examples/0610-comptime-inline-for-const-bound.sx
Normal file
23
examples/0610-comptime-inline-for-const-bound.sx
Normal file
@@ -0,0 +1,23 @@
|
||||
// `inline for 0..K` with a named-const or constant-foldable bound unrolls at
|
||||
// compile time, just like a literal bound.
|
||||
//
|
||||
// Regression (issue 0083): the inline-for bound folder (`evalComptimeInt`) only
|
||||
// handled literals, comptime cursors, and `<pack>.len`, so `inline for 0..M`
|
||||
// (M a module const) and `inline for 0..(M + 1)` (a const expression) both
|
||||
// failed with "range end is not a compile-time integer". `evalComptimeInt` now
|
||||
// delegates to the single shared const-int evaluator
|
||||
// (`program_index.evalConstIntExpr`), so the inline-for bound and an array
|
||||
// dimension fold the same shapes to the same value.
|
||||
#import "modules/std.sx";
|
||||
|
||||
M :: 3;
|
||||
|
||||
main :: () {
|
||||
s := 0;
|
||||
inline for 0..M: (i) { s += i; }
|
||||
print("sum 0..M = {}\n", s); // 0 + 1 + 2 = 3
|
||||
|
||||
t := 0;
|
||||
inline for 0..(M + 1): (i) { t += i; }
|
||||
print("sum 0..(M+1) = {}\n", t); // 0 + 1 + 2 + 3 = 6
|
||||
}
|
||||
35
examples/1501-vectors-const-lane.sx
Normal file
35
examples/1501-vectors-const-lane.sx
Normal file
@@ -0,0 +1,35 @@
|
||||
// A `Vector` lane count from a named const or a constant-foldable expression
|
||||
// resolves to the SAME layout as a literal lane — DIRECT (param / return type)
|
||||
// and via a type ALIAS. A 3-lane (named const `N`) and a 4-lane (const expr
|
||||
// `M + 1`) prove the lane VALUE is folded, not fabricated: reading `.w` requires
|
||||
// the 4-lane vector to actually have four lanes.
|
||||
//
|
||||
// Regression (issue 0083): the stateful Vector lane resolvers hand-rolled an
|
||||
// `else => 0` switch, so a module-const lane (`Vector(N, f32)`) lowered a 0-lane
|
||||
// `<0 x float>` and died in LLVM verification ("huge alignment values are
|
||||
// unsupported"); a const-expr lane (`Vector(M + 1, f32)`) was rejected at parse.
|
||||
// Both now fold through the single shared const-int evaluator
|
||||
// (`program_index.evalConstIntExpr`) — the same one the array-dimension path
|
||||
// uses — so a named-const / const-expr lane is identical to a literal lane.
|
||||
#import "modules/std.sx";
|
||||
|
||||
N :: 3;
|
||||
M :: 3;
|
||||
|
||||
LaneAlias :: Vector(N, f32); // ALIAS: 3-lane via named const.
|
||||
ExprAlias :: Vector(M + 1, f32); // ALIAS: 4-lane via const expression.
|
||||
|
||||
mk3 :: () -> Vector(N, f32) { .[1.0, 2.0, 3.0] } // DIRECT named-const lane.
|
||||
mk4 :: () -> Vector(M + 1, f32) { .[1.0, 2.0, 3.0, 4.0] } // DIRECT const-expr lane.
|
||||
|
||||
main :: () {
|
||||
a := mk3();
|
||||
print("direct3: {} {} {}\n", a.x, a.y, a.z);
|
||||
b := mk4();
|
||||
print("direct4: {} {} {} {}\n", b.x, b.y, b.z, b.w);
|
||||
|
||||
c : LaneAlias = .[5.0, 6.0, 7.0];
|
||||
print("alias3: {}\n", c.z);
|
||||
d : ExprAlias = .[5.0, 6.0, 7.0, 8.0];
|
||||
print("alias4: {}\n", d.w);
|
||||
}
|
||||
16
examples/1502-vectors-runtime-lane-not-const.sx
Normal file
16
examples/1502-vectors-runtime-lane-not-const.sx
Normal file
@@ -0,0 +1,16 @@
|
||||
// A `Vector` lane count that is not a compile-time integer (here a runtime
|
||||
// function call) is a hard error — a clean sx diagnostic with a non-zero exit,
|
||||
// NOT a fabricated `<0 x float>` lane that crashes LLVM verification.
|
||||
//
|
||||
// Regression (issue 0083): the Vector lane resolver hand-rolled an `else => 0`
|
||||
// switch that silently fabricated a 0-lane vector for a non-const lane. It now
|
||||
// folds the lane through the shared const-int evaluator and, when that yields no
|
||||
// compile-time integer, emits this diagnostic and halts the build.
|
||||
#import "modules/std.sx";
|
||||
|
||||
lanes :: () -> u32 { return 3; }
|
||||
|
||||
main :: () {
|
||||
v : Vector(lanes(), f32) = ---;
|
||||
print("unreachable: {}\n", v.x);
|
||||
}
|
||||
1
examples/expected/0207-generics-value-param-const.exit
Normal file
1
examples/expected/0207-generics-value-param-const.exit
Normal file
@@ -0,0 +1 @@
|
||||
0
|
||||
1
examples/expected/0207-generics-value-param-const.stderr
Normal file
1
examples/expected/0207-generics-value-param-const.stderr
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
3
examples/expected/0207-generics-value-param-const.stdout
Normal file
3
examples/expected/0207-generics-value-param-const.stdout
Normal file
@@ -0,0 +1,3 @@
|
||||
named: len=3 a0=10.000000 a2=30.000000
|
||||
expr: len=3 e2=9.000000
|
||||
copy: len=3 b2=30.000000
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
sum 0..M = 3
|
||||
sum 0..(M+1) = 6
|
||||
1
examples/expected/1501-vectors-const-lane.exit
Normal file
1
examples/expected/1501-vectors-const-lane.exit
Normal file
@@ -0,0 +1 @@
|
||||
0
|
||||
1
examples/expected/1501-vectors-const-lane.stderr
Normal file
1
examples/expected/1501-vectors-const-lane.stderr
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
4
examples/expected/1501-vectors-const-lane.stdout
Normal file
4
examples/expected/1501-vectors-const-lane.stdout
Normal file
@@ -0,0 +1,4 @@
|
||||
direct3: 1.000000 2.000000 3.000000
|
||||
direct4: 1.000000 2.000000 3.000000 4.000000
|
||||
alias3: 7.000000
|
||||
alias4: 8.000000
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
11
examples/expected/1502-vectors-runtime-lane-not-const.stderr
Normal file
11
examples/expected/1502-vectors-runtime-lane-not-const.stderr
Normal file
@@ -0,0 +1,11 @@
|
||||
error: Vector lane count must be a positive compile-time integer constant
|
||||
--> examples/1502-vectors-runtime-lane-not-const.sx:14:16
|
||||
|
|
||||
14 | v : Vector(lanes(), f32) = ---;
|
||||
| ^^^^^^^
|
||||
|
||||
error: field 'x' not found on type 'unresolved'
|
||||
--> examples/1502-vectors-runtime-lane-not-const.sx:15:32
|
||||
|
|
||||
15 | print("unreachable: {}\n", v.x);
|
||||
| ^^^
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
Reference in New Issue
Block a user