From 39a804f25e687bc53016e1f5ff0ebbfb06e96797 Mon Sep 17 00:00:00 2001 From: agra Date: Wed, 27 May 2026 14:58:13 +0300 Subject: [PATCH] ffi checkpoint: 2a control-flow fix + step 2/3 roadmap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Logs the 2a.C/2a.D pair (6b7a66b + e6d6903) — the if-return regression in issue-0045's original fix, now resolved via the SSA "return-done block" pattern. Step 2 is functionally sufficient for the impl-matching payoff (step 5 of the plan); per-call-shape monomorphisation deferred until a real workload demands it. Step 3 ("Type-reflection intrinsics") flagged as the natural next slice: `$args[$i]` in type positions unlocks the block- trampoline body in stdlib's generic Into(Block) impl, which is the visible payoff of the whole pack feature. --- current/CHECKPOINT-FFI.md | 75 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 2 deletions(-) diff --git a/current/CHECKPOINT-FFI.md b/current/CHECKPOINT-FFI.md index 5fc7e0f..cc2960d 100644 --- a/current/CHECKPOINT-FFI.md +++ b/current/CHECKPOINT-FFI.md @@ -6,6 +6,48 @@ add a test and make it pass — that's two commits). ## Last completed step +**M5.A.next.2a.D — inline-return uses CFG terminator, not block_terminated** +(commit `e6d6903`, lock-in `6b7a66b`). + +Fixes a control-flow regression in issue-0045's original fix +(commit `9e78790`). The original set `block_terminated = true` +after each inline `return X;` to skip dead code in the inlined +body — but the flag leaked past structured control flow. A +body shaped `if cond { return X; }; return Y;` had its +trailing `return Y;` short-circuited at `lowerBlockValue`'s +`if (self.block_terminated) return null;` check. For the false- +condition path, the slot was never written → load read +uninitialised stack memory. + +Reshaped to the classical SSA "return-done block" pattern: + +- `InlineReturnInfo` gains a `done_bb: BlockId` field — a + fresh basic block allocated by `lowerComptimeCall` per + comptime-call instance. +- `lowerReturn`'s inline path stores into the slot, drains + defers, and emits `br done_bb`. The basic block's terminator + carries the "no fall-through" signal; the + `block_terminated` flag is no longer touched. +- `lowerComptimeCall` lowers the body, then unconditionally + switches to done_bb and loads the slot. Tail-expression + bodies (rare when has_return is true) get a synthetic store + + br so the CFG stays well-formed. + +`examples/157-pack-if-return.sx` flips from `8354116000` (the +uninit slot load) to `-1`. A three-way `classify(..$args)` +smoke confirms multi-return-path bodies work for any of the +three branches; defer-with-return still fires the defer at the +correct exit. + +issue-0046 (nested `print` inside `..$args` body that also +`return`s) is unrelated to this fix and is still open — same +two faces as filed: interp panic for plain comptime, "unresolved +'result'" for pack-fn. + +196/196 example tests + `zig build test` green. + +--- + **M5.A.next.2a.B — pack typed indexing: `args[$i]` substitutes call arg** (commit `cd36784`, lock-in `223ec3d`). @@ -484,13 +526,42 @@ plus 2 codegen fixes surfaced along the way.** ## Current state -- 195/195 example tests pass; `zig build test` green. +- 196/196 example tests pass; `zig build test` green. - Phase 3.0/3.1/3.2 + M1.0–M1.3 + M2.1–M2.3 + M3 + M4.0 + M4.A all landed. - Pack feature step 1 done (1c.A → 1d.B; commits bb6eca6 → 08feb60). -- Pack feature step 2a done — typed `args[$i]` at literal indices (cd36784). +- Pack feature step 2a done — typed `args[$i]` at literal indices + (cd36784) + control-flow CFG terminator fix (e6d6903). - issue-0045 (comptime-fn-with-return verifier crash) fixed (9e78790). +- issue-0046 (nested-comptime-call + return) FILED, not blocking + next slices since builders run inside `#insert`, not inside + public pack-fn bodies. - Chess on macOS / iOS-sim / Android all build and run. +## Pack feature — next slice options + +Step 2 ("Runtime indexing + mono expansion") is functionally +sufficient for the impl-matching payoff (step 5) thanks to step +1d.B's per-mono mangling on `.convert__` keys. +Remaining within step 2 is real per-call-shape monomorphisation +(today pack-fns inline at every call site) — performance/IR-size +concern, not correctness. Defer until a real workload demands it. + +Step 3 ("Type-reflection intrinsics") is the natural next slice: +- `$args[$i]` in TYPE positions (param types, return types, + fn-pointer types, struct fields). +- `type_name`, `type_eq`, `has_impl` comptime intrinsics. +- Unlocks the block-trampoline body in stdlib's generic + `Into(Block) for Closure(..$args) -> $R` impl — the body + needs `typed_fn : (*void, $args[0], $args[1]) -> $R = xx ...` + per-position type interpolation. + +Step 4 (`#insert` pack passthrough + `compile_error`) is a +parser/interp tweak letting builders raise build-time +diagnostics from inside `#insert` bodies. + +Steps 5 and 6 are stdlib refactors gated on step 3's +type-position substitution landing. + **M4.0 — context.allocator threading** (4 commits this session): - `__sx_allocator: Allocator` prepended at field index 0 of every sx-defined class's state struct