# V3 step 5.4 — Files (done 2026-09-22) Fourth stdlib landing; completes the plan-listed stdlib (Strings/TextIO/WholeIO/Math/Files). Suite 94/94 (51 run — incl. 4 trap-aborts, 5 sessions, 2 session-output, 2 session-input, 1 unit — + 43 reject). LL(1)-clean, zero gm2 warnings. ## Files (`stdlib/files`) Sequential files over libc stdio, bound via `EXTERNAL`: `OpenRead`/`OpenWrite`, `Close`, `ReadChar`/`ReadInt`, `WriteChar`/`WriteInt`/`WriteString`/`WriteLn`. A `File` is an opaque handle; `NIL` means the open failed. ## Shim additions (`runtime/syslib/shim.c`) `m2fopenread`/`m2fopenwrite` (convert the descriptor string to a NUL-terminated buffer, then `fopen`), `m2fclose`, `m2fgetc`, `m2freadint`, `m2fputc`, `m2fwriteint`, `m2fputs`, `m2fwriteln`. A shared `m2cstr` helper does the descriptor→C-string copy. ## Tests `FilesProg` → 42: writes `42` to `gen_ssa/_files_tmp.txt`, reopens it, reads the integer back into `ExitCode`. ## Two findings (documented, affecting later work) 1. **Qualified access is flatten-on-import.** `Files.WriteInt` materializes `WriteInt` into the client scope; if the client already imported a different module's `WriteInt` (e.g. from `SysIO`), `Materialize` returns the existing node and the call resolves to the wrong signature (233). The tests avoid mixing same-named imports; true per-module qualified resolution is deferred. 2. **Handles must be pointer-typed.** `File = INTEGER` gave a `w` (32-bit) value and truncated the C `FILE*` on return, segfaulting on reuse. `File = POINTER TO FileRec` (opaque, `l`) matches the C pointer width. Rule: any value carrying a host pointer must be a pointer-class type, not `INTEGER`. ## Deferred to 5.5+/step 7 `Storage`/`SYSTEM` (`ALLOCATE`/`DEALLOCATE`, `ADDRESS`, generic pointers — needs SYSTEM types), `ReadString`, string escapes, explicit external link names. Traps still `$abort`; `runtime/syslib` `SYSTEM`/`Storage`/`Trap` land with the step-7 syslib port.