Three adjacent cells of the shared count surface still diverged from the rest; all now route through the same leaf+fold+narrow+diagnose path. 1. Aliased integer constraint bypassed the value-param range gate — only builtin constraint names matched intTypeRange, so Box(5_000_000_000) with `$K: Count` (Count :: u32) compiled and bound a truncated value. resolveValueParamArg (shared by both the struct AND type-fn binder) now resolves the constraint to its underlying builtin via canonicalIntConstraintName (Count -> u32, Small -> s8) before range-checking, so an aliased integer constraint behaves exactly like the builtin it names. 2. A named const with an expression RHS (M :: 2; N :: M + 1) did not fold as a count — moduleConstInt read only a literal RHS node. It now folds every const's RHS through the shared evalConstIntExpr, cycle-guarded (mutual / self cycles fold to null, not a stack overflow), and pass-0 pre-registers expression-RHS consts. N :: M + 1 == 3 at every consumer: dim (direct + alias), Vector lane, value-param (struct + type-fn), inline for. 3. Stateful resolveArrayLen still fabricated length 0 after a failed fold; it now returns null -> the .unresolved sentinel (no fabrication). The binding's lowering never reaches sizeOf (alloca defers it; hasErrors aborts first) and a field access on an already-diagnosed .unresolved value is poison-suppressed (emitFieldError), so a failed-fold dim emits ONE clean diagnostic with no panic. Regressions: examples/0146 (full positive matrix — every consumer x leaf form), 1135 (aliased u32 + s8 overflow), 1136 (direct non-const dim halts cleanly). The cascade cleanup also tightened 1502/1503 to one diagnostic. Unit test added for moduleConstInt expression-folding + cycle detection.
69 lines
3.7 KiB
Plaintext
69 lines
3.7 KiB
Plaintext
// The comptime-int COUNT surface is uniform: every count consumer — array
|
|
// dimension (direct `[N]T` and via type alias), `Vector` lane, generic
|
|
// value-param (struct AND type-fn binder), and `inline for 0..N` — folds the
|
|
// SAME leaf forms to the SAME value through one shared evaluator
|
|
// (`program_index.evalConstIntExpr` / `moduleConstInt`). The leaf forms
|
|
// exercised here: untyped int const (`M`), a named const with an EXPRESSION RHS
|
|
// (`N :: M + 1`), a typed-int const (`S : s64 : 5`), an integral float const
|
|
// (`F :: 4.0` ≡ 4), and an ALIASED integer constraint (`Count :: u32`,
|
|
// `Small :: s8`) on a value-param.
|
|
//
|
|
// Regression (issue 0083): two cells of this surface diverged from the rest.
|
|
// (1) A named const whose RHS is an expression (`N :: M + 1`) did not fold as a
|
|
// count ("not a compile-time integer constant") — `moduleConstInt` read only a
|
|
// literal RHS; it now folds the RHS through the shared `evalConstIntExpr`. (2) An
|
|
// aliased integer constraint (`$K: Count`) bypassed the value-param range gate,
|
|
// which only matched builtin constraint names; the constraint now resolves to
|
|
// its underlying builtin before range-checking, so `$K: Count` behaves exactly
|
|
// like `$K: u32`.
|
|
#import "modules/std.sx";
|
|
|
|
M :: 2; // untyped int const
|
|
N :: M + 1; // named const, EXPRESSION RHS (== 3)
|
|
S : s64 : 5; // typed-int const
|
|
KU : u32 : 3; // typed-u32 const
|
|
F :: 4.0; // integral float const (== 4)
|
|
Count :: u32; // integer ALIAS — value-param constraint
|
|
Small :: s8; // integer ALIAS — value-param constraint
|
|
|
|
ArrN :: [N]s64; // array dim via alias: expression const (3)
|
|
ArrF :: [F]s64; // array dim via alias: integral float (4)
|
|
ArrS :: [S]s64; // array dim via alias: typed const (5)
|
|
|
|
Buf :: struct ($K: u32, $T: Type) { data: [K]T; }
|
|
BufC :: struct ($K: Count, $T: Type) { data: [K]T; } // ALIASED u32 constraint
|
|
BufS :: struct ($K: Small, $T: Type) { data: [K]T; } // ALIASED s8 constraint
|
|
|
|
Make :: ($K: u32, $T: Type) -> Type { return [K]T; } // type-fn value-param
|
|
|
|
main :: () {
|
|
// array dimension — DIRECT
|
|
a : [N]s64 = ---; a[0] = 7; a[2] = 9;
|
|
print("dim.direct.expr: len={} a0={} a2={}\n", a.len, a[0], a[2]);
|
|
f : [F]s64 = ---; f[3] = 40;
|
|
print("dim.direct.float: len={} f3={}\n", f.len, f[3]);
|
|
|
|
// array dimension — via type ALIAS
|
|
aa : ArrN = ---; aa[2] = 99; print("dim.alias.expr: len={} aa2={}\n", aa.len, aa[2]);
|
|
af : ArrF = ---; print("dim.alias.float: len={}\n", af.len);
|
|
az : ArrS = ---; print("dim.alias.typed: len={}\n", az.len);
|
|
|
|
// Vector lane — expression const (3) and integral float (4)
|
|
v3 : Vector(N, f32) = .[1.0, 2.0, 3.0];
|
|
print("lane.expr3: {} {} {}\n", v3.x, v3.y, v3.z);
|
|
v4 : Vector(F, f32) = .[1.0, 2.0, 3.0, 4.0];
|
|
print("lane.float4: {}\n", v4.w);
|
|
|
|
// generic value-param — struct binder: expr const, aliased u32, aliased s8
|
|
bn : Buf(N, s64) = ---; bn.data[2] = 30; print("vp.struct.expr: len={} v={}\n", bn.data.len, bn.data[2]);
|
|
bc : BufC(KU, s64) = ---; bc.data[2] = 31; print("vp.struct.alias.u32: len={} v={}\n", bc.data.len, bc.data[2]);
|
|
bs : BufS(4, s64) = ---; bs.data[3] = 32; print("vp.struct.alias.s8: len={} v={}\n", bs.data.len, bs.data[3]);
|
|
|
|
// generic value-param — type-fn binder: expr const
|
|
mk : Make(N, s64) = ---; mk[2] = 33; print("vp.typefn.expr: len={} v={}\n", mk.len, mk[2]);
|
|
|
|
// inline-for bound — expr const (3) and integral float (4)
|
|
s := 0; inline for 0..N: (i) { s += i; } print("for.expr: {}\n", s); // 0+1+2 = 3
|
|
t := 0; inline for 0..F: (i) { t += i; } print("for.float: {}\n", t); // 0+1+2+3 = 6
|
|
}
|