Explorar el Código

docs: session summary for the resolve/check pass (2026-10-15)

Records the thread end to end: steps 1..4d (scaffold, forward resolution
into Check, InvalidType/IsFwdVar removal, stored expression types,
forward designator resolver, type propagation, forward-tainted
assignment checking), the key findings (AST is not a typed IR; Lower
constructs types while emitting; the scanner's position model; FwdVar*
cannot be deleted by moving checks), the machinery inventory, and the
next-step choices - implement the two-phase parse, drop the non-standard
tolerance, extend the touched-subtree checks, or stop.

Suite 193/193; fixpoint OK (3,491,608 bytes).
Eric Streit hace 2 días
padre
commit
05ede78ac2
Se han modificado 1 ficheros con 92 adiciones y 0 borrados
  1. 92 0
      docs/summary_resolve-check-2026-10-15.md

+ 92 - 0
docs/summary_resolve-check-2026-10-15.md

@@ -0,0 +1,92 @@
+# Session summary — 2026-10-15: the resolve/check pass
+
+Branch **`resolve-check`** (off `generics-inc1` / `6639c19`).
+Suite **193/193**; fixpoint **OK** (**3,491,608 bytes**); tree clean.
+
+Tags: `v3-resolve-check-step1` … `step4d`,
+`v3-two-phase-parse-plan`.
+
+## What the thread set out to do
+
+Split semantic analysis out of the grammar's parse-time actions into a
+post-parse pass (`Check`), so that identifier binding happens *after* a
+unit's declarations are known — which is what the forward-reference
+machinery (`FwdVar*`, `IsFwdVar`, the `Design` forward branch) exists to
+work around.
+
+## What landed (all green at each step)
+
+| Step | Commit | What |
+| --- | --- | --- |
+| 1 | `50df067` | `Check.def/.mod` scaffold (`Resolve`), called by the driver between `Parse` and `Lower`, gated by `-noresolve`. No-op; byte-identical. |
+| 2 | `f34ca98` | Forward *resolution* moved into `Check` (`NoteForward`/`FlushForward`); made scope-independent (`SymTab.fwdVarScope` + `FindScope`) so it can run post-parse. `FwdVarType` deleted. |
+| 3 | `7ca434b` | The forward operand is now `InvalidType`, so the generic "skip checks on an unknown type" rule replaces the purpose-built leniency. Deleted `SymTab.IsFwdVar`, the `fwdVarT` table and all five guards; `FwdVarRef` no longer allocates a placeholder. |
+| 4a | `5703b27` | Expression nodes now store their type (`SetTy` at the Expr/SimExpr/Term/Fact levels). |
+| 4b | `192ad55` | `Check` resolves a **forward designator's** full type (base symbol + selectors via `SelType`) and stores it on the node. |
+| 4c | `9dc2ed7` | Resolved types **propagate up** the expression tree (`PropNode`/`EvalExpr`); `Lower` reads the stored type for string/set literals (removes a duplicate construction). |
+| 4d | `8efae2e` | A **forward-tainted assignment is now type-checked** — `n := "abc"` with a later `VAR n : INTEGER` was silently accepted. |
+
+New tests: `t_fwdsel.mod`, `t_fwdprop.mod`, `t_bad_fwd.mod` (expect_fail).
+
+## Findings worth keeping
+
+- **The AST is not a typed IR.** `NkArray`/`NkRecord`/`NkPointer`/
+  `NkEnum`/`NkTypeIdent`/`NkProcType` are never built; constructed types
+  live only in `SymTab`.  So the original plan's "walk the type AST" had
+  nothing to walk — type-constructor checks stay parse-time.
+- **`Lower` constructs types while emitting** and `NewDesc` does not
+  dedup, so a type must be constructed once and cached on the node (the
+  parser already does, and `Lower` now reads it for literals).
+- **The forward tolerance is a tested feature** (`t_fwdvar`,
+  `t_fwdsel`, `t_fwdprop`, `Showcase22`), including through an
+  array-index selector.
+- **`FwdVar*` cannot be deleted by moving checks.**  Even with every
+  check in the walk, `Design` must still *bind* a not-yet-declared name —
+  impossible in one pass.  That needs a genuinely **two-phase parse**
+  (declarations before bodies).  See `docs/plan-two-phase-parse.md`.
+- **The scanner's position model is quirky**: after `Get`, `sym` is the
+  token just scanned but `pos`/`len` describe the *previous* one
+  (`LexString` vs `LookAheadName`), and `Reset` does **not** reset
+  `pos`/`len`/`nextPos`/`nextLen`/`line`/`col`, so stale values leak
+  across files.  Any mid-file restart must reproduce that state exactly.
+
+## Where the machinery lives now
+
+- `SymTab`: `FwdVarRef`/`FwdVarName`/`FwdVarResolve` — record (name,
+  kind, scope) and resolve by name; no placeholder, no leniency.
+- `Check`: `NoteForward`/`FlushForward` (resolve + store the designator
+  type), `SelType`, `EvalExpr`/`PropNode` (propagate), `CheckAssign`
+  (forward-tainted assignments).
+- Grammar: `Design`'s forward branch (`FwdVarRef` + `Check.NoteForward`,
+  `Op=1` marker on the node).
+- `Lower`: unchanged type computation except the literal cases.
+
+## Next steps (choices)
+
+1. **Implement the two-phase parse** (`docs/plan-two-phase-parse.md`),
+   then delete `FwdVar*`/`NoteForward`/`FlushForward` and the `Design`
+   forward branch.  Recommended *start*: do the restart at the
+   **driver level** using the exported `Reset` + repeated `Get` (not new
+   scanner internals), add a small grammar **body-entry** production, and
+   **calibrate the stop point empirically** (encode observed `pos`/`len`
+   into the error channel).  Largest piece in the thread; spans sessions.
+2. **Drop the non-standard tolerance** (a forward reference is a plain
+   `201`) and update the four tests that rely on it.  Deletes the whole
+   mechanism for near-zero work, but is a language-semantics change.
+3. **Extend the touched-subtree checks** (control-flow conditions,
+   `RETURN`, call arity) — the same safe pattern as step 4d; more
+   forward-reference diagnostics, no parser change.
+4. **Stop** — the forward mechanism is clean, forward references are
+   typed and checked, and `FwdVar*` is a small, self-contained path.
+
+## Resume
+
+```sh
+cd .../m2compiler-V3
+git checkout resolve-check
+./bootstrap/fixpoint.sh                       # FIXPOINT OK
+cd compiler && ./build.sh && ./run_tests.sh   # 193/193
+```
+
+Key docs: `docs/plan-resolve-check.md` (running record),
+`docs/plan-two-phase-parse.md` (design for step 1 above).