SizeOf(<type name>) is settled against a KIND that cannot express the size
Split from [[bug-p-sizeof-rejects-twelve-type-names-that-a-declaration-accepts]]
on 2026-09-02, at the coordinator's routing call. That ticket's pattern — a
builtin type name settled against the builtin TABLE rather than against the
PROGRAM — had two instances and both are now fixed (582e4de09 for SizeOf,
2ba37ba91 for ParseTypeKind). This is what is left, and it is a different
defect: not an ordering question at all.
It is split rather than left as residue because a ticket titled "twelve type names" whose twelve names are resolved reads as done, and the newest finding should not sit in the least-read position of a closed-looking ticket.
Measured 2026-09-02, by two instruments that fail differently
| name | var v: N |
SizeOf(N) |
|---|---|---|
| ShortString | accepted, 263 | rejected |
| PChar, PAnsiChar, PWideChar | accepted, 8 | rejected |
| TextFile | accepted, 4128 | rejected |
SizeOf: unknown type or variable in every case — the same no-diagnostic shape
BuiltinTypeNameTk's header says it was created to end.
Identical on the pinned binary 766b99f98 (v401), which POSTDATES
582e4de09, so none of it is a regression from the recent work.
Corroborated, and the corroboration counts, because the two readings cannot
go wrong the same way: frankC read SizeOf(v) of a declared variable, and
frankb-a9 read the physical element stride of an array (@a[1] - @a[0]) —
deliberately not using SizeOf, since SizeOf is the operator under repair and
validating it with itself is the trap. Same five numbers.
Why no further ordering fix reaches them
582e4de09 added the fallback for exactly this shape — a name demoted from
builtin and then unresolved must come back to the builtin answer — and wrote:
if szBTkAny <> tyUnknown then prevTok := TypeSize(szBTkAny)
That fallback takes a KIND. ShortString is 263 bytes and TextFile is
4128; no TTypeKind carries a capacity or a record layout, so there is no value
szBTkAny could hold that produces them. The declaration side answers all five
correctly because it resolves a TYPE.
That is umbrella-sizeof-is-one-answer shape 1 stated on its own residue: the
oracle takes too few parameters, so a type whose size is not a function of its
kind cannot be answered at all.
Do not
Do not copy the declaration arms into SizeOf. They are the textfile,
shortstring, pchar/pansichar and pwidechar arms in ParseTypeKind
(pasparser_decl.inc; cited by content because they moved ~21 lines in
2ba37ba91 and a stale line number does not error, it points somewhere). Each
sets something a kind cannot carry — LastTypeStrCap, LastTypeRecId,
LastTypePointerElemTk — which is precisely why BuiltinTypeNameTk's header
restricts the shared table to side-effect-free names. These five are the names
that break that rule, so copying the arms would be the fourth instance of a
drift that header already records three times (Real, bare string,
Extended), bought with a green tick today.
TextFile settles it independently of the argument: it resolves through
IsRecordType('text'), so even a width table would not be enough.
Do not fix toward FPC's numbers. FPC says 256 for ShortString and 888 for
TextFile. Ours are 263 and 4128 and are correct about our own storage —
ShortString maps here to a 255-cap tyFixedString with an 8-byte length word.
Reaching for the oracle first is the obvious wrong move, and it is the same
distinction the string[N] work turned on. Per CLAUDE.md we are on par with the
LANGUAGE, not with FPC.
The lead
The carrier already exists. SizeOfSlot(tyFixedString, DEFAULT_STR_CAP) is
exactly 263 (DEFAULT_STR_CAP = 255, defs.inc). What is missing is that the
NAME never reaches a sizing call that takes a capacity — SizeOf(<name>)
resolves through a kind and stops.
So the direction is for SizeOf(name) to resolve the name the way a
DECLARATION does and size the resulting type, rather than to grow another
name→width table. Adding one would be the fifth oracle the umbrella exists to
prevent.
Wiring
Add to umbrella-sizeof-is-one-answer's blocked-by. It is the same sentence
as the C members closed on 2026-09-02 and as frankb-a9's string[N] work:
something other than the layout engine was asked how big a type is, and it
answered — or here, could not, and said "unknown type".
Log
- 2026-09-04 — resolved; this names the commit that carried the resolve, which is not always the one that carried the change — commit 6a890a405.
Resolved 2026-09-04 (frankh-15)
The lead in this body was right about the DIRECTION and stale about the NUMBERS. Only the direction was load-bearing, so separating them is worth a paragraph.
Right, and it held: do not copy the declaration arms in, and do not grow a
name-to-width table. The fix delegates to ParseTypeKind -- the declaration
resolver itself -- so a width changed in an arm is picked up with no edit at
the SizeOf site, and the only thing written down twice is a list of NAMES.
PChar/PAnsiChar/PWideChar are worth a note against this body's own
framing: their size IS expressible by a kind (pointer width). They failed not
because no kind carries the answer but because their ARM carries a pointee, so
the name never reached the shared table at all. Three of the five, not five.
Stale: SizeOfSlot(tyFixedString, DEFAULT_STR_CAP) is not 263 -- it
measured 264 before the flip -- and do not fix toward FPC's 256 inverted
after it. This body was written before fd186a975 and nothing updated it.
Why that was not a footnote: chasing the 263 is what surfaced the layout
split. SizeOf(ShortString) measured 264 where this ticket said 263, and
SizeOf(string[255]) measured 256 -- two numbers for one Pascal type. A
number that is merely stale reads as noise; a number that is stale in a
direction nothing explains is a lead.
The exposure half, closed 2026-09-04 — surveyed, and it is ZERO
The fixing session recorded honestly that it had not surveyed lib/ or
examples/ for the vulnerable shape and was "not claiming anyone was actually
hit — that half is open and I am not holding it". An exculpation needs an owner
for the residual question, so the coordinator took it. Result:
No site in lib/ or examples/ can hit this. Surveyed at HEAD for both
directions of the aliasing shape:
var/outparameters declaredShortString— none.var/outparameters declaredstring[N]— none.- every
shortstringoccurrence in those trees, listed rather than counted: four are comments (classes_lite.pas:215,typinfo.pas:24/35/41), and the two live ones aretypinfo.pas:392(pxxTkShortString = 25, a type-kind constant) and:632(PxxKindToTypeKind, a kind-to-kind mapping). Neither reads a length prefix, so neither depends on the layout.
So pin v403 (ce63beeeb, sha256 c31d03b202da) carries a real
wrong-VALUE defect with no reachable victim in the shipped trees. The bug
was reproducible on the pinned binary itself — len=122511465736197 against
HEAD's len=5 — and nothing in lib/ or examples/ is written in the shape
that reaches it.
What this does NOT cover, stated so it is not read as broader than it is:
code written AFTER 01:44 today against $(PXX_STABLE). The survey is of the
existing trees. Any lane now writing new Pascal that passes a string[N] to a
var ShortString parameter, or the reverse, will get a wrong length from the
pin and will reasonably suspect its own code first. The Track B/E lanes were
told directly.
A re-pin remains the owner's alone and nobody has asked for one.