The for header is now a comma-separated list of iterables with a
positional capture group and no ':' separator:
for xs (x) { } // collection
for 0..n (i) { } // range (end exclusive)
for 1..=5 (a) { } // ..= inclusive end
for xs, 0.. (x, i) { } // index idiom (replaces (x, i))
for xs, ys (x, y) { } // parallel (zip) iteration
for xs (x) => sum += x; // arrow body (full statement)
First-iterable-wins: the first iterable's length drives the loop and
must be bounded; the other positions follow by their own cursors (a
non-first range's end is not consulted or evaluated; a shorter
non-first collection is read past its length on mismatch). The old
single-iterable index capture is replaced by the trailing open range.
Capture/call disambiguation is positional: the paren group immediately
before '{' or '=>' is the capture, every earlier top-level group is a
call. 'for zip(a, b) (x, y)' calls zip; 'for f(n) { }' reads (n) as
the capture and errors with a parenthesize/add-capture hint. The old
':' form errors with a migration hint.
Lowering is unified across forms: one cursor slot per position (ranges
start at their start, collections at 0), all advanced together, the
first position's bound terminating. inline for keeps the single
bounded comptime range.
Migrated the full corpus (examples, library modules, issue repros,
in-source test strings). New coverage: examples/0050 (the full feature
surface) and examples/1149-1155 (seven diagnostic faces). specs.md For
Loop section + grammar rewritten; readme teaser updated.
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
|
|
}
|