Eric Streit f236600299 language: ISO procedure-type syntax (unnamed comma params, PROC result) 1 dienu atpakaļ
..
README.md f236600299 language: ISO procedure-type syntax (unnamed comma params, PROC result) 1 dienu atpakaļ
gap01_fwd_type_const_conv.mod b5c959c4f4 docs+revert: follow Modula-2, not gm2 leniency (docs/gaps) 5 dienas atpakaļ
gap02_fwd_type_var.mod 5fb4950e65 language: strict declaration order (no forward type references) 5 dienas atpakaļ
gap03_fwd_type_heading.mod b5c959c4f4 docs+revert: follow Modula-2, not gm2 leniency (docs/gaps) 5 dienas atpakaļ
gap04_fwd_const_bound.mod b5c959c4f4 docs+revert: follow Modula-2, not gm2 leniency (docs/gaps) 5 dienas atpakaļ
gap05_fwd_type_size.mod b5c959c4f4 docs+revert: follow Modula-2, not gm2 leniency (docs/gaps) 5 dienas atpakaļ
gap06_fwd_setconst.mod b5c959c4f4 docs+revert: follow Modula-2, not gm2 leniency (docs/gaps) 5 dienas atpakaļ
gap09_scalar_to_bytearray.mod b5c959c4f4 docs+revert: follow Modula-2, not gm2 leniency (docs/gaps) 5 dienas atpakaļ
gap10_var_param_type.mod b5c959c4f4 docs+revert: follow Modula-2, not gm2 leniency (docs/gaps) 5 dienas atpakaļ
gap11_bare_proc_stmt.mod b5c959c4f4 docs+revert: follow Modula-2, not gm2 leniency (docs/gaps) 5 dienas atpakaļ
gap12_variant_empty_arms.mod b5c959c4f4 docs+revert: follow Modula-2, not gm2 leniency (docs/gaps) 5 dienas atpakaļ
gap14_builtin_pragma.def b5c959c4f4 docs+revert: follow Modula-2, not gm2 leniency (docs/gaps) 5 dienas atpakaļ
gap16_wide_set.mod b5c959c4f4 docs+revert: follow Modula-2, not gm2 leniency (docs/gaps) 5 dienas atpakaļ
gap17_set_of_cardinal.mod b5c959c4f4 docs+revert: follow Modula-2, not gm2 leniency (docs/gaps) 5 dienas atpakaļ
gap18_set_case_labels.mod b5c959c4f4 docs+revert: follow Modula-2, not gm2 leniency (docs/gaps) 5 dienas atpakaļ
gap19_fwd_array_index.mod d91871a0d5 docs(gaps): add gap19 (forward array index) after the revert 5 dienas atpakaļ
gap20_fwd_set_of_type.mod 5fb4950e65 language: strict declaration order (no forward type references) 5 dienas atpakaļ
gap21_fwd_record_field.mod 5fb4950e65 language: strict declaration order (no forward type references) 5 dienas atpakaļ
gap22_file_line.mod 481b2c306d language: set<->word VAL/CAST (BITSET as a machine word) 5 dienas atpakaļ

README.md

Unsupported / non-standard cases

One minimal source per known V3 gap (2026-10-18). Each file's header comment carries its tag, its Modula-2 status, and the gm2 corpus file(s) it stands for. These are not part of the test suite (they are expected to fail); they exist so each case has a runnable example.

Verify one with:

cd compiler && ./build.sh
./M2 ../docs/gaps/gap07_convert.mod     # prints the diagnostic on stdout

Policy: follow Modula-2, not degraded gm2

The corpus is gcc's gm2 testsuite, so "compiles on the corpus" mixes conforming Modula-2 with gm2 leniencies and extensions. This project follows the language:

Standard Modula-2 (PIM and ISO) requires declaration before use; the declaration part is ordered CONST, TYPE, VAR. The only forward reference the language allows is a POINTER's base type (for recursive/self-referential types).

V3 therefore intentionally rejects use-before-declaration; that is conforming behaviour, not a gap. gm2 accepts such programs because it resolves the whole declaration part before checking — a leniency.

Since 2026-10-19 the rule is also enforced inside deferred procedure bodies for module-level data (variables, constants, types): a body may not use one declared after its procedure. Forward procedure calls from a body remain allowed (the generated parser relies on it).

