handoff-2026-10-19-next.md 8.1 KB

Next session — plan

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).


P0 — generics: two instantiations of one generic ✅ DONE

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).
  • Regression: 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.

P1 — quick corpus wins ✅ DONE

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).

P2 — structural gm2 extensions ✅ DONE (2026-10-19)

  • 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).

P3 — strict declaration order in bodies ✅ DONE (2026-10-19)

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.

P4 — latent correctness ✅ DONE (2026-10-19)

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.

Next: robustness ✅ first pass (2026-10-19)

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/).

Library holes ✅ first slice (2026-10-19)

  • stdlib/chanconsts + stdlib/streamfile (ISO 10514-1), layered on IOChan. Regression t_streamfile (open/write/read/close + flag handling, exit 42).
  • This surfaced and fixed a real compiler bug: a set CONST exported from a definition module was pooled as $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).
  • Remaining library work: the rest of the device/IO framework (docs/wip/).

Library holes ✅ second slice (2026-10-19)

  • Harness fidelity: 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).
  • PIM/gm2 I/O family: added NumberIO, RealInOut, FpuIO (StrIO/InOut already shipped; a new m2writefix C formatter backs the fixed-point writers). Regression t_pimio (exit 42). 447 → 467.
  • Still missing: gm2 runtime modules (M2RTS, Builtins, Processes, Preemptive, M2Printf, …).

Baseline: suite 249/249, fixpoint OK (3,882,337 bytes), corpus 467/612, 0 crashes.

P5 — housekeeping ✅ DONE (2026-10-19)

  • docs/INDEX.md rewritten as the entry point (start-here, load-bearing list, this session).
  • 45 unreferenced historical files moved to docs/archive/ (docs/archive/README.md); referenced ones stay in docs/.
  • (resolve-check → master: done.)

Corpus-feature follow-ups

  • 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.

Gates for every step

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.

Resume

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.