From 6b7a66ba4dafa778dfd37a5b3195d7cdd04dc1ce Mon Sep 17 00:00:00 2001 From: agra Date: Wed, 27 May 2026 14:52:43 +0300 Subject: [PATCH] =?UTF-8?q?ffi=20M5.A.next.2a.C:=20pack=20if-return=20?= =?UTF-8?q?=E2=80=94=20lock=20in=20slot-load=20uninit=20regression?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to issue-0045's fix (commit 9e78790). The fix routes inline-comptime-body `return X;` into a result slot but sets `block_terminated = true` after the inline return — and that flag leaks past the enclosing `if`'s merge block. Body shape: maybe :: (..$args) -> s64 { if args.len > 0 { return 42; } return -1; } For `maybe()` (zero call-args), the false-condition path skips the then-branch's `return 42;` and should fall through to `return -1;`. Today's flow: - Then-branch's `return 42;` stores 42 to slot and sets block_terminated = true. - if lowering switches to merge_bb. block_terminated stays true (never reset across the if/merge boundary). - lowerBlockValue's loop sees block_terminated and returns null without processing the trailing `return -1;`. - lowerComptimeCall loads slot — slot was never written on the false-condition path → garbage (8354116000 on this machine; stable across runs). `maybe(99)` works because the cond is true; the then-branch's store wins. Next commit reshapes the inline-return mechanism to use a dedicated "return-done" basic block: each inline `return X;` stores to slot and branches to ret_done; after the body lowers, lowerComptimeCall switches to ret_done and loads. The basic block CFG carries the control-flow termination — no need for the leaking `block_terminated` flag. 196/196 example tests + `zig build test` green (the new test captures the wrong value as the snapshot to flip). --- examples/157-pack-if-return.sx | 39 ++++++++++++++++++++++++++ tests/expected/157-pack-if-return.exit | 1 + tests/expected/157-pack-if-return.txt | 2 ++ 3 files changed, 42 insertions(+) create mode 100644 examples/157-pack-if-return.sx create mode 100644 tests/expected/157-pack-if-return.exit create mode 100644 tests/expected/157-pack-if-return.txt diff --git a/examples/157-pack-if-return.sx b/examples/157-pack-if-return.sx new file mode 100644 index 0000000..fb770f8 --- /dev/null +++ b/examples/157-pack-if-return.sx @@ -0,0 +1,39 @@ +// Variadic heterogeneous type packs — control-flow follow-up to +// issue-0045 fix (commit 9e78790). +// +// issue-0045's fix routes inline-comptime-body `return X;` into a +// result slot so the caller's basic block isn't terminated +// mid-flight. But the fix sets `block_terminated = true` after +// the inline return — which leaks PAST the enclosing `if`'s +// merge block. When the body shape is +// if cond { return X; } +// return Y; +// only the then-branch's `return X;` runs; `block_terminated` +// stays true in the merge block, so `lowerBlockValue`'s loop +// exits before the trailing `return Y;` lowers. The trailing +// return never stores into the slot — for the false-condition +// path the load reads uninitialised stack memory. +// +// Pack-fn `..$args` is the shortest repro because `args.len` +// gives a comptime-feeling test for the condition. The bug is +// actually shape-agnostic — any comptime body with `if cond +// { return X; }; return Y;` regresses the same way. +// +// `maybe()` with zero call-args takes the false branch and +// should fall through to `return -1;`. Today it loads garbage +// from the uninitialised slot. + +#import "modules/std.sx"; + +maybe :: (..$args) -> s64 { + if args.len > 0 { + return 42; + } + return -1; +} + +main :: () -> s32 { + print("{}\n", maybe()); // expect -1 + print("{}\n", maybe(99)); // expect 42 + return 0; +} diff --git a/tests/expected/157-pack-if-return.exit b/tests/expected/157-pack-if-return.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/expected/157-pack-if-return.exit @@ -0,0 +1 @@ +0 diff --git a/tests/expected/157-pack-if-return.txt b/tests/expected/157-pack-if-return.txt new file mode 100644 index 0000000..1e218e9 --- /dev/null +++ b/tests/expected/157-pack-if-return.txt @@ -0,0 +1,2 @@ +8354116000 +42