# Session summary — 2026-10-15: two-phase parse, milestone 1 (probe) Branch **`resolve-check`**, HEAD `6c64b46`, tag **`v3-two-phase-m1`**. Suite **196/196**; fixpoint **OK (3,517,333 bytes)**; tree clean. Design doc: `docs/plan-two-phase-parse.md` (now carries a Progress section). Previous session: `docs/summary_resolve-check-2026-10-15.md` (step 4d, the conclusion that `FwdVar*` can only die with a two-phase parse). ## Goal of the thread Delete `SymTab.FwdVarRef`/`FwdVarName`/`FwdVarResolve`, `Check.NoteForward`/`FlushForward`, the `Design` forward branch and the `Op=1` marker — by parsing module-level declarations before procedure bodies, so a forward reference binds normally instead of being deferred. ## What milestone 1 landed (all green) Each outermost procedure/method body is **shadow-skipped at the token level and the skip verified before the body parses normally**, behind `-twophase` (default off — fully inert): | Piece | File | What | | --- | --- | --- | | State save/restore | `compiler/src/scanner.frm` | `PushPos`/`PopPos`: full scanner state (cursor, all published positions, `errors`) on an 8-deep stack | | Switch + helpers | `compiler/src/M2.atg` | `SetTwoPhase`, `procNest` (hooked once in `Block`, so `ProcDecl` *and* method bodies probe), `TokIs` (token-*text* compare), `Adv`, `ProbeBody` (opener counting + `END ` check) | | Flag plumbing | `compiler/src/compiler.frm`, `parser.frm` | `-twophase` → `M2P.SetTwoPhase` at both driver `Parse` sites | | Gate | `compiler/run_tests.sh` | `twophase_same`: `.ssa` byte-compared flag-off vs flag-on (3 files) | | Generated fallout | `compiler/src/M2.lst` | Coco/R listing regenerated (line shifts only) | Opener set: `MODULE` (nested modules), `PROCEDURE`, `CLASS` (incl. `CLASS IMPLEMENTATION`), `RECORD` (incl. variant `CASE`), `IF`, `CASE`, `WHILE`, `FOR`, `LOOP`, `WITH`. `BEGIN` is not an opener; `REPEAT…UNTIL` needs none. The probe is silent on success; a bad skip prints `twophase probe mismatch` to StdOut — never into the listing. ## Verification - `./build.sh` + `./run_tests.sh` → **196/196** (193 + 3 probe gates). - Flag-on sweep: all **142 single-file tests** plus session spot-checks (`DBasicProg`, `TString`, `Showcase12`, `Library1`) and the compiler's own sources (`SymTab.mod`, `M2P.mod`, `Lower.mod`) produce **byte-identical `.ssa`/`.LST`**, zero mismatches. - Positive control: a temporary success print proved the probe fires exactly when the flag is on (then reverted). - `bootstrap/fixpoint.sh` → **FIXPOINT OK** (3,517,333 bytes). ## Finding worth keeping (V3 name resolution) The first cut called the frame's `Get` from the new atg helper. gm2 accepts it (whole-module scope), but the self-hosted V3 compiler resolves **single-pass**: the bare `Get` bound the *imported* `M2S.Get(VAR sym)` and stage 2 failed with 233s (plus knock-on 201s at `END M2P`). Fix: advance via `Adv` (`M2S.Get(sym); INC(errDist)` — exactly the frame's `Get` body for this pragma-free grammar, where the scanner never returns a token above `maxT`). **Rule going forward: atg helpers must never call a later-declared frame procedure by a name that collides with an import.** (Same family as the known declaration-before-use constraint on `Check`.) ## What is next (milestone 2) Deferral + body re-parse, still behind the flag, byte-compared: 1. **Record body spans** in the probe: table of (procedure name, start `nextPos`) for outermost bodies; dump it in diagnostic mode and calibrate (stability across the suite is the gate). 2. **Restart mechanism** — two candidates, decision open: - *(a, recommended)* driver-level replay: `M2S.Reset` + repeated `Get` to the recorded offset (reproduces exact scanner state, incl. `pos`/`len`, with no new scanner internals). Caveat: `Reset` re-reads the `src` handle — check `FileIO` reposition semantics or reopen the file per body. - *(b)* scanner `SeekTo(p)` with line recompute (plan's original piece 1); must handle the stale `pos`/`len` model (they describe the *previous* token — see the resolve-check summary §"scanner's position model"). 3. **Body-entry production**: parse one `Block` for an already-entered heading at end of unit, walking the span table; bodies then see all module-level declarations. 4. **Flip + delete**: default on when suite and fixpoint byte-compare; then delete `FwdVar*`, `NoteForward`/`FlushForward`, the `Design` forward branch and the `Op=1` marker (the §9 list in `docs/plan-resolve-check.md`, plus `PushPos`/`PopPos`/`ProbeBody` if unused). Watch items: nested-module procedures (probe covers them; decide whether deferral does), generic re-parse (`DoInst` interplay), `errDist` save/restore across restarts (error suppression must not shift), the `M2.lst` churn convention (commit it with the `.atg`). ## Resume ```sh cd .../m2compiler-V3 git checkout resolve-check # HEAD 6c64b46, tag v3-two-phase-m1 cd compiler && ./build.sh && ./run_tests.sh # 196/196 cd .. && ./bootstrap/fixpoint.sh # FIXPOINT OK ``` Probe check on any file: `./M2 -twophase tests/.mod` — identical `.ssa`, no `twophase probe mismatch` on StdOut.