← board

Text-file primitives (Assign/Rewrite/Reset/Close) not visible inside a unit

Symptom

The classic Text-file procedures (Assign, AssignFile, Reset, Rewrite, Append, Close, writeln(f,…)) are ambient inside a program but undefined inside a unit — even one that uses sysutils.

context uses sees Assign?
program (none) yes — compiler auto-injects the primitives
program sysutils yes
unit sysutils no — undefined variable (Assign)
unit sysutils IntToStr etc. visible; only the file procs are missing
unit textfile (explicit) yes

The Text type itself resolves in a unit (a bare var f: Text compiles); only the file procedures are missing. So whatever step makes lib/rtl/textfile's procedures ambient runs for a program's main scope but not for a unit's interface/implementation scope.

Minimal repro

unit eng;
interface
uses sysutils;
procedure P;
implementation
procedure P;
var f: Text;
begin
  Assign(f, '/tmp/u.txt'); Rewrite(f); Close(f);   { error: undefined variable (Assign) }
end;
end.

uses sysutils, textfile; (explicitly naming textfile) compiles and runs. Note sysutils does not uses textfile (it uses platform, platform_types) — so the program-scope visibility comes from a compiler auto-injection, not from sysutils re-exporting.

Impact

Any unit that does file I/O the FPC way (no uses textfile, because in FPC Assign/Reset/Rewrite/Close live in System and are ambient everywhere) fails to compile. This is the adventure engine's blocker and will hit every library/demo unit that reads or writes a file. Idiomatic FPC code never names a textfile unit, so requiring it is non-platonic.

Fix

Make the textfile primitives ambient in unit scope exactly as they already are in program scope (mirror FPC: these are System-unit routines available to programs and units alike). Whatever injects them into the program's global scope should also inject them when compiling a unit.

Done when

Resolution (2026-06-25, v62)

The implicit textfile RTL was injected only by the program-level pre-scan, which sees only the program's own tokens. A unit pulled later via uses was lexed after that scan, so a unit using var f: Text; Assign(f,...) (no explicit uses textfile) never triggered the injection → undefined variable (Assign).

Fix (parser.inc, ParseUsesUnit): the existing per-unit token scan (which already flags try/raise and __pxxcoswitch) now also flags a Text / textfile / ioresult reference; if found (and the default surface is not opted out, non-ESP target) it loads builtin + textfile before ParseUnit, so the unit's Assign/Reset/Rewrite/Close resolve. Idempotent via the CompiledUnits guard, so an explicit uses textfile is harmless, and the self-build (PXX_NODEFAULTRTL) is unaffected — byte-identical.

Verified: the minimal-repro unit compiles with only uses sysutils; a unit read/write round-trip works; examples/adventure now advances past the Assign error (next blocker is an unrelated Move overload). Regression test/test_textfile_in_unit.pas (+ test/textfile_unit_dep.pas) under make test. Pinned v62.