Quellcode durchsuchen

fix: RestoreSession clears the VAR-actual note ring (grammar notes leaked into Lower)

AddrOfVal scans the whole 256-entry nVal/nAddr ring, while RestoreSession
restored nn but left the ring contents.  The grammar's suppressed
parse-time emit still fills that ring via NoteAddr, and the keys are
temps that restart per function, so a stale grammar entry can false-match
Lower's own key and resolve a VAR actual to the wrong address.

Removing the grammar's QbeGen.DeclVar (fewer AllocLocal/NewTemp calls)
shifted those keys and exposed it: Strings.Replace's `src` VAR actual
became `$Strings_nl`, so Showcase11 no longer linked.  RestoreSession now
clears the ring; Lower re-notes its own VAR actuals, so an empty ring is
correct.

The fix is output-neutral on Showcase11 and corrects some latent
resolutions in the compiler's own image.  Suite 204/204; FIXPOINT OK
(3,541,049 bytes).
Eric Streit vor 4 Tagen
Ursprung
Commit
7de78bbefc
2 geänderte Dateien mit 45 neuen und 25 gelöschten Zeilen
  1. 14 2
      compiler/src/QbeGen.mod
  2. 31 23
      docs/STATUS.md

+ 14 - 2
compiler/src/QbeGen.mod

@@ -133,10 +133,11 @@ PROCEDURE SaveSession;
   END SaveSession;
   END SaveSession;
 
 
 PROCEDURE RestoreSession;
 PROCEDURE RestoreSession;
+  VAR i : CARDINAL;
   BEGIN
   BEGIN
     nTemp := sav_nTemp; nLab := sav_nLab; loopTop := sav_loopTop;
     nTemp := sav_nTemp; nLab := sav_nLab; loopTop := sav_loopTop;
     nR := sav_nR; nStr := sav_nStr; nLoc := sav_nLoc; nPar := sav_nPar;
     nR := sav_nR; nStr := sav_nStr; nLoc := sav_nLoc; nPar := sav_nPar;
-    nArg := sav_nArg; nn := sav_nn; nInit := sav_nInit;
+    nArg := sav_nArg; nInit := sav_nInit;
     sessUsed := sav_sessUsed; delayTop := sav_delayTop;
     sessUsed := sav_sessUsed; delayTop := sav_delayTop;
     ustrN := sav_ustrN; ustrUsed := sav_ustrUsed;
     ustrN := sav_ustrN; ustrUsed := sav_ustrUsed;
     ctorN := sav_ctorN; ctorTop := sav_ctorTop;
     ctorN := sav_ctorN; ctorTop := sav_ctorTop;
@@ -144,7 +145,18 @@ PROCEDURE RestoreSession;
     callDepth := sav_callDepth; outSel := sav_outSel;
     callDepth := sav_callDepth; outSel := sav_outSel;
     withTop := sav_withTop;
     withTop := sav_withTop;
     inBody := sav_inBody; inFunc := sav_inFunc;
     inBody := sav_inBody; inFunc := sav_inFunc;
-    useStack := sav_useStack; recvArmed := sav_recvArmed
+    useStack := sav_useStack; recvArmed := sav_recvArmed;
+    (* Clear the VAR-actual note ring.  The grammar's suppressed
+       parse-time emit never reaches the buffer, but its NoteAddr calls
+       do fill this ring; AddrOfVal scans the whole ring and the keys
+       are temps that restart per function, so a stale grammar entry can
+       false-match Lower's own key.  Lower re-notes its own VAR
+       actuals, so starting from an empty ring is correct. *)
+    nn := 0;
+    i := 0;
+    WHILE i <= HIGH(nVal) DO
+      nVal[i][0] := CHR(0); nAddr[i][0] := CHR(0); INC(i)
+    END
   END RestoreSession;
   END RestoreSession;
 
 
 (* ---------------- small string utilities ---------------- *)
 (* ---------------- small string utilities ---------------- *)

+ 31 - 23
docs/STATUS.md

@@ -111,9 +111,13 @@ Done:
 1. Builtin statements (`EXIT`, `INCL/EXCL`, `INC/DEC`, `NEW`, `DISPOSE`).
 1. Builtin statements (`EXIT`, `INCL/EXCL`, `INC/DEC`, `NEW`, `DISPOSE`).
 2. Control flow (`IF`/`WHILE`/`REPEAT`/`LOOP`/`FOR`/`CASE`), `RETURN`,
 2. Control flow (`IF`/`WHILE`/`REPEAT`/`LOOP`/`FOR`/`CASE`), `RETURN`,
    `HALT`.
    `HALT`.
+3. Declarations/lifecycle: `DeclVar`/`DeclConst`, the module/impl/
+   nested/class `BeginInit`/`EndInit`/`BeginBody`/`EndModule`,
+   `FlushPend`, `FwdPatch`.  (This exposed and fixed a real bug — see
+   below.)
 
 
 Still to do:
 Still to do:
