fix(builtins): release boxed string elements consumed by implode over a mixed array#613
Conversation
|
Adjacent pre-existing bug found while validating this fix (reverified on the pre-fix build — independent of this change): #614, |
… a mixed array implode() over an indexed array held in a boxed `mixed` cell leaked one heap block per string element (issue illegalstudio#601). Each boxed element is stringified through `__rt_mixed_cast_string`, whose string-tag path calls `__rt_str_persist` to allocate an owned copy detached from the source mixed owner. implode copied the bytes into the concat buffer but never released that copy. The `__rt_implode` mixed-element loop now records the cast string pointer in a dedicated frame slot and releases it with `__rt_heap_free` after its bytes are copied, on both the ARM64 and x86_64 paths. Borrowed typed-array string slots clear the slot so nothing is freed, and integer/float/bool elements (whose casts return shared scratch storage) pass through `__rt_heap_free`'s foreign-pointer guard as a safe no-op. The joined output lives in the concat buffer and is never freed, so it stays valid. Adds heap-debug regression tests for the exact repro, a multi-string mixed literal, an int-in-mixed no-op-release case, a 20-iteration loop proving the leak no longer scales, an empty-separator join, and a typed-array control that must stay untouched, plus emitter unit tests asserting the release path and balanced frame on both targets. Claude-Session: https://claude.ai/code/session_01HRvbcjPHa3kAojdCjNZL5L
61de0b9 to
fe1145e
Compare
|
Rebased onto current The only conflict was Verified on the rebased head Nothing else changed. The branch is now a single commit on top of |
Summary
implode()over an indexed array held in a boxedmixedcell leaked one heapblock per string element (issue #601). Stdout was correct but the heap grew with
the string-element count.
Root cause
In
__rt_implode's Mixed-element loop each boxed element is stringified through__rt_mixed_cast_string. Its string-tag path calls__rt_str_persist, whichallocates a fresh owned heap block (kind 1) detached from the source mixed owner.
implode copied those bytes into the shared concat buffer but never released the
persisted copy — one leaked 48-byte block per string element. Integer, float, and
bool elements stringify into shared scratch storage (
__rt_itoa/__rt_ftoa) withno allocation, so they never leaked.
Fix
The
__rt_implodeMixed-element loop now records the cast string pointer in adedicated frame slot and releases it with
__rt_heap_freeonce its bytes have beencopied, on both the ARM64 and x86_64 paths:
freed (they are owned by the array).
__rt_heap_free'sforeign-pointer guard (live-heap range check) makes the release a safe no-op for
them.
joined output lives in the concat buffer and is never freed.
The x86_64 frame grows from 64 to 80 bytes for the extra spill slot (balanced in
prologue/epilogue); ARM64 preserves its loop registers around the release call.
Tests
heap-debug regression tests in
tests/codegen/runtime_gc/regressions.rs: the exactrepro, a multi-string mixed literal, an int-in-mixed no-op-release case, a
20-iteration loop proving the leak no longer scales, an empty-separator join, and a
typed-array control that must stay untouched. Plus emitter unit tests asserting the
release path and balanced frame on both targets. Focused slices (
runtime_gc225,implode/join14,strings212,mixed285,arrays386) pass with noregressions. Linux x86_64 end-to-end coverage relies on CI (no local
cross-assembler).
Fixes #601
https://claude.ai/code/session_01HRvbcjPHa3kAojdCjNZL5L