# Within-module forward procedure calls — M2S and M2P compile clean (110/110) Tag `v3-forward-calls`. ## The finding CR's parser output **relies on gm2 hoisting**: by default it wraps its forward declarations in a comment (`(* ----- FORWARD not needed in multipass compilers … *)`). The `-m` flag toggles that (`M Suppresses FORWARD declarations in parser (for multipass compilers)`), and our `build.sh` passed `-m`. V3 is single-pass, so with `-m` the generated `M2P.mod` had ~102 `undeclared identifier` errors for its own procedures. ## The fix (two halves) 1. **Emit real `FORWARD` declarations for V3-facing generation** — run CR **without `-m`**. V3 already supports `FORWARD` (including mutual recursion); verified with a direct test. 2. **Keep `-m` for the gm2 stage-1 build.** gm2 *compiles* `FORWARD` fine (`gm2 -c` OK) but its link-time whole-program/“pass 3” recompilation rejects `FORWARD` in an implementation module (`too many errors in pass 3`); reproduced minimally, and `-fscaffold-*`, `-fno-whole-program` etc. do not help. So the bootstrap is stage-specific: stage 1 (gm2) generates with `-m`; stage 2+ (V3) generates without `-m`. The `.ssa` fixpoint is between V3 stages, both using the `FORWARD` form. ## Other fixes in this step - **Empty `ELSIF … THEN` body** — the earlier `[ StatSeq ]` change had missed the `ELSIF` site (different indentation); CR-generated code hits it (comment-only `THEN`). - **`BITSET`** — added as a predefined type (`SET OF [0..15]`). - **Typed set constructor** `TypeName{ … }` (`BITSET{0}`, `BITSET{}`) — new `TypedSetLit` rule; `Fact` uses it when a type name designator is followed by `{`. - **File-handle leak** — `compiler.frm` never closed the source file, so each session source consumed a handle and the host `FileIO` cap (`MaxFiles = BitSetSize` = 16) tripped once a session had >16 sources (exactly the compiler's own build). The driver now closes `src` after each file. ## Result Under V3 (sources generated without `-m`): | Source | Errors | |---|---| | `SymTab` | 0 | | `QbeGen` | 0 | | `M2S` | 0 | | `M2P` | **0** (was 107 before FORWARDs, then 5, then 0) | Main suite **110/110**. ## Remaining: the driver `M2.mod` (12 errors) - **Nested modules** — the driver's `ListHandler` is a Wirth-style local `MODULE … EXPORT StoreError, PrintListing, …`, which V3 parses but marks 230; its exports (`StoreError`, `PrintListing`, `PrintConsoleErrors`, `ResetErrors`) are therefore undeclared at the enclosing scope. - **String building** — the driver uses `Strings.Delete`/`Append` to construct file names. V3's descriptor header is *both* the open-array count and the logical string length (`m2setlen` sets it), so once `Assign` shrinks it, `Append` writing past the logical length traps. `Delete` alone is fine; incremental `Append` needs a model decision (or a driver rewrite). ## Build note Generating for V3 (no `-m`) and for gm2 (`-m`) are different commands; this wants a small stage-aware build script (or a `build_v3.sh`), which also feeds `bootstrap/fixpoint.sh`. ## Files `compiler/src/M2.atg`, `compiler/src/SymTab.mod`, `compiler/src/compiler.frm`, `compiler/src/M2.lst`.