This answers "where are we now?".
compiler/run_tests.sh).tools/v3-corpus/corpus.sh over the gm2 testsuite); the rest are
genuine language gaps, gm2 leniencies V3 deliberately rejects, or
unresolved imports.resolve-check; tree clean.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
FwdVar*, Design's forward branch).
A module body may now call a procedure declared later; a nested-local
forward is a plain 201. -notwophase restores the legacy inline
parse for debugging.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.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.
Lower (L0–L7) — Lower emits the whole session from the AST.Check deleted.T(x) (incl. CONST folding), the builtins
ODD/TRUNC/FLOAT, and BOOLEAN/ENUM array bounds + indices:
corpus 365 → 381.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 CONSTs,
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):
corpus 382 → 398. Fixed a SHIFT-by-a-negative-amount fault (wild
word load).M2 exit codes (0 ok / 1 errors / 2 usage-IO).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.showcase23 (generic doubly linked list).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 references to module-level variables and forward
procedure calls from a body (both are the two-phase parse deferring
procedure bodies — separate from the declaration part). The declaration
part itself is now strict: module-level forward type/VAR references,
forward types inside a TYPE block, and forward SET OF <type> are all
rejected (Track A, first cut, 2026-10-18). Corpus 398 → 388 (the drop
is the 10 gm2-leniency files now correctly rejected).
not supported yet,
undeclared identifier, invalid call); separate them from gm2
leniencies (docs/gaps/) and missing stdlib imports (LIB). See
docs/analysis_gaps.md.ASTDUMP from M2.atg breaks the fixpoint" has never been
root-caused. A real correctness landmine.docs/wip/).docs/summary_*.md (see
docs/INDEX.md); decide on merging/pushing resolve-check.cd compiler
./build.sh # M2.atg -> scanner/parser/driver -> ./M2
./run_tests.sh # 234/234
cd ..
./bootstrap/fixpoint.sh # FIXPOINT OK (byte-identical stage2/stage3)