# 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 `** (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 ` 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`, `LongIO`, `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.