← board

Aggregate / frozen-string result via virtual or indirect call — cross backends

What's missing

On i386 / arm32 / aarch64, a function returning an aggregate (record/set) or a frozen string via a virtual (IR_VIRTUAL_CALL) or indirect (IR_CALL_IND) call now errors cleanly:

target arm32: aggregate/frozen-string result via a virtual call is not yet
supported (feature-cross-virtual-indirect-hidden-dest)

rather than silently emitting a call that never loads the hidden-destination register → garbage dest → crash. Direct calls work on every backend (they reuse the proven record-return dest path). riscv32 / xtensa don't implement virtual/indirect calls at all (bare-metal, no OOP), so they're moot there.

The work

Mirror the x86-64 codegen (ir_codegen.inc): in each cross backend's IR_VIRTUAL_CALL and IR_CALL_IND case, when IRCallDest[node] >= 0, evaluate that IR_LEA and load it into the target's indirect-result register before the dispatch — the same register EmitAggregateDestStash reads (i386 = ecx, arm32 = r12, aarch64 = x8). The dest IR_LEA only touches the scratch reg, so the already-loaded argument registers stay intact (verified on x86-64 with r10). Remove the clean-error guard once each is wired.

Why deferred

x86-64 is the tested path (no cross harness exercises a virtual/indirect frozen/aggregate return — and nested dynarrays already segfault on these targets, [[bug-nested-dynarray-cross-segfault]]). Writing untested cross codegen for a niche-within-niche path (cross + frozen/aggregate + virtual/indirect) was held back in favour of an honest compile error.

Acceptance

Log

RESOLVED 2026-07-12 (7f0c7442) — i386 / arm32 / aarch64 all wired

All six clean-error guards on the three OOP-capable cross backends are gone; the hidden caller-destination now travels on the virtual and indirect paths too.

Test: test/test_cross_virtual_indirect_aggret.pas — virtual + indirect record and string returns, register-arg and stack-arg shapes, oracle == x86-64; wired into the i386 / aarch64 / arm32 suites. Gate: testmgr quick green, self-host byte-identical, make test-i386 / test-aarch64 / test-arm32 all green.

Pre-existing caps hit while writing the test (NOT part of this ticket, unchanged): x86-64 indirect calls allow <= 6 params; aarch64 virtual calls allow <= 8 arg words (Self included).

riscv32 / xtensa keep their guards — bare-metal, no OOP dispatch exercised.