Procházet zdrojové kódy

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 před 3 dny
rodič
revize
7de78bbefc
2 změnil soubory, kde provedl 45 přidání a 25 odebrání
  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;
 
 PROCEDURE RestoreSession;
+  VAR i : CARDINAL;
   BEGIN
     nTemp := sav_nTemp; nLab := sav_nLab; loopTop := sav_loopTop;
     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;
     ustrN := sav_ustrN; ustrUsed := sav_ustrUsed;
     ctorN := sav_ctorN; ctorTop := sav_ctorTop;
@@ -144,7 +145,18 @@ PROCEDURE RestoreSession;
     callDepth := sav_callDepth; outSel := sav_outSel;
     withTop := sav_withTop;
     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;
 
 (* ---------------- small string utilities ---------------- *)

+ 31 - 23
docs/STATUS.md

@@ -111,9 +111,13 @@ Done:
 1. Builtin statements (`EXIT`, `INCL/EXCL`, `INC/DEC`, `NEW`, `DISPOSE`).
 2. Control flow (`IF`/`WHILE`/`REPEAT`/`LOOP`/`FOR`/`CASE`), `RETURN`,
    `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:
-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
    front-end **constant evaluator**.  `Expr`'s `q` is handed to
    `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
    dropping the grammar's value layer.  Designators are entangled the
    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`.
-6. Remove the dead scaffolding: `LowerSession`, `MaterializeGlobals`,
+7. Remove the dead scaffolding: `LowerSession`, `MaterializeGlobals`,
    `SaveSession`/`RestoreSession` and the `SetNoEmit` machinery (once
    nothing emits inline), plus the dead `SetLower`/`lowerRequested`.
 
 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?