|
@@ -0,0 +1,104 @@
|
|
|
|
|
+# Session summary — 2026-10-16: two-phase parse, milestones 2a–2c
|
|
|
|
|
+
|
|
|
|
|
+Branch **`resolve-check`**, HEAD `f500a12`, tags `v3-two-phase-m2a`,
|
|
|
|
|
+`v3-two-phase-m2b`, `v3-two-phase-m2c`. Suite **199/199**; fixpoint
|
|
|
|
|
+**OK (3,620,461 bytes)**; tree clean. Design/record:
|
|
|
|
|
+`docs/plan-two-phase-parse.md`.
|
|
|
|
|
+
|
|
|
|
|
+## Goal
|
|
|
|
|
+
|
|
|
|
|
+Delete `SymTab.FwdVarRef`/`FwdVarName`/`FwdVarResolve`,
|
|
|
|
|
+`Check.NoteForward`/`FlushForward`, the `Design` forward branch and the
|
|
|
|
|
+`Op=1` marker — by parsing a unit's declarations before its procedure
|
|
|
|
|
+bodies, so forward references bind normally.
|
|
|
|
|
+
|
|
|
|
|
+## What landed (all behind `-twophase`, default off)
|
|
|
|
|
+
|
|
|
|
|
+| Step | Commit | What |
|
|
|
|
|
+| --- | --- | --- |
|
|
|
|
|
+| 2a | `dce67fc` | `scanner.SeekTo(p)`: replay `NextCh` over the in-memory buffer (O(p), no I/O); body-span table + a `SeekTo` round-trip fidelity check in the probe. |
|
|
|
|
|
+| 2b | `cdb6436` | `SymTab.DeferReset`/`DeferBody`/`ReenterDeferred`/`ParamName`; `Lower.ReenterScopeProc`/`CurScopeProc`; span table carries the deferral index. |
|
|
|
|
|
+| 2c | `f500a12` | Real deferral: phase-1 `DeferSkip` (leave scanner at the matching `END`, close like `FORWARD`), phase-2 re-parse loop at module ends. |
|
|
|
|
|
+
|
|
|
|
|
+## Mechanism (2c)
|
|
|
|
|
+
|
|
|
|
|
+- **Phase 1** — `DeferSkip` (reached from `Block`, only for plain
|
|
|
|
|
+ `ProcDecl` bodies; methods stay inline) consumes the body tokens for
|
|
|
|
|
+ real, verifies `END <name>`, leaves the scanner at that `END` so
|
|
|
|
|
+ `Block`'s trailing `END ident` consumes it, records the span; then
|
|
|
|
|
+ `ProcDecl` closes the proc like `FORWARD` (`AbortFunc`, no body node).
|
|
|
|
|
+- **Phase 2** — an inlined loop at both module ends
|
|
|
|
|
+ (`ProgModule`/`ImplUnit`) does, per span: `SeekTo`+`Adv`,
|
|
|
|
|
+ `SymTab.ReenterDeferred`, `Lower.ReenterScopeProc`, `QbeGen.BeginFunc`,
|
|
|
|
|
+ `Block(nm)`, attach body + block decls to the proc node,
|
|
|
|
|
+ `ScopeLeave`/`CloseProc`/`EndFunc`. Scanner/parser state and the
|
|
|
|
|
+ unit's own `astDecl`/`astStmt` are saved and restored around the loop.
|
|
|
|
|
+- Opens/closes pair **within each pass**; **registrations run once**
|
|
|
|
|
+ (skip pass); the re-parse uses the resume paths.
|
|
|
|
|
+
|
|
|
|
|
+## Two real bugs found and fixed while landing it
|
|
|
|
|
+
|
|
|
|
|
+1. The re-parse loop **clobbered the unit's `astDecl`/`astStmt`** before
|
|
|
|
|
+ `ProgModule` built the unit (main got a proc's statements). Fixed by
|
|
|
|
|
+ saving/restoring them around the loop.
|
|
|
|
|
+2. `PopPos` restores the scanner's **`errors` counter** (deliberate for
|
|
|
|
|
+ the shadow probe), so it **wiped every diagnostic recorded during the
|
|
|
|
|
+ re-parse** — `210`/`232` etc. inside deferred bodies were silently
|
|
|
|
|
+ dropped (`t_bad_fwd` was wrongly accepted). Fixed by re-instating
|
|
|
|
|
+ the post-loop count after `PopPos`.
|
|
|
|
|
+
|
|
|
|
|
+## Findings worth keeping
|
|
|
|
|
+
|
|
|
|
|
+- **No forward procedure tolerance exists.** A call to a proc declared
|
|
|
|
|
+ later in the same module is `233` (+ a knock-on `201` at EOF); a bare
|
|
|
|
|
+ `P;` two lines early is `233`. The compiler's own sources self-compile
|
|
|
|
|
+ only because each `.mod` is paired with its `.def`, parsed first (so
|
|
|
|
|
+ e.g. `SemError` is declared before `NoteLabel`'s body compiles), plus
|
|
|
|
|
+ strict declaration-before-use inside modules. Consequence for the
|
|
|
|
|
+ grammar: **atg helpers must be declared before use** (`NoteSpan` had to
|
|
|
|
|
+ move above `ProbeBody`); the `FwdVar` tolerance covers module-level
|
|
|
|
|
+ *variables* only.
|
|
|
|
|
+- **Suppressed inline emit = emission only.** Under `SetNoEmit` every
|
|
|
|
|
+ `QbeGen` counter still advances and is discarded per unit by
|
|
|
|
|
+ `RestoreSession`; `Lower`'s tables are permanent session state.
|
|
|
|
|
+- **Byte-identity vs the old default is the wrong gate for forward
|
|
|
|
|
+ code.** Deferral makes a forward variable ordinary, so it compiles
|
|
|
|
|
+ like a normally-ordered one; the legacy `FwdVar` path is the divergent
|
|
|
|
|
+ one (it drops a `loadw`). Deferred output matches the *normal*
|
|
|
|
|
+ codegen and runs correctly.
|
|
|
|
|
+
|
|
|
|
|
+## Measured (2c)
|
|
|
|
|
+
|
|
|
|
|
+- Suite **199/199** — 193 core + gates: `twophase_same`
|
|
|
|
|
+ (`t_nestedproc`, `t_classmethod`) identity; `twophase_run`
|
|
|
|
|
+ (`t_fwdvar`/`t_fwdsel`/`t_fwdprop`) `-> 42`; `twophase_fail`
|
|
|
|
|
+ (`t_bad_fwd`) `incompatible assignment`.
|
|
|
|
|
+- All **142 single-file tests** under `-twophase`: **0 signals**,
|
|
|
|
|
+ **138 byte-identical**. The 4 differing files are exactly the
|
|
|
|
|
+ forward-reference tests.
|
|
|
|
|
+- Sessions `DBasicProg`/`TString`/`Showcase12` byte-identical.
|
|
|
|
|
+- Fixpoint OK (flag off).
|
|
|
|
|
+
|
|
|
|
|
+## Known gap (why it is not flipped)
|
|
|
|
|
+
|
|
|
|
|
+On some multi-module **implementation** sessions (repro:
|
|
|
|
|
+`convtypes.def/.mod conversions.def/.mod`, i.e. `Conversions.mod` inside
|
|
|
|
|
+the Library1 session) `-twophase` records a **spurious `ident expected`**
|
|
|
|
|
+positioned inside a repassed body — while the emitted `.ssa` is
|
|
|
|
|
+**identical** to the default. The repass reaches the correct AST but
|
|
|
|
|
+trips one extra `SynError`. Candidate causes (to pin next): the
|
|
|
|
|
+`pos`/`len` "previous token" model interacting with `SeekTo`, or a
|
|
|
|
|
+construct where the repass consumes one token too many. Reproduction is
|
|
|
|
|
+small and self-contained, so this is the immediate next step; the flip
|
|
|
|
|
+and the `FwdVar*` deletion follow it.
|
|
|
|
|
+
|
|
|
|
|
+## Resume
|
|
|
|
|
+
|
|
|
|
|
+```sh
|
|
|
|
|
+cd .../m2compiler-V3/compiler
|
|
|
|
|
+./build.sh && ./run_tests.sh # 199/199
|
|
|
|
|
+./M2 -twophase ../stdlib/convtypes.def ../stdlib/convtypes.mod \
|
|
|
|
|
+ ../stdlib/conversions.def ../stdlib/conversions.mod
|
|
|
|
|
+# -> spurious ".../conversions.mod:148:3: ident expected"
|
|
|
|
|
+cd .. && ./bootstrap/fixpoint.sh # FIXPOINT OK
|
|
|
|
|
+```
|