Indexing a frozen string through a pointer deref reads the wrong byte
type TS10 = string[10]; PS = ^TS10;
var s: TS10; p: PS;
begin
s := 'hello'; p := @s;
WriteLn(s[1]); { h — correct }
WriteLn(p^[1]); { blank }
end.
Measured 2026-09-02 at 764dc3a30, compiler e81a80c4621c, under
-dPXX_SHORTSTRING, on x86-64, aarch64, arm32 and riscv32. Default mode is
correct on all four.
Why it is the deref path and not the index origin
index direct (s[1]) and index field (r.f[1]) are green in the same
run on all four backends. Only the deref spelling is wrong. So the origin
computation follows the prefix width correctly where the symbol is reachable,
and does not where the operand is a bare pointer whose value IS the buffer
address — the same shape that made Length(p^) wrong before 764dc3a30.
A blank rather than garbage is consistent with reading at base + 8 while the
chars begin at base + 1: offset 8 of an 11-byte slot holding 'hello' is
still inside the slot and still zero-filled.
Where it is asserted
test/test_shortstring_through_a_pointer.pas, row index deref. One of the
two reasons that file's -dPXX_SHORTSTRING rows are not yet wired.
[[bug-a-comparing-a-frozen-record-field-to-a-literal-crashes-or-answers-false]] [[feature-p-implement-the-real-tyshortstring-byte-prefix-layout]]
RESOLVED — 0dd5858e6
Cause: DerefFrozenStrPtrSym wired into IRLowerAddress; closed the silently DROPPED WRITE with the read.
Re-verified at 05f50f9ae with the repro in this ticket, unchanged, against the FPC 3.2.2 oracle: exact match at default AND -dPXX_SHORTSTRING. Covered going forward by test/test_frozen_field_and_deref_readers.pas, wired into all 12 expected blocks (4 native modes; x86-64, aarch64, arm32, riscv32, xtensa x 2 modes) — the 32-bit targets included, since this is a width class and x86-64 is where width bugs hide.
Log
- 2026-09-03 — resolved; this names the commit that carried the resolve, which is not always the one that carried the change — commit 24a899f7a.