(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)))))))
