analysis_gaps.md 6.0 KB

m2compiler-V3 — gap analysis (2026-09-30)

Suite 169/169, self-hosting fixpoint OK (2,754,353 bytes). Corpus at this point (with $V3_LIBS):

corpus compile-OK CRASH LANG
gm2 testsuite (612) 392 0 212
gm2 stdlib (336) 127 0 209

Failure taxonomy (testsuite, per file): undeclared identifier 65, not supported yet 43, invalid call 25, incompatible assignment 25, ident expected 8, invalid ImpName 7.

6. Fixed today (items 1–5)

  1. 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. This removed the dominant undeclared bucket (136 → 65 files).
  2. SET OF enum and SET OF <char-range> (plus enum subranges [a..d], ARRAY [FALSE..TRUE]), and the set span limit lifted 256 → 65536.
  3. MIN/MAX for REAL/LONGREAL/LONGINT and gm2 sized names (LONGCARD, SHORTREAL, INTEGER8/16/32/64, CARDINAL8/…).
  4. SYSTEM/PIM set builtins — INCL/EXCL (statements) and SHIFT/ROTATE (expressions), bit-by-bit over a runtime amount.
  5. Module-level forward type references — a global may name a type declared further down; its emission is deferred until the TYPE block completes (TYPE-block forwards already worked).

1. Language features genuinely missing

Ranked by corpus impact (each verified by a failing file or a probe):

  1. Sets over enumerations and CHAR ranges — SET OF <enum> and SET OF ['A'..'z'] are 230. Only bool/char/integer subrange bases are accepted. (goodifin.mod, charset2.mod, enum3.mod.)
  2. MIN/MAX for REAL/LONGREAL/LONGINT — only ordinal types, arrays and sets are handled; MAX(REAL) / MIN(LONGINT) are 230. (maxreal.mod, minmaxreal2.mod, longmm.mod, constcast.mod.)
  3. gm2 sized types — LONGCARD, SHORTREAL, INTEGER8/16/32/64, CARDINAL8/16/32/64, LONGWORD undeclared.
  4. SYSTEM builtins — SHIFT, INCL, EXCL, ROTATE, LOC, sized-bit names (INCL/EXCL/SHIFT recur).
  5. Enum subranges / BOOLEAN bounds — [a..d], ARRAY [FALSE..TRUE] … are 230.
  6. String — not a gm2 built-in: it is an opaque type imported from a definition module (DynamicStrings, or a local opaque). V3 now treats unresolved (opaque/forward) aliases as pointer-like, which is the real fix.
  7. GOTO + labels — parse error.
  8. Whole-variant-record assignment/equality — deliberately 236.
  9. Nested local MODULE — parsed, not lowered.
  10. Result suffixes F()^.
  11. Generics — not started (docs/analysis_generics.md).
  12. Coroutines / processes (NEWPROCESS/TRANSFER/IOTRANSFER).
  13. Multiple inheritance — deliberately out of scope (docs/OOP.txt).
  14. Recursive DISPOSE (shallow today) and runtime/syslib/Trap (traps are $abort, rc 134).

Procedure types and indirect calls do work (t_proctype passes); docs/features.md still lists them as ⏸ — stale.

2. Library

  • gm2-only modules V3 lacks: String, StrIO, RealStr/WholeStr, LongIO/LongMath/LongStr, the InOut/StdIO/STextIO/ SWholeIO/SIOResult family, IOConsts, Termination, StdChans/StreamFile/FIO, SysStorage, wrapclock/time, ChanConsts/GenDevIF/DeviceTablePtr (the gm2 device/IO framework).
  • FROM libc IMPORT printf, exit — no intrinsic libc module; this is the largest undeclared identifier bucket (163 + 141 occurrences) and is not a real language gap.

3. Architecture / robustness

  • No AST, single pass. Declaration-before-use is enforced, so forward references in CONST/expressions fail (e.g. arraycons3.mod declares TYPE after the CONST constructor and is rejected). Only FORWARD procedures and ForwardType are tolerated.
  • Single image only — no per-module object emission, no import cycles.
  • Driver robustness — M2 exits 0 even with errors (harnesses parse the .LST verdict) and aborts on a missing input file.
  • Fixed-size tables — 64 formals/locals (233 past that), 64 call args, 256 VAR-actual note entries, 4096 string literals, 256 pooled constructors, 8 constructor levels, 4 MiB image buffer.
  • Weak error recovery — one bad token cascades into ident expected / 'END' expected.

4. Deliberate dialect choices (not gaps)

Classic identifiers (no _), # not <>, no octal B/C, 0x-only hex, NOT/AND/OR only, TopSpeed legacy isolated in the sidecar.

5. Biggest wins (all five landed today — see §6)

  1. FROM libc intrinsic ✅
  2. SET OF enum + SET OF char-range ✅
  3. MIN/MAX for REAL/LONGINT + gm2 sized types ✅
  4. SYSTEM INCL/EXCL/SHIFT/ROTATE ✅
  5. Forward type references ✅ (module-level globals; TYPE-block forwards already worked)

Also done: opaque/pointer-like types (incl. String), variant-record assignment (flat blit), nested-MODULE lowering, ADDRESS↔pointer assignment.

Remaining after today: GOTO/labels, generics, multiple inheritance (deliberate), result suffixes on statement designators, and the channel/device library holes in §2.

7. Done after the gap analysis (this session)

  • Result suffixes — F()^, F()[i], F().field (expression context). Statement-context suffix targets (F()[i] := x) and chained post-call calls (F().M()) are still not accepted.
  • PIM coroutines — PROCESS + NEWPROCESS/TRANSFER/IOTRANSFER (ucontext-backed).
  • Core text/ISO library — ConvTypes, IOConsts, SIOResult, TERMINATION, StrIO, InOut, StdIO, WholeStr, RealStr, LongStr, LongMath, STextIO, SWholeIO, SRealIO, DynamicStrings (core), SysStorage.
  • Backend — procedure values / LONGINT are 8-byte in array load/ store and in the parameter prologue (were 32-bit); driver session file cap 32 → 128.