# m2compiler-V3 — status (2026-10-19) This answers "where are we now?". ## Numbers - Suite **246/246** (`compiler/run_tests.sh`). - Self-hosting **FIXPOINT OK** — stage2 and stage3 images are byte-identical (3,872,467 bytes). - Corpus **427 / 612** sources compile with 0 errors, **0 crashes** (`tools/v3-corpus/corpus.sh` over the gm2 testsuite); the rest are genuine language gaps, **gm2 leniencies V3 deliberately rejects**, or unresolved imports. - Branch **`master`**; tree clean. ## Architecture (current) ``` per unit: Parse -> AST + declarations; each procedure body is skipped and re-parsed at unit end, so forward references bind normally Lower -> walk the AST, emit QBE (the only emitter) Emit -> buffered image, EndModule ``` - **Two-phase parse.** Procedure bodies are deferred (module level) and re-parsed after the unit's declarations. The re-parse is **order-aware**: a body sees only module-level variables, constants and types declared before its procedure (forward *procedure* calls remain allowed). `-notwophase` restores the legacy inline parse for debugging. - **No resolve/check pass.** The `Check` module was deleted once it was proven vestigial (gutting it left the emitted `.ssa` byte-identical): binding is late by construction. - **`Lower` is the only emitter**; the AST is the interface. ## Language coverage Program modules; `CONST`/`VAR`/`TYPE` (scalars, subranges, enums, arrays, records incl. variants, sets, pointers); `WITH`/`IN`; whole array/record copy; `NEW`/`DISPOSE`; full expressions and control flow; `GOTO`/labels; procedures (value/`VAR` params, functions, recursion, `FORWARD`, nested procedures, procedure values); Clarion-form `CLASS` (methods, single inheritance, virtual dispatch); PIM coroutines; Unicode (`UCHAR`/`UString`, UTF-8 edges); ISO type conversions `T(x)`; separate compilation (`FROM`/`IMPORT`, qualified `L.x`, opaque types); **generics** (ISO 10514-2 refinement): type/value/procedure parameters, constraints, nested generics, generic classes. ## Landed recently (this thread) - AST + `Lower` (L0–L7) — `Lower` emits the whole session from the AST. - Two-phase parse; forward machinery deleted; `Check` deleted. - Generics increment 3: value/procedure parameters, constraint model, nested generics, generic classes via generic modules. - ISO type conversions `T(x)` (incl. CONST folding), the builtins `ODD`/`TRUNC`/`FLOAT`, and BOOLEAN/ENUM array bounds + indices: corpus 365 → 381. - **SET CONST** (static masks): a set constant (`CharSet{"0".."9"}`, a union of set constants) is folded to a mask at parse and re-emitted as a pooled `data $setN` descriptor. **Builtin `CONST` folding** (`CAP`/`ABS`/`ODD` over immediates), `BOOLEAN` `CONST`s, representation-changing `CONST` conversions (`VAL(REAL, int)`, `VAL(LONGINT, int)`, …), **procedure values** (`ADR(proc)`, `VAL`↔`PROC` conversions, `p := foo` into a `PROC`), SYSTEM sized reals (`REAL32`/`REAL64`), `FROM SYSTEM IMPORT SHIFT`/`ROTATE`/`CAST`/… (a builtin import is accepted), and ISO foreign modules (`DEFINITION/IMPLEMENTATION MODULE FOR "C"`, `EXPORT UNQUALIFIED`), and ISO `CONVERT(T, x)` (a `VAL` sibling), set↔word `VAL`/`CAST` (BITSET), and **enumeration literals with their type** (export/import/ qualified; `imp*`) and **`ORDL` de-reserved** (a gm2/FileIO helper, not a built-in; `fileio`/`baseimport`): corpus 382 → 405. Fixed a `SHIFT`-by-a-negative fault and the qualified-const value (`LoadDesignator`). - The **corpus harness** now resolves **transitive** imports (it used to under-provide `.def`s); this more accurate harness is the method from 2026-10-18. - Clean `M2` exit codes (0 ok / 1 errors / 2 usage-IO). - Bug fixes: `PopPos` wiping repass diagnostics; global record data size (`w 0` → `l 0` for pointer/`LONGINT`/proc fields); template arithmetic over a type formal; proc-typed formals in DEF/IMPL; imported procedures used as values. - Showcase `showcase23` (generic doubly linked list). ## Conformance policy V3 follows **Modula-2** (PIM/ISO), not gm2's degradations. In particular the language requires **declaration before use** (the only forward reference is a `POINTER`'s base type), so the use-before- declaration programs that gm2 accepts are **rejected by design**. The corpus is gcc's gm2 testsuite, so its number mixes conforming programs with gm2 leniencies; each known case is catalogued (with a reproducer) in `docs/gaps/`. *Known non-conforming behaviour still retained* (pre-existing, not yet removed): forward **procedure** calls from a body (the two-phase parse defers procedure bodies, and the generated parser's mutually recursive productions rely on it). Everything else is strict: - the declaration part: module-level forward type/`VAR` references, forward types inside a `TYPE` block, and forward `SET OF ` are rejected (Track A, 2026-10-18); - inside a deferred procedure body, a reference to a module-level **variable, constant or type** declared *after* the enclosing procedure is rejected as undeclared (Track P3, 2026-10-19) — the body re-parse is order-aware (`SymTab` declaration sequence + `Lookup`). `FORWARD` remains the way to call a later procedure. Corpus 427 → 421 (the drop is 6 gm2-leniency files now correctly rejected, e.g. `block`, `blockindirect`, `nestedfor`). Module-level forward **procedure** references and `FORWARD` declarations still work (`t_fwdproc`, `t_fwdok`). ## What remains 1. **Corpus** — genuine gaps remain (`not supported yet`, `undeclared identifier`, `invalid call`); separate them from gm2 leniencies (`docs/gaps/`) and missing stdlib imports (`LIB`). See `docs/analysis_gaps.md`. 2. ~~**The size-sensitive V3 miscompile**~~ — **root-caused and fixed** (2026-10-19; see `docs/summary_l3_selfhost_fix.md`). It was never size-sensitive: the fixpoint gate matched the literal string `"Parsed correctly"` that the `ASTDUMP` debug dump printed unconditionally, so the gate passed while the compiler session was actually reporting `Incorrect source` (Lower's forward procedure references → `invalid call`). Removing `ASTDUMP` merely removed the masking line, which looked like a size effect. Fixed by `a3ef897` (remove ASTDUMP; gate on `Parsed correctly`; declare Lower's walkers in `Lower.def`). The gate now fails on errors (verified with `t_fwdvar`), and self-hosting stays byte-identical. 3. **Robustness** — fixed table limits resolve silently when exceeded; weak error recovery; error-message polish. 4. **Library holes** — channel/device framework (`docs/wip/`). 5. **Housekeeping** — `docs/INDEX.md` is the entry point; the unreferenced historical records are under `docs/archive/` (2026-10-19). `resolve-check` was merged to `master`. ## How to check for yourself ```sh cd compiler ./build.sh # M2.atg -> scanner/parser/driver -> ./M2 ./run_tests.sh # 246/246 cd .. ./bootstrap/fixpoint.sh # FIXPOINT OK (byte-identical stage2/stage3) ```