04f75c8b71
A heap-Str accumulator threaded through a `(loop …)`/`recur` as a loop binder leaks: each iteration's superseded `str_concat` slab is never RC-dec'd, even when the loop's final result is consumed. The fixture reports `allocs=4 frees=1 live=3` under AILANG_RC_STATS (stdout `xyyy` is correct). Root (data-flow traced): the `Term::Recur` arm in crates/ailang-codegen/src/lib.rs (~2131) gates its superseded-value dec behind `!is_str`. That exclusion exists because a `Str` loop seed may be a static-literal Str — a constexpr GEP into a packed-struct global with no rc_header (lib.rs:1612) — and `ailang_rc_dec` on a static pointer reads garbage at `payload-8` and aborts on underflow (rc.c:221, no runtime guard, per design/contracts/0011-str-abi.md). So the Str binder's prior heap slab is overwritten by a plain `store` with no dec. Distinct from the loop-RESULT scope-close drop (8bcdae1, which also excluded Str) and from the ADT leg already fixed inf488d31— this is the per-iteration leak of the *intermediate* Str binder values, whichf488d31explicitly left open pending a representation decision. RED test crates/ail/tests/loop_recur_str_binder_no_leak_pin.rs with fixture examples/loop_recur_str_binder_no_leak_pin.ail: the Str leg asserts live=0 (RED at HEAD, live=3); the ADT leg (f488d31) is re-pinned as a green guard so a regression on the shared Term::Recur dec path is caught with it. GREEN side (chosen direction): promote a static-literal Str seed to heap via str_clone at loop entry so every Str binder slot holds a heap Str with a valid rc_header, then lift the `!is_str` recur-dec exclusion. This UPHOLDS the 0011-str-abi codegen-proof invariant (extends "static Str never reaches dec" to the loop case) rather than adding a runtime guard. refs #49