← board

A set- or fixed-array-returning function answers an empty/partial value

function MkSet: TSet;  begin MkSet := [1, 4]; end;         { FPC: {1,4} }
function MkArr: TArr;  begin MkArr[0] := 8; MkArr[1] := 9; MkArr[2] := 10; end;

s := MkSet;   { pxx: the EMPTY set — 1 in s is False }
a := MkArr;   { pxx: 8 0 0 }

Both silent, both on every target. Every way of producing the set failed (literal, via a local, from a global, built up with +); only a var parameter worked, which is what localised it to the RESULT path.

Two different causes

The setFuncName := [1, 4] leaves the LHS ASTTk unset (0), and the set arm of the assignment lowering tested the NODE type only, so the assignment fell through to the scalar store and wrote the literal's ADDRESS into the 32-byte Result slot. The record arm immediately below it already carries the symbol-TypeKind fallback for exactly this case, comment and all — the set arm is the sibling that never got it. Textbook devdocs/dev/normalise-dont-special-case.md: fix one arm of a double case, grep for the sibling.

The arrayfunction F: TArr records the ELEMENT kind in Procs[].RetType, so RetViaHiddenDest (a kind-only predicate) answered False and no hidden destination was allocated anywhere. The callee filled its Result array correctly and then returned element 0 in a register. The parser now records the byte size in ProcRetFixedArrBytes and the new ABIRetViaHiddenDestProc in the ABI oracle joins the two, so the caller allocates a right-sized scratch, the callee's epilogue copies into it, and a := MkArr copies the whole array out of it (a new assignment arm — the existing whole-array arm requires an LVALUE on the right, and a call is not one).

That the array half needed a per-proc marker is the ABI oracle's case in miniature: the type's identity was not recoverable from the kind, so five backends and the return path each answered a question they could not answer. Every one of them now asks ABIRetViaHiddenDestProc.

Scope, deliberately narrowed twice — both by measurement

Verified

test/test_aggregate_function_results.pas — every set-result shape, the array result twice (fresh and over a dirtied destination), and record/shortstring results as controls. Byte-identical to FPC's own output on x86-64, and the same last line on aarch64 and riscv32 under qemu. The 30-row parameter × aggregate matrix that found this now matches FPC exactly. make compiler/pascal26 fixedpoint + tools/gate.sh quick + make test-core (a new compiler global — the MAX_GLOBFIX landmine only the --threadsafe self-host shows) GREEN.

Log