diff --git a/c/jit.c b/c/jit.c index 1525127..1a25708 100644 --- a/c/jit.c +++ b/c/jit.c @@ -839,6 +839,18 @@ static bool emit_expr(JitCtx *j, Value expr, bool tail) { int saved_loop_start = j->loop_start; int saved_loop_nparams = j->loop_nparams; Value *saved_loop_params = j->loop_params; + /* 2026-06-14: loop_slots is an ARRAY (memcpy'd in below); save it + * for nested named-let restore. Without this, inner (let loop ...) + * overwrites outer's slots and outer recursive call writes args to + * inner's slots → outer's bindings appear stale → "set! undefined" + * or wrong values at recursion. Bug surfaced in squaring.lsp's + * round84-fold-hi-into-lo-aggregate with outer terms-keyed loop + + * 4 inner (let loop ((i 0))). */ + int saved_loop_slots[MAX_JIT_LOCALS]; + if (saved_loop_nparams > 0 && saved_loop_nparams <= MAX_JIT_LOCALS) { + memcpy(saved_loop_slots, j->loop_slots, + sizeof(int) * saved_loop_nparams); + } /* Pre-compute stack slots (but don't register yet — inits use outer scope) */ Value loop_var_syms[MAX_JIT_LOCALS]; @@ -899,6 +911,10 @@ static bool emit_expr(JitCtx *j, Value expr, bool tail) { j->loop_start = saved_loop_start; j->loop_nparams = saved_loop_nparams; j->loop_params = saved_loop_params; + if (saved_loop_nparams > 0 && saved_loop_nparams <= MAX_JIT_LOCALS) { + memcpy(j->loop_slots, saved_loop_slots, + sizeof(int) * saved_loop_nparams); + } return true; }