// 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 : i64 : 5`), an integral float const // (`F :: 4.0` ≡ 4), and an ALIASED integer constraint (`Count :: u32`, // `Small :: i8`) 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 : i64 : 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 :: i8; // integer ALIAS — value-param constraint ArrN :: [N]i64; // array dim via alias: expression const (3) ArrF :: [F]i64; // array dim via alias: integral float (4) ArrS :: [S]i64; // 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 i8 constraint Make :: ($K: u32, $T: Type) -> Type { return [K]T; } // type-fn value-param main :: () { // array dimension — DIRECT a : [N]i64 = ---; a[0] = 7; a[2] = 9; print("dim.direct.expr: len={} a0={} a2={}\n", a.len, a[0], a[2]); f : [F]i64 = ---; 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 i8 bn : Buf(N, i64) = ---; bn.data[2] = 30; print("vp.struct.expr: len={} v={}\n", bn.data.len, bn.data[2]); bc : BufC(KU, i64) = ---; bc.data[2] = 31; print("vp.struct.alias.u32: len={} v={}\n", bc.data.len, bc.data[2]); bs : BufS(4, i64) = ---; bs.data[3] = 32; print("vp.struct.alias.i8: len={} v={}\n", bs.data.len, bs.data[3]); // generic value-param — type-fn binder: expr const mk : Make(N, i64) = ---; 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 }