buildPackSliceValue (lower/pack.zig) materialized a bare `$<pack>` []Type slice
as []Any (16-byte elements) — a stale mapping from before the dedicated Type
builtin (.type_value, 8 bytes) replaced Type -> .any. It stored 8-byte const_type
words into 16-byte slots, so a []Type reader (8-byte stride) read [t0, pad, t1, ...]
instead of [t0, t1, ...]. The legacy interp's tagged-Value model hid it; the
byte-accurate comptime VM exposed it (a `..$args` pack forwarded as a []Type
argument across a call read its elements shifted/garbled).
Fix: build the pack-slice array + slice as .type_value (8 bytes). Removed the
stopgap type_name .unresolved guard (379ed05) now that the root cause is fixed.
Regression test examples/0525-packs-pack-as-type-slice-arg.sx (outer(42,"hi",true)
-> inner($args: []Type) -> "i64 string bool"). 700/0 both gates; issue 0143 RESOLVED.
4.8 KiB
RESOLVED (2026-06-18). Root cause:
buildPackSliceValue(src/ir/lower/pack.zig) built the$<pack>[]Typeslice as[]Any(16-byte elements) — a stale mapping from before the dedicatedTypebuiltin (.type_value, 8 bytes) replacedType → .any. It stored 8-byteconst_typewords into 16-byte slots, so a[]Typereader (8-byte stride) read[t0, pad, t1, …]. Fix: build the slice/array as.type_value(8 bytes). Regression test:examples/0525-packs-pack-as-type-slice-arg.sx. The stopgaptype_name.unresolvedguard added in379ed05was removed (root cause fixed). 700/0 both gates.
0143 — A variadic pack passed as []Type across a call is mis-strided (Any-sized backing, Type-sized view)
Symptom — When a variadic ..$args pack is forwarded as a []Type argument
to another function, reading args[i] yields the wrong element: the backing
array is laid out as [N x Any] (16-byte {i64,i64} slots) but the slice's
element type is Type/type_value (8 bytes), so an 8-byte-strided index read
lands on elem0, then the padding of elem0 (reads as .unresolved), then
elem1, … — i.e. half-stride reads.
Observed (on the flat-memory comptime VM, which is byte-accurate) vs expected
(legacy interp, whose tagged-Value model is stride-agnostic and reads the right
logical element):
VM: [i64 <unresolved> string ]
legacy: [i64 string bool ]
The legacy interpreter masks the bug (it indexes a logical sequence of Values,
not bytes). The VM exposes it. This is what makes examples/0114-types-build-block-convert
read arg1: <unresolved> instead of arg1: string once the VM handles type_name
(it currently bails on the .unresolved read — see the guard added in comptime_vm.zig
type_name, commit 379ed05 — so under the fallback it falls back; under
SX_COMPTIME_FLAT_STRICT it is a hard error).
Reproduction
#import "modules/std.sx";
inner :: (args: []Type) -> string {
s := ""; i : i64 = 0;
while i < args.len { s = concat(s, type_name(args[i])); s = concat(s, " "); i = i + 1; }
return s;
}
outer :: (..$args) -> string { return inner($args); } // <-- pack passed as []Type across a call
R :: #run outer(42, "hi", true);
main :: () { print("[{}]\n", R); }
Expected: [i64 string bool ]. Actual (VM): [i64 <unresolved> string ].
Note the direct use (no cross-call) is fine — walk :: (..$args) { list := $args; … type_name(list[i]) … }
reads correctly. Only forwarding the pack as a []Type argument mis-strides.
Root cause (hypothesis)
The pack $args is materialised as a [N x Any] array (each arg boxed as a
16-byte Any — confirmed in the LLVM IR: array_to_string__AR_3_Any /
[3 x { i64, i64 }]), but when forwarded to a []Type parameter the slice is
typed with element type_value (8 bytes). The slice {ptr,len} points at the
[N x Any] data, so index_get with an 8-byte element stride reads garbage.
The fix belongs in lowering, not the VM: a pack consumed as []Type must
either (a) materialise a real [N x Type] (8-byte) array by extracting each
arg's type tag from its Any box, or (b) keep the slice element type as Any and
have type_name/reflection read the Any's tag (the VM's type_name already
handles an .any arg). Option (b) is likely smaller. Whichever: the slice's
element type and its backing array's element size MUST agree.
Suspected area: the pack-expansion / pack-as-slice lowering (search src/ir/lower/
for the ..$args → slice materialisation and the []Type coercion of a value
pack; pack.zig).
Investigation prompt (paste into a fresh session)
A variadic
..$argspack forwarded as a[]Typeargument across a call is mis-strided: the backing array is[N x Any](16B slots) but the slice element type istype_value(8B), soargs[i]reads at the wrong offset. The legacy interp masks it (Value-indexed); the byte-accurate comptime VM exposes it (examples/0114, and the minimal repro inissues/0143-…md). Fix it in lowering: make the pack→[]Typematerialisation agree on element size — either build a real[N x Type]array (extract each Any's type tag) or type the slice[]Anyand let reflection read the Any tag (type_namealready handles.any). Verify: run the repro on the VM (SX_COMPTIME_FLAT=1 sx run) and expect[i64 string bool ]; thenexamples/0114must producearg1: string(not<unresolved>) and run HANDLED underSX_COMPTIME_FLAT_STRICT=1matching legacy. Drop thetid == .unresolvedguard incomptime_vm.zigtype_nameonce the root cause is fixed (it was a stopgap so the VM declines rather than emits garbage).
Status
OPEN. Blocks examples/0114 from running HANDLED on the comptime VM (Phase 4
legacy-interp retirement). Not a blocker for the other comptime-op ports.