summary_step4.3.md 3.2 KB

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 <Prog>.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 functions, and its BEGIN body to <mod>_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.