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

Next session — plan

Branch state (2026-10-19): work is on master, tree clean.

Baseline: suite 246/246, fixpoint OK (3,869,013 bytes), corpus 421/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.

New remaining latent item: the record array field as an open-array actual bug (inline length word lost in static array-of-record data) — worked around by flattening; the general codegen fix and docs/wip/nested-array-field-bug.mod are still open.

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   # 246/246
cd .. && TMP="$PWD/.scratch/fixpoint" ./bootstrap/fixpoint.sh
tools/v3-corpus/corpus.sh                     # 421 OK, 0 crash

Recommendation: P1 (two bounded wins) next, then reserve a separate session for P4.