The int-returning type-query builtins now fold to a compile-time constant in const-required positions (`inline for` bound, array dimension), like a plain `K :: 3` const — previously they evaluated only as runtime values, so `[field_count(S)]T` and `inline for 0..field_count(S)` were rejected as "not a compile-time integer". This is what lets a `($T) -> Type` builder loop `inline for 0..field_count(T)` to assemble a member list from a type's fields (the `race` result synthesis). The shared comptime-int folder `evalConstIntExpr` (program_index.zig) gained a `.call => ctx.evalConstCallInt(node)` arm. The body-lowering ctx (`Lowering`) implements it — resolve the type arg via `resolveTypeArg`, return `memberCount orelse 0` / `typeSizeBytes` / `typeAlignBytes`, matching the runtime value path in lower/call.zig exactly. `SourceConstCtx` delegates to its wrapped Lowering; the stateless ctxs (`ModuleConstCtx`, `StatelessInner`, test `DimCtx`) stub null (they cannot resolve a type-expr arg). A non-type-query call / wrong arg count / unresolved type arg folds to null (not a comptime integer). Adversarially reviewed (SHIP): the fold matches the value path across every type kind, ctx coverage is complete, recursion is AST-depth bounded, no speculative spurious diagnostics, `orelse 0` is field_count's definitional value for non-aggregates (not a silent default). Locked by examples/comptime/0648-comptime-typequery-const-fold.sx. Suite green (820/0).
40 lines
1.5 KiB
Plaintext
40 lines
1.5 KiB
Plaintext
// Int-returning type-query builtins (`field_count` / `size_of` / `align_of`)
|
|
// fold as comptime CONSTANTS — usable as an `inline for` bound and an array
|
|
// dimension, exactly like a plain `K :: 3` const. Previously
|
|
// they evaluated only as runtime values, so `[field_count(S)]T` and
|
|
// `inline for 0..field_count(S)` were rejected as "not a compile-time integer".
|
|
// (This is what lets a `($T) -> Type` builder loop `inline for 0..field_count(T)`
|
|
// to assemble a variant list from a type's members — the `race` result synthesis.)
|
|
#import "modules/std.sx";
|
|
|
|
S :: struct { a: i64; b: bool; c: f64; }
|
|
E :: enum { X; Y; Z; W; }
|
|
|
|
main :: () -> i32 {
|
|
// field_count as an inline-for bound
|
|
s := 0;
|
|
inline for 0..field_count(S) (i) { s = s + i; } // 0+1+2 = 3
|
|
print("field_count(S) loop sum = {}\n", s);
|
|
|
|
// field_count as an array dimension; fill it in a folded loop
|
|
xs : [field_count(S)]i64 = ---;
|
|
inline for 0..field_count(S) (i) { xs[i] = i * 10; }
|
|
print("array[field_count(S)] len = {} xs[2] = {}\n", xs.len, xs[2]);
|
|
|
|
// field_count of an enum (4 variants) driving a loop
|
|
e := 0;
|
|
inline for 0..field_count(E) (i) { e = e + 1; }
|
|
print("field_count(E) = {}\n", e);
|
|
|
|
// size_of / align_of fold too
|
|
bytes : [size_of(i64)]u8 = ---;
|
|
print("size_of(i64) array len = {}\n", bytes.len);
|
|
print("align_of(f64) = {}\n", align_of(f64));
|
|
|
|
// composed const expression as a dim
|
|
ys : [field_count(S) + 1]i64 = ---;
|
|
print("[field_count(S) + 1] len = {}\n", ys.len);
|
|
|
|
return 0;
|
|
}
|