← board

The BooleanNN family, and what separates it from *Bool

Measured 2026-09-09, compiler 9b2cfef2721d, against fpc 3.2.2.

width Ord(True) X(65535) → Ord High sign
Boolean16/32/64 2/4/8 1 65535 1 unsigned
*Bool 1/2/4/8 -1 -1 -1 signed

Shared, and asserted as such: nonzero-is-true at if, not and WriteLn; Low = False; the width comes from the name; a bound carries the named type's width.

The signedness row cannot fail on a small probe. Boolean16(200) and WordBool(200) both answer 200 — 200 fits a signed 16-bit. 65535 is the discriminator, and this is the same trap ByteBool's own comment records one family over (200 vs 255).

The design, and why it costs twenty readers nothing

The SemId encoding's negative space was built so an unwidened reader gets a TRUE answer rather than a wrong one. The second family is placed below the first (SEM_PBOOL_BASE = -32, so Boolean16 is -34) rather than beside it, so:

BoolStorageTypeKind(SemBoolWidth(sem)) was written out at four call sites and is family-blind. It is now one function, BoolStorageKindOf(sem), so the next family is one arm and not a fifth transcription. Same for the three materialisation sites, now SemBoolMaterialiseSem.

Three rows of this ticket were stale, and one would have stopped the work

Re-measured before starting, per this file's own "re-run the rung" rule:

A hazard block is the most expensive kind of stale row: it is written to stop a reader, it succeeds, and nothing about obeying it produces a signal.

Two things measured on the way that were not the subject

fpc cannot store its own High(WordBool). wb := High(WordBool) is refused by fpc's assemblerword value exceeds bounds 9223372036854775807 — while the same line compiles here. SizedBoolBound's comment predicted exactly that from the other direction; this is it observed.

QWordBool is where the truncation coincidence stops. fpc answers the Int64 extremes for High/Low of every *Bool width and we answer True/False. At widths 1/2/4 the extremes truncate to exactly our -1/0 even through an Int64() readout, because the ordinal is narrower than the reader — so the two agree and the chosen divergence is invisible at every width the original measurement used. At width 8 there is nothing left to truncate. Both findings are now in SizedBoolBound's comment.

The corpus claim, and what it actually bought

uthlp.pp declares record helper for Boolean16 and twelve tthlp* files use it — verified, not taken on trust. All twelve moved off unknown type: Boolean16. None of them compiles yet: all twelve now stop at uthlp.pp:242, Length needs a string, an array or a PChar, not Integer, on Result := Length(Self) inside a record helper for a dynamic array type. Filed as [[bug-p-self-in-a-record-helper-for-a-dynamic-array-types-as-integer]].

One name unblocked twelve files by exactly one defect. This ticket warned that moving a corpus wall forward "looks like progress and is not"; it meant that about shipping the sibling bug, and the warning landed somewhere else instead. Report the wall that moved, never the files that "now work".

Log