# V3 step 4.3 — separate compilation (done 2026-09-22) Single-image session model (V1/V2 lineage): one compiler run takes `lib.def lib.mod prog.mod ...` and produces one `.ssa` image with the definitions, implementations and the program linked together. Suite 87/87 (44 run — incl. 4 trap-aborts + 4 sessions — + 43 reject). LL(1)-clean, zero gm2 warnings. ## Session model - **Driver** (`compiler.frm`): collects all `NextParameter` files, calls `SymTab.Init` and `QbeGen.OpenModule` once, then parses each file into the shared table. The program module is last; a session without one is `Incorrect source` (nothing to link). - **DEFINITION MODULE** — opens its own scope, records CONST/TYPE/VAR and procedure headings (bodies discarded via `AbortFunc`), emits nothing. - **IMPLEMENTATION MODULE** — resumes the definition's scope, lowers procedure bodies to `export function`s, and its `BEGIN` body to `_init`, which `main` calls at startup (before the program body). - **Program MODULE** — imports resolved, emits `main`; the image is named after its module (`EndModule(m1)`). ## Modules / imports (SymTab) - Module registry (`modNames/modScopes/modKind/modImpl`): module names live in the global scope as `KindModule`; each module gets a child scope that *is* its export table. - `Materialize` clones an export into the current scope; shared by `FROM L IMPORT x` and by qualified `L.x`/`L.P` (flatten-on-first- use, idempotent). Unknown modules stay silent stubs (legacy); a known module's missing export is 201. - `ResumeProc` lets an implementation heading re-open a definition's procedure (fresh body scope, formals re-entered); its uid — hence the mangled `$P_uid` symbol — matches what clients call. - **Opaque completion**: `OpaqueBase` detects a definition's opaque alias (target still `InvalidType`); the implementation's `TYPE T = ...` completes it via `SetTarget` instead of re-declaring. `TYPE T;` stays usable behind `POINTER TO T`. ## Backend (QbeGen) - Output is now fully buffered (`sessBuf`, 1 MB) and written at `EndModule` under the program module's name, so a session whose output name is only known once the program parses still lands correctly. Nested-function hoisting keeps its per-depth buffers. - `BeginInit/EndInit` emit module init functions; `BeginBody` calls each recorded init right after `main`'s `@start` and real-const flush. `LoadDesignator` is the shared value-load path for qualified access. Bare zero-arg function calls (`F` without parentheses) now work. ## Tests Sessions (`expect_run_files`, `expect_fail_files`): `DBasicProg` 49 (FROM imports of const/var/proc, impl `BEGIN` init), `DQualProg` 46 (qualified `L.x`/`L.P`, bare `DLib.Bump`), `DTypesProg` 36 (array + record types shared across units), `DOpaqueProg` 55 (opaque `Node` completed in the implementation, factory + accessor), `d_badimp_prog` 201 (missing export). The old `t_defunit`/`t_implunit`/`t_opaqueptr` "not supported yet" rejects became session-level rejections (definition-only / no program). ## Deferred Strict signature checking (231) for impl-vs-def headings (arity is checked; types are not), variant records, per-file object emission (this is whole-program single-image), import cycles, `HIGH`/`LEN`, class lowering.