← board

PInteger(p)^ does not compile, though var p: PInteger does

Symptom

var a: Integer; p: Pointer;
begin
  a := 7; p := @a;
  writeln(PInteger(p)^);     { error: undefined variable (PInteger) }
end.

var p: PInteger works. So does a user's own type PInt = ^Integer; PInt(p)^. Only the built-in names fail in a cast. Same for PByte, PWord, PCardinal, PDouble, …

Root cause

ParseTypeKind recognises these names in a TYPE position, but a type CAST resolves through the alias table (FindTypeAlias), which has never heard of them. PChar / PAnsiChar were given a cast path as a one-off (parser.inc ~7520); nothing else was.

The obvious fix is a TRAP — do not take it

Registering them as real aliases at startup (RegisterPtrAlias, next to RegisterBuiltinTGuid/TObject) looks right and even compiles. It silently breaks the compiler. Tried 2026-07-13; the self-host byte-identical gate caught it.

FindTypeAlias scans FORWARD and returns the FIRST match. A builtin registered before the source is parsed therefore shadows the source's own declaration. The compiler itself declares PWord = ^NativeInt (the machine word — see the PWord/ILP32 landmine), so a builtin PWord = ^UInt16 would quietly re-type it and the self-compile stopped being byte-identical. Units are lexed even later than the main source, so any RTL unit's own PInteger would be shadowed the same way.

So any fix must make a source declaration WIN. Options, roughly in order of safety:

The first is almost certainly the right one: it puts the builtin names exactly where they already live, and a real alias always takes precedence.

Workaround

Declare the alias locally: type PInt = ^Integer;. Costs one line.

Gate

make test + self-host byte-identical (this is the check that catches the shadowing — do not skip it) + cross.

Log