ClassParent was missing from the class-reference operations
- Type: feature (missing
TObjectentry point) — Track A (compiler/builtin/builtin.pas,pasparser_call.inc,pasparser_prog.inc). - Status: done
- Opened: 2026-08-21, from the OOP differential that also produced
bug-a-metaclass-create-needs-a-declared-constructor. - Closed: 2026-08-21.
Symptom
WriteLn(d.ClassName + '/' + d.ClassParent.ClassName);
pascal26:137: error: "ClassParent": no such member on this record/class
FPC has ClassParent on TObject and it is ordinary reflection code —
walking a hierarchy for a name, deciding how far down a class sits, printing an
ancestry chain.
Why it was cheap
ClassName, ClassType and InheritsFrom are not three implementations; they
are one — GenMakeClassRefOp builds all three, IsClassRefOpName gates all
three, and ParseClassRefOpTail chains whichever of them yields a class
reference onto the next. That structure already carried every hard part:
- four receiver kinds — an instance, a
class of Tvalue, aTClassvariable, and a static class name — because each of those call sites asksIsClassRefOpNamerather than testing names itself; - chaining, so
o.ClassParent.ClassParent.ClassNameworks without anything new:ClassParentanswerstyPointer, which is exactly the conditionParseClassRefOpTailloops on; - member shadowing — a class that declares its own
ClassParentmember wins at the call site, since a real member outranks an operation in every arm.
So the whole feature is: one runtime helper reading one field, and three
one-line registrations. __pxxInheritsFrom already walks the very same
+PXX_RTTI_PARENT chain — ClassParent is one step of that walk.
This is what normalise-dont-special-case.md buys when it is followed: the
fourth case cost almost nothing because the first three were not three copies.
Contrast the metaclass-Create bug found in the same differential run, where
they were.
Fix
__pxxClassParent(Rtti: Pointer): Pointerinbuiltin.pas—nilin,nilout;nilat the root, matching FPC'sTObject.ClassParent.- A
ClassParentarm inGenMakeClassRefOpwithoutTk := tyPointer. ClassParentadded toIsClassRefOpName.- The bare-name token pre-scan in
ParseProgrampulls the builtin unit for it, besideclassname/inheritsfrom— so it works with nousesclause, as aSystementry point must.
Verification
test/test_classparent.pas, wired into test-core, byte-identical to fpc
3.2.2 across nine rows: two- and three-level chains, = 'TObject' at the top,
TObject.ClassParent = nil, the metaclass-value receiver, assignment into a
TClass and InheritsFrom off the result, the static class-name receiver, and
a class whose own ClassParent member shadows the operation.
Separately verified: a program with no uses clause compiles and runs it.
Gate: make compiler/pascal26 fixedpoint + tools/gate.sh quick GREEN.