Branch state (2026-10-19): work is on master, tree clean.
Baseline: suite 249/249, fixpoint OK (3,882,337 bytes), corpus 467/612, 0 crashes.
Policy: follow Modula-2, not gm2 degradation — declaration
before use (only a POINTER's base may forward-reference). In
docs/gaps/, the decl-order and gm2-ext classes are not targets
except where gm2 compatibility was explicitly chosen (gap22, gap09).
Fixed on 2026-10-19. The bug was not EnterTypeParamBound (each
instance re-binds T correctly). It was in Lowering: after
EndUnit, Lower re-emitted each unit in globScope, so
Lower.MaterializeGlobals copied the unit's types into globScope; a
generic instance leaked its NodeRec there, and the second instance's
POINTER TO NodeRec picked it up via ForwardType (a forward pointer
base made it visible; direct records did not). See
docs/analysis_gaps.md §10 for the full trail.
SymTab.EnterLowerScope/LeaveLowerScope + Lower re-emits each
unit in its own module scope (also around the ExitCode probe in
Lower.EndSession).Lower.ReenterImports re-materialises a nested module's FROM-imports
in the enclosing scope (the nested scope is popped before lowering).compiler/tests/g_twice{,i,c,p} (GTwiceProg, 42).Follow-up: showcase24 can now use two instantiations of one generic
(simplify it, or add a demo) — optional.
Chosen with the user despite the gm2-ext class (compatibility mode).
gap22 __FILE__/__LINE__ — done (v3-file-line): scanner
allows a leading _, both names are reserved literals folded to the
file-name string / line-number integer. +3 corpus.gap09 scalar → ARRAY OF BYTE — done (v3-bytebuf): a
parse-time marker on ARRAY OF BYTE open formals, accepted for
scalar/pointer/string actuals; a scalar is wrapped in a stack
descriptor [count=SIZE(actual)][copy]. +3 corpus. See the
caveats in docs/gaps/README.md (writes reach the copy; string
HIGH is one less than gm2; BYTE is still 32-bit).gap10 same-size scalar VAR actual (VarParamOk). testparam3
still fails (needs a 4-byte SHORTREAL; V3 reals are all 8 bytes).gap11 bare proc-value statement + PIM PROC type keyword.gap12 variant empty arms, ELSE, comma labels, untagged inner
CASE, nested variant CASE.gap18 set-typed CASE labels (word-wise CmpSet).Regression tests: t_varsame, t_bareproc, t_variantext,
t_setcase. Corpus +16.
Still open: gap13 remainder (de-reserve the standard builtins —
contextual-keyword parser refactor; no corpus exemplar); gap14
(__BUILTIN__) — DONE (see the follow-ups below).
Deferred bodies are now order-aware: SymTab assigns each module-level
symbol a declaration sequence; DeferBody records the enclosing
procedure's sequence and ReenterDeferred arms a window during which
Lookup reports a module-level variable, constant or type declared
after the procedure as undeclared. Parser state (sym/errDist) moved
into the grammar's section so the parser itself obeys the rule.
Scope note: forward procedure calls from a body stay allowed — the
generated parser's mutually recursive productions rely on it, and
forbidding it would need FORWARDs that the gm2 bootstrap link rejects.
FORWARD works for the conforming case (t_fwdok).
Tests: t_fwdvar/t_fwdsel/t_fwdprop now expected to fail
(undeclared); t_fwdproc/t_fwdok run. Corpus 427 → 421 — 6
gm2-leniency files (block, blockindirect, nestedfor, …) are now
correctly rejected. The drop is intended conformance, not a regression.
Root-caused: not size-sensitive, not a codegen bug. ASTDUMP
printed the literal "Parsed correctly", and the fixpoint's M2make gate
was grep -q Parsed — so the gate passed while the compiler session was
reporting Incorrect source. Removing the dump unmasked real errors
(Lower's forward procedure calls). Already fixed in a3ef897; verified
the gate now fails on errors. See docs/summary_l3_selfhost_fix.md.
Latent item — fixed: the record array field as an open-array
actual bug (inline length word lost in static array-of-record data) was
resolved by the hybrid QbeGen.ArrBodyItems/InitRecArray fix; covered
by t_arrrecfld/t_arrrecbig/t_nestarr (all green). No open codegen
case remains.
Table-limit overflows are now loud (SemError(230)) instead of silent:
the two-phase span table (silently dropped a body's codegen — regression
t_manyproc), the label/GOTO tables, the 32-actual AST buffer, and
SymTab.MaxTypes (TypesOverflow). Remaining: error recovery and
message polish, and the library holes (docs/wip/).
stdlib/chanconsts + stdlib/streamfile (ISO 10514-1), layered on
IOChan. Regression t_streamfile (open/write/read/close + flag
handling, exit 42).$setN while clients
referenced <unit>_<name> → undefined symbol. Module-level set
consts are now emitted under their global name (QbeGen.PoolSetNamed;
Lower uses SymTab.GlobalRef), verified cross-unit by t_streamfile
(StreamFile.write) and a ChanConsts link test. Also fixed
Lower.EvalOrdConst to fold a qualified enum literal (M.lit) in
set constants (a different blocker for the same file).docs/wip/).tools/v3-corpus/corpus.sh now defaults
V3_LIBS to V3's own shipped libraries (stdlib, runtime/syslib)
and only treats SYSTEM/libc as built-ins-without-.def. A corpus
file importing Strings/IOChan/StrIO/… now resolves against the
real .def instead of being mislabelled LIB (427 → 447).NumberIO, RealInOut, FpuIO
(StrIO/InOut already shipped; a new m2writefix C formatter backs
the fixed-point writers). Regression t_pimio (exit 42). 447 → 467.M2RTS, Builtins, Processes,
Preemptive, M2Printf, …).Baseline: suite 249/249, fixpoint OK (3,882,337 bytes), corpus 467/612, 0 crashes.
docs/INDEX.md rewritten as the entry point (start-here, load-bearing
list, this session).docs/archive/
(docs/archive/README.md); referenced ones stay in docs/.resolve-check → master: done.)gap14 __BUILTIN__ / __ATTRIBUTE__ pragmas — DONE (2026-10-19):
an optional declaration pragma after PROCEDURE is parsed and ignored
(builtin.def, builtin.mod now compile).gap13 remainder — documented, deferred: the corpus case (ORDL)
is already fixed; the standard built-ins (~24 reserved words) as
identifiers need a contextual-keyword rewrite of
Fact/ArgList/statements (Coco/R is LL(1)) with no corpus
exemplar; a declaration-site-only half-measure would bind calls to
the built-in. See docs/gaps/README.md.compiler/build.sh → compiler/run_tests.sh (must stay 100%) →
bootstrap/fixpoint.sh (byte-identical) → tools/v3-corpus/corpus.sh
(record OK/CRASH + note) → update docs/gaps/ + STATUS/README →
commit with a v3-* tag.
cd .../m2compiler-V3
git checkout master
cd compiler && ./build.sh && ./run_tests.sh # 249/249
cd .. && TMP="$PWD/.scratch/fixpoint" ./bootstrap/fixpoint.sh
tools/v3-corpus/corpus.sh # 467 OK, 0 crash
Recommendation: P1 (two bounded wins) next, then reserve a separate session for P4.