← board

tyUnknown means both "untyped pointer" and "I read garbage"

The collision

tyUnknown (ord 0) carries two unrelated meanings on a pointee field:

  1. "the pointer is untyped" — a genuine Pointer formal, which legitimately accepts any argument. ProcRetPtrElemTk documents this as its own convention: "pointed-at TTypeKind ord when the result is a typed pointer (tyUnknown otherwise)".
  2. "nothing wrote here" — the zero an unset array slot holds, and the value a recycled symbol slot reads back as once some unrelated symbol occupies it.

A consumer sees ord 0 and cannot tell them apart. Worse, meaning (1) is the permissive one, so the guard fails open: on garbage it does not refuse, it allows.

The measured instance

MatchParamCompatible narrows the blanket tyPointer <- tyClass rule by asking what the parameter points at. It read Syms[Procs[i].Params[j].SymIdx], whose slot SymRollbackTo had already handed back:

REG   proc=GetPropInfo i=0 sym=363 elemtk=5 (tyRecord)  name=cls kind=2 (skParam)
MATCH proc=GetPropInfo j=0 sym=363 ptrelem=0 (tyUnknown) name=o   kind=1

o is the calling program's own variable. The guard read ord 0, concluded "untyped pointer, permitted", kept the PClassRTTI arm viable, preferred it over the exact class match, and GetPropInfo(AnObject, 'Caption') segfaulted.

Had the two meanings been distinct, the same stale read would have been an immediate internal error naming the exact problem, instead of a segfault in a user program three layers away.

Why it is filed and not fixed here

Separating them is a type-model change, not a call-site change: a distinct tyInvalid/tyUnset ord, every producer taught which one it means, and every <> tyUnknown test audited for which of the two it actually intended. Several of those tests are correct today precisely because the two collapse. That is an A-lane change with a real audit attached, and it should not ride along inside a P-lane bug fix.

What to check when it is taken

Gate

make compiler/pascal26 + tools/gate.sh quick; the audit above is the work, so the real gate is that every converted <> tyUnknown test states which meaning it tests for.