Read into a frozen string uses the wrong prefix width
var s: ShortString; ReadLn(s); from stdin stores a value that is wrong in a
way that survives the obvious check. EmitReadVarParse's tyString arm writes
a QWORD length at [rdi] and the characters at rdi+8 — the tyFixedString
word-prefix shape — into a byte-prefix [len][chars…] slot.
The bytes, reading abc into a ShortString, pxx vs fpc:
pxx 0 0 0 0 0 0 0 97 98 99 0 0
fpc 97 98 99 35 35 35 35 35 35 0 0 0 (35 = pre-filled '#')
Length(s) was correct on both. The qword length shares its low byte with
the byte prefix on a little-endian target, so the single field anyone spot-checks
agreed with the source while every character was six bytes off. That is the
reason this outlived the segfault it is a sibling of: the crashing door
announces itself and this one hands back a plausible length.
There was also no clamp to the declared capacity, so s: string[4] reading a
sixteen-character line wrote past the slot.
Fix
ParseReadArgsAST: for a TypeIsFrozenString target, allocate an AnsiString
temp, put the temp in the ARG chain, and chain an AN_ASSIGN dest := temp
onto a raHead/raTail list; the whole read returns GenMakeSeq(node, raHead)
when any temps exist. The frozen-assignment path already truncates to capacity,
so string[4] now yields over / long exactly as fpc does.
At the AST level, not in codegen: the wrong-prefix arithmetic exists once per backend, and normalising the read into "managed read + assign" removes all six copies rather than correcting them.
Pre-existing
Measured at HEAD and on the pinned compiler from v405. Not a regression from the semantic-identity work in flight beside it.
Test: test/test_read_into_a_frozen_string_from_stdin_and_a_file.pas, fed
printf 'abc\nlonger-than-four\n77\n', .expected copied from fpc.
Fixed in 7a79ff1d2.