A class typecast is refused as a by-reference argument
- Type: bug (compat — everyday Pascal is refused) — Track P
(
compiler/pasparser_call.inc). - Found in fcl-passrc rung 7, [[feature-pascal-corpus-passrc]].
The measurement
Bump(TBase(d)); { class hard cast — refused }
Bump(TBase(o.Fld)); { over a field — refused }
BumpPtr(PtrUInt(u)); { pointer spelling — COMPILES }
fpc 3.2.2 -Mobjfpc compiles all three. pxx answered
error: by-reference argument must be a variable on the first two.
Where it was decided
IsVarArgLvalueCast accepted ASTKind[n] = AN_PTR_CAST only. That is an
enumerated predicate — a hand-maintained node-kind list that must grow per
new member, with no diagnostic when it does not. AN_CLASS_CAST was the
member it never got. The backend was never the limit: IRLowerAddress peels
both kinds.
What must NOT be added to the list
d as TBase performs a checked conversion and produces a value. Accepting
it would take a temp's address and drop the callee's write on the floor, with
the call still compiling — a silently wrong program in place of a refusal. fpc
3.2.2 also refuses it here (Can't take the address of constant expressions),
so the boundary is not ours to move. Named in the test header so the next
reader does not "complete" the list.
Resolution 2026-09-06
IsVarArgLvalueCast accepts AN_PTR_CAST or AN_CLASS_CAST over
AN_IDENT/AN_FIELD/AN_INDEX/AN_DEREF.
test/test_a_class_typecast_is_a_by_reference_argument.pas, wired in the
Makefile, byte-identical to fpc:
var : nil
field : nil
element: nil neighbour: live 9
ptrcast: 42
Every row asserts the callee's nil ARRIVED, not that the call compiled —
a fix that materialised a temp would compile every row and change no output
except this file's. Arr[0] is created deliberately: an un-created slot is
nil too, and nil is what a correct Bump writes, so the neighbour column
would have been a control whose expected value collided with its failure value.
Log
- 2026-09-06 — fixed and resolved, commit 72c937239 (the fix and the close are the same commit).