Branch ast-stage-c, HEAD e276345. Suite 182/182;
fixpoint OK (3,147,849 bytes). Tree clean except toto.mod.
Finish L3 (procedures) for the Lower phase
(docs/plan-lower.md, docs/summary_two-phase-lower3-finding.md) using
option 2 with zero SymTab surface: a Lower-owned symbol/signature
table filled by the grammar while scopes are open, so the end-of-unit
walk can resolve locals/params after SymTab has popped their scopes —
without touching SymTab (which broke self-hosting when tried).
Option 2's architecture works and does not break self-hosting:
Lower owned tables worked: syms[] (name/kind/ty) with a scope
save-stack (ScopeEnter/ScopeLeave), and procs[] (uid/res/depth/
ext/params) via NoteProc/NoteParam/SetProcRes. Resolution via
FindSym/FindProc (Resolve, ProcInfo, ParamInfo).M2: Lower.ResetVarDecl: Lower.NoteVar(nm, KindVar, t) per pend nameConstDecl: Lower.NoteVar(n, KindConst, t)ProcHeading: Lower.ScopeEnter, Lower.NoteProc(pn, uid, Invalid,
depth, FALSE) after EnterProc; Lower.SetProcRes(t) at the resultParamSection: Lower.NoteParam(nm, t, isV) +
Lower.NoteVar(nm, KindParam, t) per nameProcDecl: Lower.ScopeLeave in all three arms (FORWARD / EXTERNAL /
body)CanLower, the procedure prologue emitted
correctly and byte-aligned with the legacy image: BeginFunc,
FuncParam for each formal, EndFuncHeader, and the locals did
emit (%t8 =l alloc4 4 ... for a local s).The procedure body did not emit: LowerDecls's NkProcDecl branch
does
LowerDecls(AST.Child(c, 2)); (* locals -- emitted OK *)
LowerStmt(AST.Child(c, 1)); (* body -- emitted NOTHING *)
QbeGen.EndFunc(res)
and the output jumped straight from the locals to EndFunc's default
ret 0, i.e. LowerStmt(AST.Child(c, 1)) dispatched to nothing.
First step next session: dump ONE NkProcDecl's children to pin the
body index. Add a one-off AST.Dump(astUnit, 0) in the root (and
remove it before committing — an ASTDUMP was accidentally committed
once and cost real time), compile e.g.:
MODULE Proc1;
VAR ExitCode : INTEGER;
PROCEDURE Add(a, b : INTEGER) : INTEGER;
VAR s : INTEGER;
BEGIN s := a + b; RETURN s END Add;
BEGIN ExitCode := Add(4, 10) END Proc1.
Expect NkProcDecl = [0]=NkIdent Add, [1]=NkBlock[assign, return],
[2]=locals. If the body is elsewhere (e.g. child 1 is the locals and
the block is nested), fix the dispatch in LowerDecls. The earlier
slice-5 dump showed child 1 = the body block; the locals (child 2) now
emit, so verify whether setting child 2 shifted the body.
Likely fix: the body is AST.Child(c, 1) and LowerStmt handles
NkBlock — check LowerStmt's NkBlock branch and the assign/return
cases reach it. A QbeGen.Remark at the top of LowerStmt (removed
after) will show whether it is entered for the body.
CanLower (it already routes NkDecl → CanProc; the
earlier session found NkProcDecl op defaulted to -1 and CanProc
requires 0 for a normal body — set AST.SetOp(astNode, 0) at node
creation in ProcDecl; this was one of the two real bugs).lower_ok t_lower3.mod to run_tests.sh.build.sh + run_tests.sh (expect 183/183) + fixpoint.sh
(must stay OK — the whole point of option 2).NkProcDecl op defaults to -1; normal body must be 0
(CanProc rejects otherwise). Set it at creation.NkCall child 0 is an NkDesignator, not a bare NkIdent — calls
mangled as $_0. Lower.CalleeName unwraps the designator (already
in the committed v3-lower3-wip Lower.mod; re-add for option 2).SymTab (that broke self-hosting).L emitters concurrently (shared
QbeGen buffer + counters).ASTDUMP in M2.atg.cd ~/Projets/Projets-Modula2/MyWork/m2compiler-V3
git checkout ast-stage-c
cd compiler && ./build.sh && ./run_tests.sh # 182/182
cd .. && ./bootstrap/fixpoint.sh # FIXPOINT OK