The class column says what a change would mean:

  • decl-order — not legal Modula-2; gm2 leniency. V3 rejects it correctly. Not a target.
  • gm2-ext — not standard; a gm2/PIM extension or leniency. Not a target unless we choose gm2 compatibility.
  • iso/system — a standard ISO Modula-2 / SYSTEM facility V3 lacks. A genuine gap.
  • v3-deviation — V3 is stricter than the standard (a V3 bug to fix).
  • impl-limit — the language allows it; V3 has a fixed implementation limit.

Index

tag file class diagnostic corpus exemplars
gap01-fwd-type-const-conv gap01_fwd_type_const_conv.mod decl-order undeclared identifier subrange4, subrange7
gap02-fwd-type-var gap02_fwd_type_var.mod decl-order undeclared identifier subrange11, arrayptr, arrayptr6, constructor1, constructor3, param3
gap03-fwd-type-heading gap03_fwd_type_heading.mod decl-order undeclared identifier shift3, shift4
gap04-fwd-const-bound gap04_fwd_const_bound.mod decl-order undeclared identifier setconst3
gap05-fwd-type-size gap05_fwd_type_size.mod decl-order undeclared identifier constsize2
gap06-fwd-setconst gap06_fwd_setconst.mod decl-order undeclared identifier setoverflow
gap09-scalar-to-bytearray gap09_scalar_to_bytearray.mod gm2-ext (accepted) — aochar, arrayofbyte, bytearray
gap10-var-param-type gap10_var_param_type.mod gm2-ext (accepted) — testparam2
gap11-bare-proc-stmt gap11_bare_proc_stmt.mod gm2-ext (accepted) — tinyproc2, tinyproc4
gap12-variant-empty-arms gap12_variant_empty_arms.mod gm2-ext (accepted) — varient2, varient3, varient8, tsize, tsize2
gap14-builtin-pragma gap14_builtin_pragma.def gm2-ext (accepted) — builtin.def, builtin.mod
gap16-wide-set gap16_wide_set.mod impl-limit not supported yet settest1
gap17-set-of-cardinal gap17_set_of_cardinal.mod impl-limit not supported yet settest3
gap18-set-case-labels gap18_set_case_labels.mod gm2-ext (accepted) — bitset5
gap19-fwd-array-index gap19_fwd_array_index.mod decl-order not supported yet record7, complextypes
gap20-fwd-set-of-type gap20_fwd_set_of_type.mod decl-order undeclared identifier tinyset6, tinyset7
gap21-fwd-record-field gap21_fwd_record_field.mod decl-order undeclared identifier record14.def, record15.def, record16.def
gap22-file-line gap22_file_line.mod gm2-ext (accepted) — packed

First diagnostic (message) per file at commit time (line:col varies as comments change):

gap01_fwd_type_const_conv.mod   undeclared identifier   (ind0)
gap02_fwd_type_var.mod          undeclared identifier   (arr)
gap03_fwd_type_heading.mod      undeclared identifier   (large)
gap04_fwd_const_bound.mod       undeclared identifier   (n)
gap05_fwd_type_size.mod         undeclared identifier   (t)
gap06_fwd_setconst.mod          undeclared identifier   (CharSet / tab)
gap09_scalar_to_bytearray.mod   invalid call            (scalar -> ARRAY OF BYTE)
gap10_var_param_type.mod        invalid call            (VAR actual type)
gap11_bare_proc_stmt.mod        invalid call            (bare proc value)
gap12_variant_empty_arms.mod    invalid VarLabel
gap14_builtin_pragma.def        ident expected          (__BUILTIN__)
gap16_wide_set.mod              not supported yet       (span > 65536)
gap17_set_of_cardinal.mod       not supported yet       (unbounded base)
gap18_set_case_labels.mod       not supported yet       (set as CASE selector)
gap19_fwd_array_index.mod       not supported yet       (array index declared later)
gap20_fwd_set_of_type.mod       undeclared identifier   (SET OF base declared later)
gap21_fwd_record_field.mod      undeclared identifier   (record field type declared later)
gap22_file_line.mod             invalid Fact            (__LINE__)