-3. **Expressions / `Fact` / `Term` / `SimExpr` / `Expr`: BLOCKED.**
+4. **Expressions / `Fact` / `Term` / `SimExpr` / `Expr`: BLOCKED.**
    Their `QbeGen` calls are not (only) inline emit — they are the
    Their `QbeGen` calls are not (only) inline emit — they are the
    front-end **constant evaluator**.  `Expr`'s `q` is handed to
    front-end **constant evaluator**.  `Expr`'s `q` is handed to
    `SymTab.SetSymVal` and `Lower.NoteConstVal` for a CONST, and
    `SymTab.SetSymVal` and `Lower.NoteConstVal` for a CONST, and
@@ -125,33 +129,37 @@ Still to do:
    constants itself from the AST (a real change, not cleanup), then
    constants itself from the AST (a real change, not cleanup), then
    dropping the grammar's value layer.  Designators are entangled the
    dropping the grammar's value layer.  Designators are entangled the
    same way (`qn`/`NoteAddr` feed VAR-actual addresses).
    same way (`qn`/`NoteAddr` feed VAR-actual addresses).
-4. **Calls, declarations, proc headings, imports, classes: BLOCKED
-   too, by fused validation.**  The `QbeGen` emit functions double as
-   semantic checks and the grammar depends on their result:
-   `QbeGen.CallArg` (`IF NOT … THEN SemError(233)`), `AddrOfVal`
-   (VAR-actual address + 233), `FuncParam` (`IF NOT …` on a heading).
-   Removing the calls would remove error detection, so this needs the
-   checks reimplemented in `SymTab`/the grammar first — again a real
-   change, not cleanup.  Only the genuinely pure emitters
-   (`Jmp`/`EmitLabel`/`NewLabel`/`SetBit`/`NewHeap`/`DeclVar`/… when
-   not used as a check) can be dropped.
-5. Retire the `-lower` test flag and the now-degenerate byte-compare
+5. **Calls, proc headings, imports, classes: BLOCKED too, by fused
+   validation.**  The `QbeGen` emit functions double as semantic
+   checks and the grammar depends on their result: `QbeGen.CallArg`
+   (`IF NOT … THEN SemError(233)`), `AddrOfVal` (VAR-actual address +
+   233), `FuncParam` (`IF NOT …` on a heading).  Removing the calls
+   would remove error detection, so this needs the checks
+   reimplemented in `SymTab`/the grammar first — again a real change,
+   not cleanup.
+6. Retire the `-lower` test flag and the now-degenerate byte-compare
    tests in `run_tests.sh`.
    tests in `run_tests.sh`.
-6. Remove the dead scaffolding: `LowerSession`, `MaterializeGlobals`,
+7. Remove the dead scaffolding: `LowerSession`, `MaterializeGlobals`,
    `SaveSession`/`RestoreSession` and the `SetNoEmit` machinery (once
    `SaveSession`/`RestoreSession` and the `SetNoEmit` machinery (once
    nothing emits inline), plus the dead `SetLower`/`lowerRequested`.
    nothing emits inline), plus the dead `SetLower`/`lowerRequested`.
 
 
 Note: full deletion to zero is gated on moving constant folding into
 Note: full deletion to zero is gated on moving constant folding into
-`Lower` (item 3).  Until then the expression/value layer stays.
-
-Attempted and reverted (2026-10-08): deleting the grammar's
-`QbeGen.DeclVar` (in `VarDecl`) is **not** safe.  It makes `Lower`
-resolve one local to a global (`Showcase11`: the `src` VAR actual in
-`Strings.Replace` becomes `$Strings_nl`), reproducibly, even though
-`Lower.mod` and the AST are unchanged — another instance of the
-layout-sensitive latent bug class this project has hit before.
-`QbeGen.DeclConst` removal alone is fine; `DeclVar` stays until that
-bug is understood.
+`Lower` (item 4).  Until then the expression/value layer stays.
+
+### Fixed (2026-10-08): the note ring leaked the grammar's notes into Lower
+
+Removing `QbeGen.DeclVar` from `VarDecl` had made `Lower` resolve one
+VAR actual to the wrong symbol (`Showcase11`: `Strings.Replace`'s `src`
+became `$Strings_nl`).  Root cause: `QbeGen.AddrOfVal` scans the whole
+256-entry `nVal`/`nAddr` VAR-actual note ring, and `RestoreSession`
+restored `nn` but never cleared the ring — so the grammar's suppressed
+parse-time `NoteAddr` entries survived into `Lower`.  Their keys are
+temps that restart per function, so a stale entry could false-match
+`Lower`'s own key.  Removing `DeclVar` (fewer `AllocLocal`/`NewTemp`
+calls) shifted those keys and exposed it.  `RestoreSession` now clears
+the ring; `Lower` re-notes its own VAR actuals, so an empty ring is
+correct.  The fix is output-neutral on `Showcase11` and corrects some
+latent resolutions in the compiler's own image (fixpoint size moved).
 
 
 
 
 ## Are we near completion?
 ## Are we near completion?