← board

A representation contract

Why this is not ordinary doc work

Owner, 2026-09-02: "there is no formal OOP specification. delphi just does as they see fit, FPC did the same, trying to emulate delphi .. and we take (most of) their design decisions as FPC is de-facto standard in 2026. C is more defined — and even then we found plenty discrepancies."

For representation there is no document to be right or wrong against. That makes ours the specification, and it changes what the page owes: not "here is how it works today" but "here is what you may rely on."

Start from what is already right

docs/language/types.md is 393 lines and already correct on several points — sets as a 32-byte bitset supporting up to 256 elements, string as managed and reference-counted. Copy the Real section's shape, which is the best thing in the file:

"SizeOf(Real) is 4, and an array of Real strides by 4. A Real written to a file or sent over a wire is 4 bytes."

Size, stride, and the file/wire consequence in three lines. Stride is the part that is usually missing and the part that actually bites.

What to add

Divergences need a user-facing home

known-incompat/ is an internal folder a user never sees. Where we deliberately differ from FPC — SizeOf(set), SizeOf(string[N]), plain string — the doc must say what we do, that it is deliberate, and why, not leave a reader to discover it against FPC. Sources: known-incompat/README.md and [[decide-a-what-a-set-costs-bits-bytes-bounds-and-what-file-of-t-writes-to-disk]].

Gate

Track D's own: prose only, never compiler/** or lib/**, and every snippet compiles against $(PXX_STABLE). Sizes quoted here are claims — measure each one against the pinned compiler and say which pin, rather than copying the numbers out of this ticket.

Blocked on the shortstring work for the string half only. The set, record and file of T sections are writable now and do not change.

Log

Delivered 2026-09-05 — docs/reference/representation.md

226 lines, wired into the reference index and cross-linked from docs/language/types.md (top, Strings, Sets). Guaranteed-vs-incidental is separated throughout, as the ticket asked for.

Every number measured, not derived. Probes run natively on x86-64 and under qemu on i386, arm32, riscv32 and aarch64, plus an FPC 3.2.2 oracle for each divergence row.

The target sweep changed the content, which is why it was worth doing. string[300] is 308 on the 32-bit targets and 312 on the 64-bit ones, so the rule for N > 255 is N+8 rounded to the target's pointer alignment — and such a string is not portable across word sizes in a file or a record. Measured on x86-64 alone it is 312 and reads like a constant. That row is now a warning in the page rather than a table entry. string[N <= 255] is N+1 on every target and byte-identical to FPC, so that guarantee is real.

Two of the ticket's own claims did not survive measurement:

Records needed no correction: 8 with the field at offset 4, packed 5 at offset 1, identical on all five runnable targets and to FPC.

The page is gated, not just asserted. It carries a complete self-check program that docsnip.py compiles against the pin — the run went 39→40 complete programs and 30→31 compiled, so the snippet was demonstrably seen rather than skipped. It compiles under the pin and prints the same rows there as at HEAD.

Verified with PXX at ce19e5482, binary 9bcfd2b4da30, and the pin stable_linux_amd64/default/pinned.