Surface rename of the signed integer family: s1..s64 become i1..i64
(u1..u64, usize, isize unchanged). 'string' keeps the s-prefix arm in
name classification; width parsing moves to the i-prefix arm next to
isize.
Internal TypeId tags follow the surface (.s8/.s16/.s32/.s64 ->
.i8/.i16/.i32/.i64), as do mono-key mangle fragments (ptr_i64,
tu_i64_bool) and all display/diagnostic formatting (i{d}).
Migrated in the same sweep: stdlib + examples + issue repros + FFI C
companions (shared symbol names like ffi_id_i64), expected
stdout/stderr/ir snapshots, specs.md, readme.md, CLAUDE.md/AGENTS.md,
implementation_plan.md, docs/, issue writeups. Vendored stb_image and
historical flow state left untouched.
zig build test: 426/426; examples suite: 595/595.
4.7 KiB
FIXED. lowerComptimeCall now allocates a result slot when
the body contains a return statement and reroutes
lowerReturn to store into it instead of emitting ret into the
caller's basic block. Regression test:
examples/issue-0045.sx.
Symptom
Calling a fn declared with ..$args (variadic heterogeneous type
pack, parser-accepted as of commit a51fe26) — even with zero
positional arguments — emits LLVM IR that fails verification:
LLVM verification failed: Terminator found in the middle of a basic block!
label %entry
No IR is printed by sx ir; the sx run JIT exits 1 immediately.
Expected: at minimum, the empty-pack call site should compile and
execute the fn body. Plan step 2 ("Runtime indexing + mono expansion")
specifies per-mono mangling and ..$args expansion to N positional
IR params; until that lands, calling such a fn should at minimum
emit a clear "pack-fn calls not yet implemented" diagnostic rather
than corrupt IR.
Reproduction
foo :: (..$args) -> i64 { return 42; }
main :: () -> i32 {
n : i64 = foo();
return 0;
}
$ ./zig-out/bin/sx run repro.sx
LLVM verification failed: Terminator found in the middle of a basic block!
label %entry
foo() with zero args, one arg (foo(1)), or multiple args
(foo(1, "hello")) all produce the same crash.
Background
After M5.A.next.1b (commit a51fe26), parseParams accepts
..$args as a parameter declaration. The Param is recorded with
is_variadic = true, is_comptime = true, type_expr = inferred_type.
parseFnDecl's collectTypeParams then registers args as a
type-param (because is_comptime = true), so fd.type_params.len > 0.
This routes the fn through the existing generic-fn path:
lowerFnDecl skips eager lowering, expecting calls to monomorphise
at first use. But the existing monomorphisation machinery binds a
single TypeId per $T name — it has no notion of a pack (a
variable-length list of TypeIds bound positionally). When the
call site tries to monomorphise with the call's args, the body's
args parameter gets resolved to a single (probably default .i64)
TypeId, but the call-site arg-packing path (packVariadicCallArgs)
treats it as a regular ..T slice — the two views disagree and
the emitted IR is malformed.
The bug isn't in step 1's code itself; it's the gap between "step 1 made the syntax parseable" and "step 2 hasn't made the calls executable yet."
Investigation prompt
For a fresh session picking this up:
Plan step 2 ("Runtime indexing + mono expansion") in
~/.claude/plans/lets-see-options-for-merry-dijkstra.md is the
intended fix:
- Detect pack-fns at declaration: the fn has a trailing param
with
is_variadic && is_comptime(no concrete type annotation distinguishes it from a regularargs: ..Tvariadic). - Per-call monomorphisation: bind
$args := [T1, ..., Tn]from the call site's concrete arg types. Each unique(arg-type-tuple, $ret)combination gets its own mono. - Expand the pack into N positional IR params in the mono's signature; mangling encodes the pack shape so distinct monos get distinct symbols.
- Body
args[$i]at comptime-known$ilowers to the i-th expanded param load (return type from$args[$i]).
Key files:
src/ir/lower.zig:lowerFnDecl(around line 949 — generic skip) needs to keep skipping pack-fns.monomorphizeFunction(line 7834) needs a pack-aware path that bindspack_bindings(the field added in commit08feb60for impl matching) instead of justtype_bindings.packVariadicCallArgs(line 7275) should NOT run for pack fns — args stay positional, not slice-packed.- Index-expression lowering needs an
args[$i]arm that reads the i-th positional param.
src/ir/types.zig:FunctionInfo/ClosureInfohavepack_startalready (added in commit6582449); the mono's expanded signature should NOT carrypack_start(it's a concrete shape).
Verification: the repro above compiles and prints "42" when run
as ./zig-out/bin/sx run repro.sx. A new
examples/156-pack-fn-mono.sx (number depends on next free slot)
should be added per the FFI cadence rule (xfail-lock-in then
green).
Alternative interim option: if step 2 is too large to land in
one session, gate parseFnDecl to reject pack params with an
explicit "pack-fn body lowering not yet implemented; only impl
target types accept ..$args today" diagnostic. Lets the
parser accept the syntax in impl headers (step 1's payoff) while
preventing the LLVM verifier crash. The diagnostic disappears
when step 2 lands.
Verification
Once the fix is in:
./zig-out/bin/sx run examples/156-pack-fn-mono.sx
# Expected: prints "42"
Full suite + zig test must still pass.