State: branch resolve-check, tree clean. Suite 239/239,
fixpoint OK (3,812,771 bytes), corpus 405/612, 0 crashes.
Tags include v3-enum-import, v3-ordl, v3-showcase24,
v3-convert, v3-set-word, v3-strict-decl-order, v3-m2-conformance,
v3-gap-examples.
Follow Modula-2, not gm2 degradation: declaration before use (only a
POINTER's base may forward-reference). docs/gaps/ classifies every
remaining case; decl-order and gm2-ext are not targets (gm2
accepts non-conforming code; V3 correctly rejects it). The corpus is
gm2's testsuite, so its number mixes both; the undeclared identifier
bucket (44) is now inflated by the decl-order files — don't chase it
blindly.
The corpus harness now resolves transitive imports (2026-10-18); that is the measurement method from here.
analysis_gaps.md §10: instantiating one generic twice with arguments
from different families fails on the second instantiation
(GStack(INTEGER) + GStack(CHAR) → incompatible assignment at
n^.val := v); INTEGER+CARDINAL passes only because the wrong
binding still type-checks. Each type argument alone works.
Likely culprit: SymTab.EnterTypeParamBound (SymTab.mod ~line 675):
IF Lookup(name) AND (SymKind(name) = KindType) THEN RETURN TRUE END;
During an instantiation re-parse the formal is already visible, so it returns without rebinding to the new actual — the first instantiation's binding sticks.
First steps: reproduce with a five-file session (GBox(T) generic
def/mod + two instantiations + a program); check how the driver
establishes the scope for each re-parse (InstBegin/Reenter, compiler
frame); make the formal binding per-instantiation (fresh alias), then
add a regression test (two instantiations, distinct scalar families) and
re-run fixpoint (generics are used in the compiler's own generics
tests, not its source).
gap22 __FILE__/__LINE__ (packed): gm2 predefined
constants; __/_ are not legal identifier chars in V3. Either add
the two pseudo-constants or leave as gm2-ext (+1 file).gap09 scalar actual into ARRAY OF BYTE (gm2's untyped byte
buffer; aochar, arrayofbyte, bytearray): accept a scalar/
ADDRESS actual for an ARRAY OF BYTE/VAR ARRAY OF BYTE formal by
passing its address (+ ~3 files).gap16/gap17 set span: raise the cap / handle SET OF CARDINAL
(low value).gap12 variant records: empty variant arms, ELSE, untagged
inner CASE (varient2/3/8, tsize/2).gap11 bare procedure value as a statement (tinyproc2/4).gap10 VAR actual type leniency (gm2 same-size rule;
testparam2/3) — decide whether to adopt it (non-standard).gap13 remainder: the standard built-ins (ORD, CHR, VAL,
MOD, …) are still Coco/R keyword tokens; de-reserving them needs
contextual-keyword parsing (predeclared identifiers). ORDL (the
corpus case) is done.gap18 set-typed CASE labels (bitset5).Forward references to module-level variables/procedures from a procedure body still work (the two-phase parse defers bodies). Strict declaration-before-use would require order-aware body checking; decide whether to pursue (high risk, touches self-hosting). The declaration part itself is already strict.
The size-sensitive V3 miscompile (historical "removing ASTDUMP
breaks the fixpoint") is still unroot-caused. Worth a dedicated
bisection session: it is a correctness landmine, unlike the remaining
language gaps.
docs/summary_*.md (see docs/INDEX.md).resolve-check (no pushes so far)../build.sh, ./run_tests.sh (must stay 100%), bootstrap/fixpoint.sh
(byte-identical), corpusx.sh (record OK/CRASH + note), update
docs/gaps/ + STATUS/README, commit with a v3-* tag.
cd .../m2compiler-V3
git checkout resolve-check
cd compiler && ./build.sh && ./run_tests.sh # 239/239
cd .. && TMP="$PWD/.scratch/fixpoint" ./bootstrap/fixpoint.sh
M2FLAGS="" ./.scratch/corpusx.sh # 405 OK, 0 crash