# issue 0073 — closure literal inside a `defer` body segfaults the compiler > **✅ RESOLVED (2026-06-02).** Root cause: `lowerLambda` never opened its own > `defer` window. Every other function-lowering entry (`lowerFunction`, > `monomorphizeFunction`, `monomorphizePackFn`) saves `func_defer_base`, sets it > to `defer_stack.items.len`, and restores it — but `lowerLambda` didn't, so a > lambda's `return` drained the *enclosing* function's defers. When the defer > body itself declared the lambda, draining re-lowered the lambda, which `return`ed, > which drained again → infinite recursion → stack-overflow SIGSEGV. > Fix: `lowerLambda` now opens a fresh defer window (save `func_defer_base` + > `defer_stack` length, set base to the current length, restore both on exit) — > `src/ir/lower.zig`. Regression test: `examples/0310-closures-closure-literal-in-defer.sx` > (a closure declared + called inside a `defer`; verifies `body` then > `defer closure: 42` at scope exit). Suite 358/0. ## Symptom One-line: declaring a **closure literal inside a `defer` body** crashes the compiler with a segfault during lowering. - **Observed:** `sx run` / `sx build` SIGSEGVs in `lowerLambda` (`src/ir/lower.zig`) while lowering the enclosing function. With a *failable* closure (`() -> !E { ... }`) the crash surfaces one frame out, in `lowerCall` → `expandCallDefaults` → `scope.lookupFn` → `hash_map.get` (a corrupted/garbage scope pointer), suggesting a stale/clobbered `self.scope` (or builder/current-function state) while the deferred body is lowered. - **Expected:** the program either lowers normally or produces a clean diagnostic. A compiler segfault is never acceptable, regardless of whether the shape is intended to be supported. Isolation (all on `arch-refactor`, current `HEAD`): | Probe | Shape | Result | |-------|-------|--------| | (a) | failable closure declared + `cb() catch e {}` — **no `defer`** | OK (exit 0) | | (b) | failable closure literal inside a `defer` body | **SIGSEGV** (lowerCall/expandCallDefaults) | | (c) | **non-failable** `() { return; }` inside a `defer` body | **SIGSEGV** (lowerLambda) | | (d) | failable closure literal inside a plain `{ … }` block (not `defer`) | OK (exit 0) | So the trigger is **a closure literal lowered inside a `defer` body** — not failability, not whether the closure is called. (a)/(d) prove closures and failable closures lower fine outside a `defer`; (c) proves a bare non-failable closure in a `defer` is enough to crash. ## Reproduction `issues/0073-closure-literal-in-defer-segfault.sx` (minimal — non-failable, uncalled closure in a `defer`): ```sx #import "modules/std.sx"; work :: () { defer { cb := () { return; }; } } main :: () -> s32 { work(); return 0; } ``` Run: `./zig-out/bin/sx run issues/0073-closure-literal-in-defer-segfault.sx` → "Segmentation fault" with a stack through `lowerLambda` (`src/ir/lower.zig`). ## Investigation prompt A `defer` body is captured at its declaration site and lowered into the function's exit/cleanup path (drained by `lowerReturn` / block-exit), not inline. Lowering a **lambda literal** while emitting a deferred body appears to run with invalid lowering state — most likely `self.scope` (note probe (b)'s crash is in `scope.lookupFn` reading a hash map at a garbage address) and/or the builder's current-function / block context is stale or not the one `lowerLambda` expects when it allocates the closure's trampoline + env. Suspected area: `src/ir/lower.zig` — `lowerLambda` (~`:8145`) and the `defer` capture/replay path (`defer_stmt` handling + the cleanup-drain in `lowerReturn` / block exit; grep `defer_stack` / `func_defer_base`). Check whether deferred bodies are lowered: 1. with a scope pointer that has since been popped/freed (use-after-free → garbage `fn_names`/`map` in `lookupFn`), or 2. in a builder state where `lowerLambda`'s assumptions (current function, insert block) don't hold, or 3. re-entrantly / unbounded (the failable-variant trace looked like deep recursion through `expandCallDefaults`→`lowerCall`→`lowerLambda`). Likely fix shape: lower deferred bodies under a valid, live scope (re-establish or retain the declaring scope when replaying the `defer` body), or defer the lambda's trampoline emission to a context that has the right function/block. Verification step: run the repro above — expect it to lower cleanly (or emit a clean diagnostic) and NOT segfault. Then confirm a closure that is actually used inside the defer (`defer { cb := () { ... }; cb(); }`) also works, and re-run `bash tests/run_examples.sh` (357/0) to confirm no regression. ## Provenance Found while writing an A5.2 (architecture stream) test-first scaffolding example for the ERR E1.7 "cleanup-absorption stops at nested closures" behavior — the closure-boundary probe (a closure literal inside a `defer`) crashed the compiler instead of exercising the diagnostic. The crash is a pre-existing lowering bug, unrelated to the A5 error-analysis extraction; surfaced by the probe.