← board

riscv32 drops interface releases in six shapes

Measured — riscv32 is the only target that differs

test native / i386 / arm32 / aarch64 riscv32
test_interface_byval_param_no_leak 16 / 16 10 / 16
test_interface_arc freed=3 freed=1
test_interface_arc_exc reassign freed=2, unwind freed=3 freed=1, freed=1
test_interface_as_cast_retains 7 / 7 5 / 7
test_interface_ascast_temp_lifetime prints destroy 7 never destroys
test_interface_call_result_move 9 / 9 8 / 9 (self-returning released 0, want 1)
test_interface_com_value_param after nil freed=1 freed=0

test_interface_byval_param_no_leak is the most informative: const and var parameters release correctly (5/5 both), while plain BY-VALUE parameters release 0 of 1, 4 of 5 and 49 of 50. A by-value interface parameter carries a +1 the callee owns and must drop; riscv32 drops it only when something else happens to.

Likely one cause, not six

Every failing shape is "an interface handle held in a slot riscv32's epilogue does not walk": a by-value PARAM (not a local), an as-cast TEMP, a call-result temp. riscv32's EmitProcEpilog arm is also visibly the thinnest of the five — it has the static-array and scalar-AnsiString arms and (since 2026-08-21) the dynamic-array one, but no variant arm, no record-with-managed-fields arm, and no promotable-int arm, all of which the other four carry. Diff riscv32's arm list against aarch64's; the differences ARE the bug list, exactly as [[bug-a-no-dyn-array-scope-exit-release-on-four-backends]] found.

Check first whether riscv32's cleanup loop visits skParam at all — every arm in it is gated Syms[i].Kind = skLocal, and a by-value interface parameter is skParam.

Gate

The seven tests above matching the native answer under tools/run_target.sh riscv32; the 53-test cross differential no worse than baseline; self-host fixedpoint + tools/gate.sh quick.

Resolution (2026-08-21)

All seven tests in the table now produce output identical to the native oracle under tools/run_target.sh riscv32.

One cause, as the ticket suspected — but not the one it named

The ticket said to check whether the cleanup loop visits skParam. It does not need to: every other target gates on skLocal too and releases by-value interface parameters correctly, because a by-value param IS a local by the time the epilogue walks the scope.

The actual cause is one line long: riscv32's arm of the scope-exit release had no COM interface case at all. Not a narrower case, not a wrong register — the arm was absent, so an interface handle in a slot was simply never walked. Every failing shape in the table (by-value param, as-cast temp, call-result temp, value param) is the same slot with a different name.

That was invisible while the release loops lived inline in each of EmitProcEpilog's six branches. After [[refactor-a-the-missing-layer-between-frontends-and-backends]] put all six arms in one procedure, the matrix reads off in one grep:

arm x86-64 i386 arm32 aarch64 xtensa riscv32
COM interface yes yes yes yes — was missing
static array of managed yes yes yes yes — yes
ansistring yes yes yes yes yes yes
variant yes — yes yes — was missing
promotable int yes — — yes — was missing
record w/ managed fields yes — yes yes — was missing
dynamic array yes yes yes yes — yes

riscv32's four gaps are all closed here (COM interface, variant, promotable int, record-with-managed-fields), because they are the same omission and fixing one of six while the table is on screen is how the next one gets rediscovered in three months.

Not covered — and now written down

i386 and arm32 still have holes in that table: i386 has no variant, no promotable-int and no record arm; arm32 has no promotable-int arm. Those are silent leaks of exactly this kind and are filed as [[bug-a-scope-exit-release-matrix-has-four-holes-left-on-i386-and-arm32]]. xtensa's row is nearly empty and is Track S work — its exception runtime exists only under the Call0 ABI.

Gate

tools/gate.sh quick GREEN (self-host fixedpoint 111s). All seven listed tests byte-equal to the native oracle on riscv32, plus test_exceptions, test_cross_exception and test_interface_arc unchanged. Cross-target breadth is Track T's, against this sha.

Log