Genuine gaps (worth fixing, standard Modula-2)

  • libc prototypes — fixed (2026-10-22): the known C functions now have real signatures (exit, write/read/open/close/ lseek, malloc, memcpy/memset/strncpy, system/getenv, atof/strtod). printf/snprintf are varargs functions: the format is checked as ARRAY OF CHAR, extra arguments are untyped, and the result may be used or discarded in statement position. That last point is the single accepted "function call as statement", limited to foreign C varargs; an ordinary Modula-2 function used as a statement is still 233 (t_bad_funstmt). Corpus +6: procedure2, tinyparam, tinyparam2, sizes, variant9, varient (except5 gets past printf and stops on ISO exceptions). Regression t_libc2.
  • ISO SYSTEM sized types + qualified SYSTEM — fixed (2026-10-22): WORD8/16/32/64, BITSET8/16/32, REAL32/64/128, COMPLEX32/64/128, CSIZE_T/CSSIZE_T/COFF_T and the BITSPERLOC/LOCSPERWORD/LOCSPERBYTE constants are predefined. The hard-coded SYSTEM module is now registered with its own scope, so SYSTEM.CARDINAL8/SYSTEM.BITSET32 resolve (the qualified type path used QualType, which needs a module). FROM SYSTEM IMPORT stays permissive (its names are global predefs/keywords). Corpus +8: compsize, int8field, returnrecord, printsize, testgeneric, testgeneric2/3/4. Regression t_system. V3's word model is 32-bit, so the narrow names share INTEGER/CARDINAL and COMPLEX* is a REAL placeholder.
  • ISO procedure-type syntax — fixed (2026-10-22): comma-separated unnamed parameter types with a keyword or VAR start (PROCEDURE(ARRAY OF CHAR, ARRAY OF CHAR), PROCEDURE (CHAR, VAR scanClass, VAR scanState)), and PROC as a result type. Named groups (a, b : T) and ; sections still work. Corpus +4: typeonly.def, testrecursive.def, procconv, procconv2. Regression t_proctype2.

  • Multi-index arrays — fixed (2026-10-22): the PIM/ISO form is ARRAY [lo..hi], [lo..hi] OF T (one bracket group per dimension). V3 only accepted a non-standard single-bracket comma list; it now takes both, nesting the groups inside-out. Corpus +6: arraybool, assignment2, foo, recordarray2, tinyarray4, tinyarray5.

  • Subranges keep their ordinal base — fixed (2026-10-22): ['A'..'Z'] was classified as an integer subrange, so assigning a CHAR (or an enumeration literal to [red..blue]) was incompatible assignment/comparison. Subranges now carry their base type (SymTab.NewSubB), and CHAR/BOOLEAN/enum values assign to and compare with a subrange of the same base. Corpus +3: subrange16, subrange17, enum3. Not covered yet: a runtime range check on assignment (V3 remains lenient, as for the integer family).

  • FOR over any ordinal — fixed (2026-10-22): the control variable and bounds may be CHAR, BOOLEAN, an enumeration or a subrange (bounds must be assignment-compatible with the variable), not only the integer family. MIN/MAX of a SET now has the set's base type, so FOR i := MIN(S) TO MAX(S) works. Corpus +3: forloop2, smallset4, minmax.

  • ISO empty string literal — fixed (2026-10-22): "" / '' is a string literal of length 0 and is compatible with CHAR, where its value is the terminator (ISO 10514-1, "Strings" and "Assignment compatibility"). V3 typed it as a plain string, so ch := "", ch = "", CASE ch OF "", FOR ch := "" TO …, an actual for a CHAR formal, and RETURN "" were rejected or wrong. It stays a string in ARRAY OF CHAR contexts (whole-array assignment and content comparison), so both sides keep working. Corpus +6: arraychar, arraychar2, arraychar3, nulcharcase, nulcharvar, forloopnulchar. Regression t_emptychar.

  • gap07 CONVERT — fixed (2026-10-18): CONVERT(T, x) is accepted as a VAL sibling (procconv2 passes).

  • gap08 set↔word CAST/VAL — fixed (2026-10-18): VAL/CAST between a set and its machine word (constset, constset2, constset3 pass). packed still needs gap22 (__FILE__/__LINE__).

  • gap13 builtins as identifiers — corpus case fixed; remainder deferred (2026-10-18 / confirmed 2026-10-19): ORDL (a gm2/FileIO helper, not a Modula-2 built-in) is no longer reserved, so PROCEDURE ORDL is accepted (fileio.def/mod, baseimport pass; the scanner frame imports FileIO.ORDL). The standard built-ins (ORD, CHR, VAL, SIZE, ABS, … ~24 words) are still reserved keywords. De-reserving them is a contextual-keyword rewrite of Fact/ArgList/the statement builtins (Coco/R is LL(1), so it cannot branch on an identifier's text; the dedicated builtin alternatives would have to merge into the general Design+ArgList path). There is no corpus exemplar in the scanned dirs (no file declares a builtin as a name), so the payoff is conformance-only; a declaration- site-only half-measure would bind calls to the builtin (silently wrong), so it is deliberately not shipped. Tracked as a known v3-deviation.

  • gap15 enumeration literals with an imported type — fixed (2026-10-18): literals now travel with the type through re-export, FROM-import and qualified access; also fixed the qualified-const value (LoadDesignator returned 0 for enum consts). imp* pass.

  • Foreign modules FOR "C" / EXPORT UNQUALIFIED — already fixed (§ STATUS).

gap16/gap17 are implementation limits (raising the set span cap).

gm2 extensions now accepted (deliberate compatibility)

These are not standard Modula-2; V3 accepts them for gm2 compatibility (they do not relax any conforming rule).

  • gap22 __FILE__ / __LINE__ — supported (2026-10-19). The scanner now allows a leading _ in identifiers, the two names are reserved literals, and the parser folds them to the current file-name string and line-number integer (scanner.frm records the file with SetFileName). Corpus: packed (+2 more __LINE__ users) now pass.
  • gap09 scalar actual → ARRAY OF BYTE — accepted (2026-10-19). SYSTEM.BYTE is V3's 32-bit CARDINAL, so ARRAY OF BYTE cannot be byte-granular; the ARRAY OF BYTE open formal is marked at parse time and a scalar/pointer/string actual is accepted. For a scalar the caller builds a stack descriptor [count = SIZE(actual)][copy of the value], so HIGH matches gm2's SIZE-1, but writes reach the copy, not the actual, and a string actual is passed as-is (its HIGH is the NUL index, so one less than gm2's). A genuine 8-bit BYTE (and byte-exact buffers) remains out of scope. Corpus: aochar, arrayofbyte, bytearray (×2) now compile.
  • gap10 scalar VAR actual, same size — accepted (2026-10-19). VarParamOk accepts a scalar VAR actual of a different type when ObjectSize matches (e.g. INTEGER for VAR WORD). testparam3 (SHORTREAL for VAR WORD) still fails: V3 models every real as 8 bytes, so there is no 4-byte SHORTREAL.
  • gap11 bare procedure value as a statement — accepted (2026-10-19). The PIM PROC type keyword is now recognised, and a proc-typed designator with no parameter list is an implicit zero-argument call (named or indirect).
  • gap12 variant records — extended (2026-10-19). Empty arms, ELSE arms, comma-separated labels, tolerated extra | separators, untagged inner CASE : T OF, and nested variant CASEs inside an arm. (An untagged variant falls back to sequential layout, since there is no tag slot to overlay from.)
  • gap18 set-typed CASE labels — accepted (2026-10-19). A set selector takes set labels; the compare-chain uses word-wise set equality (QbeGen.CmpSet).
  • gap14 __BUILTIN__ / __ATTRIBUTE__ pragmas — accepted (2026-10-19). An optional declaration pragma after PROCEDURE (__BUILTIN__, or __ATTRIBUTE__ __BUILTIN__ (( name ))) is parsed and ignored; the procedure is compiled normally. Corpus: builtin.def, builtin.mod.

The gap13 remainder (standard builtins as identifiers) and gap14 (__BUILTIN__) remain; gap19/gap21 are decl-order.

Not represented by a source

  • Record array field passed as an open array — fixed (hybrid QbeGen.ArrBodyItems): small record arrays are expanded per element so each inline array field's length word is in the static data; large ones get a generated runtime init loop. Covered by t_arrrecfld, t_arrrecbig, t_nestarr; see docs/summary_l3_selfhost_fix.md.

  • Size-sensitive V3 miscompile — root-caused 2026-10-19: not size-sensitive, and not a codegen bug at all. The ASTDUMP debug dump printed the literal "Parsed correctly", which satisfied the fixpoint's grep -q Parsed gate while the compiler session was reporting Incorrect source; removing the dump simply unmasked the real errors (Lower's forward procedure calls). Fixed in a3ef897.