← board

ReadLn from a text file into a frozen string segfaults

var f: Text; s: ShortString; ... ReadLn(f, s); → SIGSEGV. Change s to string and the identical program reads the line correctly.

The working half is the finding. A crash on a construct nobody uses is a gap; a crash on the frozen spelling while the managed spelling on the same line works is a routing bug, and it says where to look without a debugger.

Cause

ParseTextReadRest had one string arm, and it emitted a call to

procedure TextReadStrTo(var f: Text; var s: AnsiString);

for every target whose kind was string-ish. For tyAnsiString the var parameter is a pointer to an 8-byte managed handle and that is what the callee writes. For tyShortString / tyFixedString / tyString the same pointer addresses an inline [len][chars…] slot, so the callee's handle store lands on the length prefix and the first characters, and the subsequent dereference of the "handle" it reads back walks a pointer made of text.

TypeIsFrozenString(tk) is the predicate that separates the two — it covers tyString, tyShortString and tyFixedString, i.e. exactly the kinds whose storage is inline.

Fix

A new TypeIsFrozenString arm in ParseTextReadRest, before the generic string arm: allocate an AllocVar('', tyAnsiString) temp, read into that, then emit AN_ASSIGN destNode := temp. The existing AnsiString→frozen assignment path already clamps to the target's capacity, so string[4] now truncates exactly as fpc does rather than overrunning.

Done at the AST level deliberately: one change covers x86-64 and every cross backend, instead of six copies of a prefix-width special case in codegen.

Scope of the pre-existing claim

Measured on compiler/pascal26 at HEAD and on the pinned compiler from v405. Neither is a regression from the semantic-identity work (decide-how-a-type-carries-an-identity-its-kind-cannot-hold) that was in flight when this was found — it was found because a test for that work read a ShortString, not caused by it.

Sibling, same session, same shape at the other door: bug-p-read-into-a-frozen-string-uses-the-wrong-prefix-width. Fixed one arm of a double case, grepped for the sibling, found it.

Test: test/test_read_into_a_frozen_string_from_stdin_and_a_file.pas, .expected copied from fpc's own output, wired into the Makefile.

Fixed in 7a79ff1d2.