# 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 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 `, `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 `230`s. ## Reproduce ```sh 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.