← board

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