summary_v3-corpus.md 7.6 KB

Step: V3 compiler corpus runs

Tag v3-corpus-run. V3 suite 169/169, fixpoint OK.

Why

The TopSpeed corpus validated the sidecar grammar, not V3 the compiler (the sidecar is a separate TSM2 binary built from TopSpeed-V3-M2.atg; it reuses V3's SymTab/QbeGen/FileIO sources but never runs compiler/M2). So this work runs V3 itself over two real Modula-2 corpora and fixes what it finds.

Harness: tools/v3-corpus/corpus.sh — compiles each file with compiler/M2 in a scratch tree, together with its sibling DEFINITION and the DEFINITION of every module it imports ($V3_LIBS search path), and splits failures into CRASH / LIB (unresolved import) / LANG (real gap).

Corpora and results

1. gm2 testsuite (gm2/pim/pass + gm2/iso/run/pass, 612 files):

no libs with $V3_LIBS
compile-OK 242 392
CRASH 0 0
LIB 185 9
LANG 185 212

2. gm2 standard libraries (m2pim/m2iso/m2log/m2cor/m2min, 336 files):

no libs with $V3_LIBS
compile-OK 78 127
LIB 176 0
LANG 82 209

(With the import path set, LIB noise is gone and the true LANG counts are visible.)

Fixes landed

  1. Long-line buffer overflow (v3-fix-longline-crash) — GetLine wrote past a 256-byte listing buffer; a long line (an 860-char enum) overflowed the stack. Fixed (GetLine truncates, buffer 4096, bounded caret loop); CRASH → 0.
  2. EXPORT QUALIFIED in DEFINITION modules (v3-export-qualified).
  3. SYSTEM facilities — predefine BYTE/SBYTE/WORD/SWORD/ LONGWORD/SHORTADDR/LONGADDR/PROC/LOC (mapped onto V3's integer family / ADDRESS) so FROM SYSTEM IMPORT BYTE; and ARRAY OF BYTE resolve.
  4. Anchored subranges T[lo..hi] (INTEGER [-1 .. 24]).
  5. Unnamed procedure-type parameters (PROCEDURE (VAR ARRAY OF REAL)) plus open arrays in procedure types and structural proc-type formal compatibility (ParamTypesOk), so assigning a procedure with an open-array parameter to a procedure variable works.
  6. Harness import resolution — corpus.sh now finds and passes the .def of each imported module ($V3_LIBS), removing the LIB noise.
  7. Array constructors T{…} (GNU Modula-2). A CONST whose value is a constructor (my = arr {'h','e','l','l','o'}, arr {1,2,3,4 BY 3}, nested names { "Thu", "Fri" }) now lowers to a pooled static descriptor (data $ctoN = { l len, … }) with the same layout as a declared array, and the constant resolves to its address; whole-array copy, indexing and LEN then work unchanged. Elements: scalars (b/w/d/l by class), nested constructors, string literals for nested ARRAY OF CHAR, and the expr BY n repeat form. New QbeGen.Ctor{Begin,Elem,End}; the old TypedSetLit became the unified TypedBraceLit (BITSET{…} still a set).
  8. Record constructors and runtime construction — RecName{…} fills a record's fields in declaration order (static when every element is constant, so it works in CONST; a nested constructor is inlined, and a string becomes a matching CHAR-array field). When any element is a runtime value the descriptor is built on the stack (alloc8) and filled with stores/copies (ElemStore, CopyArray, CopyRecord, CopySet); this also covers non-CONST array constructors (a := arr{i, i+1, i+2}). strcons.mod now compiles.
  9. Set fields in record constructors — R{7, S{1,3,5}} (both runtime and via a typed set constructor); the nested braceCls dispatch flag is saved/restored around a nested set constructor.
  10. Constant string concatenation in constructor elements — arraytype {Hello + " " + World}: CONST string values are supported, + on constant strings folds to one literal (QbeGen.StrFold), and a string element expands into consecutive CHAR elements. constarray2.mod compiles.
  11. Index-type arrays ARRAY <ordinalType> OF T (e.g. ARRAY Colour OF T, ARRAY Sq OF T) — the index type's ordinal bounds give [lo..hi] (array2/array3 now compile). Enum values are accepted as array indices.
  12. MIN/MAX builtins over any ordinal type, array, or set (MIN(Colour), MAX(large), ARRAY [MIN(c)..MAX(c)] OF T); SymTab.TypeBounds + enum literal counts.
  13. FROM libc intrinsic — printf/exit/… import as permissive external C procedures (varargs, no result) and bind to the C symbols; string/CHAR-array actuals pass as descriptor+8.
  14. Sets over enums and CHAR ranges — SET OF <enum>, SET OF ['A'..'z'], enum/BOOLEAN subranges; span limit 256 → 65536.
  15. MIN/MAX for REAL/LONGINT and gm2 sized names (LONGCARD, SHORTREAL, INTEGER8/16/32/64, CARDINAL8/16/32/64).
  16. Set builtins INCL/EXCL (statements) and SHIFT/ROTATE (expressions, bit-by-bit over a runtime amount).
  17. Module-level forward type references — a global may name a type declared further down; emission is deferred until the TYPE block completes (TYPE-block forwards already worked).
  18. String and opaque/forward types — an unresolved (opaque or forward) alias is pointer-like (assign NIL, copy, compare, pass), which is what gm2's opaque String needs. String is not built-in: it is imported from a definition module (DynamicStrings/opaque), as in gm2. An undeclared type name at module level is reported (201, undeclared identifier) once the module's TYPE declarations are known (FwdPending/FwdInfo).
  19. Variant-record assignment — whole-record assignment is now a flat byte blit of the (max) record size, correct for overlapping variant fields; variant records are emitted as a zeroed blob. (Previously it compiled but crashed / corrupted data.)
  20. Nested local MODULE — exported names, nested-nested modules and bodies are lowered as startup init functions (was already largely working; verified and documented).
  21. ADDRESS ↔ pointer assignment (p := ADR(x)), checked before the pointer-subclass test.

Remaining LANG gaps

  • undeclared identifier — SYSTEM facilities beyond the set above, and small library holes.
  • gm2-specific syntax — PROCEDURE __BUILTIN__ name(…), C-name pragmas, ISO module attributes (not V3's dialect; skip).
  • not supported yet — deliberate 230s.

Reproduce

cd tools/v3-corpus
./corpus.sh                       # testsuite, no libs
G=$HOME/bin/Modula2/Gm2/lib/gcc/x86_64-pc-linux-gnu/16.0.1/m2
V3_LIBS="$G/m2pim:$G/m2iso:$G/m2log:$G/m2cor:$G/m2min" ./corpus.sh
V3_LIBS="$G/m2pim:$G/m2iso:$G/m2log:$G/m2cor:$G/m2min" \
  ./corpus.sh "$G/m2pim" "$G/m2iso" "$G/m2log" "$G/m2cor" "$G/m2min"

Suggested next steps

  1. More SYSTEM facilities / library holes.
  2. Whole-set LargeInt-style operations; gm2-only pragmas (skip).

Files

tools/v3-corpus/{corpus.sh,README.md}, compiler/src/compiler.frm (long-line), compiler/src/M2.atg (EXPORT QUALIFIED, anchored subranges, unnamed proc-type params, TypedBraceLit array/record constructors, index-type arrays, MIN/MAX, enum indices), compiler/src/SymTab.def/.mod (SYSTEM predefs, ParamTypesOk, TypeBounds, enum counts), compiler/src/QbeGen.def/.mod (CtorBegin/CtorElem/CtorEnd, StrFold, FlushCtors, static + runtime descriptor building), compiler/tests/t_ctor{1..7}.mod, compiler/tests/t_minmax.mod, compiler/tests/t_longline.mod, compiler/run_tests.sh, this doc.