← board

P: object gets its standard meaning; the rooted reference is retired

Decided in decided/decide-revisit-object-types-rtl-generics-fired-the-trigger (owner, 2026-08-30). Filed as a bug, not a feature, per CLAUDE.md's compat table: real Pascal source compiles wrong, or not at all -> bug, own lane, own prio.

Repro

$ pinned generics.collections.pas
pascal26:146: error: generic templates must be class, record, interface,
                     array or procedure declarations
  near:  T  PT   >>> object strict private

The blocking declaration is a stateless methods-only handle — no fields, no inheritance, no virtual methods, no constructor, reached by @ as a pointer:

TCustomPointersCollection<T, PT> = object
strict private type
  TLocalEnumerable = TEnumerable<T>;
protected
  function Enumerable: TLocalEnumerable; inline;
public
  function GetEnumerator: TEnumerator<PT>;
end;

Exactly one = object across all six rtl-generics units, with 7 references.

Part 1 — retire the rooted reference

pasparser_decl.inc:492 resolves object in type-reference position to tyPointer with PtrElemTk=tyClass, PtrElemRec=REC_NONE. It was added 2026-07-03 (7859911e3) to provide "a lightweight root, like TObject without a unit". Builtin TObject landed 2026-07-12 (c53dd8953, RegisterBuiltinTObject), which supersedes it entirely.

Part 2 — object as a value type

Accept object in type-declaration position and lower it exactly as a record-with-methods. The two positions are distinguishable by one token of lookahead (var o: object; ends at ;/); type T = object is followed by members or an access specifier), but with Part 1 done there is no ambiguity left to resolve.

Already supported on HEAD and needing no new work — generic records with methods, strict private type sections, pointer-to-specialization access. The deltas are:

  1. the parser arm in type-declaration position;
  2. protected inside it — records refuse it ("records do not inherit"), and it is inert here since nothing derives from the type.

Hard-error, naming this ticket, on: an ancestor (= object(TParent)), virtual/dynamic/override, constructor/destructor. Those are the real second-object-model cost and are deliberately out of scope. A loud diagnostic is the whole point — the earlier decide-old-style-object-types refused a middle option specifically for silently refusing virtual.

Gate

make compiler/pascal26 (self-host fixedpoint) + the repros:

Breadth is Track T's against the pushed sha.

Consequences

Log

Resolution (2026-08-30)

Both parts landed. object in type-DECLARATION position is now the standard Pascal value type with methods, lowered as an advanced record; the rooted class reference it used to mean in type-REFERENCE position is gone.

Part 1 removed the object arm in pasparser_decl.inc and converted test_object_reference.pas to TObject.

Two corrections to what Part 1's own commit message claimed, both worth recording because both were the same mistake — asserting rather than measuring:

Part 2 added the type-declaration arm, protected under object, generic object templates, and the three refusals — ancestor, virtual/dynamic/override/ abstract, constructor/destructor — each naming this ticket. Two of the three had to be explicit rather than merely absent: the record machinery underneath would have accepted constructor silently, and virtual falls out of the record directive loop to be misread as the next field. isObjectType is threaded as a parameter, not a global, so nothing in defs.inc changed and no Track A ticket was needed.

The repro, measured both ways against the same file:

pinned:  generics.collections.pas:146: error: generic templates must be class,
         record, interface, array or procedure declarations
HEAD:    past it; the wall moves to :120, `unknown type: PT`

The new wall is a pre-existing generics scope gap that pinned never reached, because it aborted 26 lines later at the syntax error before any specialization was streamed. Filed as [[bug-p-generic-type-param-unresolved-in-class-abstract-template]] (p70), which is now what rung 6 of [[feature-pascal-corpus-expansion]] waits on.

Follow-ups not done here: