(module loop_recur_heap_binder_no_leak_pin ; RED for bug #49: a heap loop binder that `recur` REPLACES with a fresh ; heap value each iteration. Each superseded value is a distinct heap ; allocation; codegen's Term::Recur arm stores the new binder value into ; the loop-carried alloca without RC-dec'ing the prior one, so every ; overwritten value leaks. ; ; A boxed ADT (`Cell`), NOT a `Str`, is the deliberate trigger: every ; ADT value is heap-allocated (there is no static-literal representation ; for an ADT), so the per-iteration dec is unambiguously UB-free. `Str` ; literals lower to constexpr-GEPs into static globals (lib.rs Str-lit ; arm), so a `Str` loop seed is static and must NOT be dec'd — the Str ; case is the separate static-vs-heap-Str representation question ; (the same obstacle behind the deliberate Str exclusion in 8bcdae1). ; ; allocs: seed Cell(0) + three recur Cells (Cell at i=0,1,2) = 4 heap ; slabs. The loop returns acc=Cell(2); that final result drops at scope ; close (the drop.rs path). The three superseded Cells (seed, Cell@i=0, ; Cell@i=1) must drop at the recur store. Pre-fix: live=3. Post-fix: ; live=0. Expected stdout: 2. (data Cell (ctor Cell (con Int))) (fn main (type (fn-type (params) (ret (own (con Unit))) (effects IO))) (params) (body (let final (loop (acc (con Cell) (term-ctor Cell Cell 0)) (i (con Int) 0) (if (app ge i 3) acc (recur (term-ctor Cell Cell i) (app + i 1)))) (match final (case (pat-ctor Cell n) (app print n)))))))