Ver Fonte

step10 fix: init numbers from shared proc pool + Showcase3; 144/144 tests green

Eric Streit há 3 semanas atrás
pai
commit
64c78543cf
15 ficheiros alterados com 203 adições e 4 exclusões
  1. BIN
      BMix.MC4
  2. BIN
      M2c
  3. 2 1
      MGen.def
  4. 6 3
      MGen.mod
  5. BIN
      MGen.o
  6. 52 0
      Showcase3.LST
  7. BIN
      Showcase3.MC4
  8. 46 0
      Showcase3.mod
  9. 7 0
      SymTab.def
  10. 21 0
      SymTab.mod
  11. BIN
      SymTab.o
  12. 16 0
      docs/summary_step10.md
  13. 1 0
      run_tests.sh
  14. 29 0
      tests/b_mix.LST
  15. 23 0
      tests/b_mix.mod

BIN
BMix.MC4


BIN
M2c


+ 2 - 1
MGen.def

@@ -271,7 +271,8 @@ PROCEDURE Leave (nPar: CARDINAL; func: BOOLEAN);
 
 
 PROCEDURE ModInitBegin (): INTEGER;
 PROCEDURE ModInitBegin (): INTEGER;
 (* Opens a module init body as a parameterless proper procedure,
 (* Opens a module init body as a parameterless proper procedure,
-   recorded for the startup calls in BeginBody. -1 when full. *)
+   recorded for the startup calls in BeginBody. The number comes
+   from SymTab's shared proc pool. -1 when full. *)
 
 
 PROCEDURE ModInitEnd (num: INTEGER);
 PROCEDURE ModInitEnd (num: INTEGER);
 (* Closes a module init body (no-op for num < 0). *)
 (* Closes a module init body (no-op for num < 0). *)

+ 6 - 3
MGen.mod

@@ -611,11 +611,14 @@ PROCEDURE ModInitBegin (): INTEGER;
 (* Opens a module init body as a parameterless proper procedure
 (* Opens a module init body as a parameterless proper procedure
    (module bodies declare no locals; globals are used directly).
    (module bodies declare no locals; globals are used directly).
    Records its number for the startup calls in BeginBody.
    Records its number for the startup calls in BeginBody.
-   -1 when the proc table or init list is full. *)
+   The number comes from SymTab's shared proc pool (never
+   maxNum+1: a later procedure body would otherwise overwrite
+   this init's table slot on emission). -1 when full. *)
   VAR num : INTEGER;
   VAR num : INTEGER;
   BEGIN
   BEGIN
-    IF (maxNum >= 64) OR (nInits > 7) THEN RETURN -1 END;
-    num := VAL(INTEGER, maxNum + 1);
+    IF nInits > 7 THEN RETURN -1 END;
+    num := SymTab.AllocInitNum();
+    IF (num < 1) OR (num > 64) THEN RETURN -1 END;
     initNums[nInits] := num;
     initNums[nInits] := num;
     INC(nInits);
     INC(nInits);
     ProcEntry(num, 0);
     ProcEntry(num, 0);

BIN
MGen.o


+ 52 - 0
Showcase3.LST

@@ -0,0 +1,52 @@
+Listing:
+
+    1  MODULE Showcase3;
+    2  
+    3  (* Step-10 tour: module BEGIN bodies run at startup in declaration order,
+    4     cross-module reads, and calls to the module's own procedures
+    5     (plain and M.-qualified) from inside its init body. *)
+    6  
+    7  MODULE A EXPORT x;
+    8  VAR x : INTEGER;
+    9  BEGIN
+   10    x := 1
+   11  END A;
+   12  
+   13  MODULE B EXPORT y;
+   14  VAR y : INTEGER;
+   15  BEGIN
+   16    y := A.x + 10
+   17  END B;
+   18  
+   19  MODULE C EXPORT cnt, Inc;
+   20  VAR cnt : INTEGER;
+   21  PROCEDURE Inc;
+   22  BEGIN
+   23    cnt := cnt + 5
+   24  END Inc;
+   25  BEGIN
+   26    Inc();
+   27    C.Inc()
+   28  END C;
+   29  
+   30  MODULE D EXPORT val;
+   31  VAR val : INTEGER;
+   32  BEGIN
+   33    val := 42
+   34  END D;
+   35  
+   36  VAR total : INTEGER;
+   37  VAR ExitCode : INTEGER;
+   38  
+   39  BEGIN
+   40    total := 0;
+   41    WriteString("show3");
+   42    C.Inc();
+   43    total := A.x * 100 + B.y + C.cnt + D.val;
+   44    WriteInt(total);
+   45    ExitCode := total
+   46  END Showcase3.
+
+    0 errors
+
+

BIN
Showcase3.MC4


+ 46 - 0
Showcase3.mod

@@ -0,0 +1,46 @@
+MODULE Showcase3;
+
+(* Step-10 tour: module BEGIN bodies run at startup in declaration order,
+   cross-module reads, and calls to the module's own procedures
+   (plain and M.-qualified) from inside its init body. *)
+
+MODULE A EXPORT x;
+VAR x : INTEGER;
+BEGIN
+  x := 1
+END A;
+
+MODULE B EXPORT y;
+VAR y : INTEGER;
+BEGIN
+  y := A.x + 10
+END B;
+
+MODULE C EXPORT cnt, Inc;
+VAR cnt : INTEGER;
+PROCEDURE Inc;
+BEGIN
+  cnt := cnt + 5
+END Inc;
+BEGIN
+  Inc();
+  C.Inc()
+END C;
+
+MODULE D EXPORT val;
+VAR val : INTEGER;
+BEGIN
+  val := 42
+END D;
+
+VAR total : INTEGER;
+VAR ExitCode : INTEGER;
+
+BEGIN
+  total := 0;
+  WriteString("show3");
+  C.Inc();
+  total := A.x * 100 + B.y + C.cnt + D.val;
+  WriteInt(total);
+  ExitCode := total
+END Showcase3.

+ 7 - 0
SymTab.def

@@ -248,6 +248,13 @@ PROCEDURE EnterProc (name: ARRAY OF CHAR): BOOLEAN;
    next proc number. FALSE on duplicate or table full. Sets the
    next proc number. FALSE on duplicate or table full. Sets the
    current-proc context for following EnterParam/FwdParam calls. *)
    current-proc context for following EnterParam/FwdParam calls. *)
 
 
+PROCEDURE AllocInitNum (): INTEGER;
+(* Reserves the next proc number for a module BEGIN init body
+   (step 10). Init bodies share the procedure number pool so an
+   init emitted before a later procedure's body can never steal
+   that procedure's number. The slot has no symbol entry and no
+   signature; -1 when the pool is full. *)
+
 PROCEDURE IsForward (name: ARRAY OF CHAR): BOOLEAN;
 PROCEDURE IsForward (name: ARRAY OF CHAR): BOOLEAN;
 (* TRUE if the visible entry is a forward-declared procedure
 (* TRUE if the visible entry is a forward-declared procedure
    whose body is still pending. *)
    whose body is still pending. *)

+ 21 - 0
SymTab.mod

@@ -1075,6 +1075,27 @@ PROCEDURE EnterProc (name: ARRAY OF CHAR): BOOLEAN;
     RETURN TRUE
     RETURN TRUE
   END EnterProc;
   END EnterProc;
 
 
+PROCEDURE AllocInitNum (): INTEGER;
+(* Reserves the next proc number for a module BEGIN init body.
+   Shares the nProcs pool with EnterProc (same parse-order
+   monotonic counter), so init bodies and procedure bodies can
+   never own the same table slot regardless of emission order.
+   The procs[] entry is initialized like a parameterless proper
+   procedure so 1..nProcs sweeps (e.g. AnyForward) stay clean. *)
+  BEGIN
+    IF nProcs >= MaxProcs THEN RETURN -1 END;
+    INC(nProcs);
+    procs[nProcs].ret := InvalidType;
+    procs[nProcs].fret := InvalidType;
+    procs[nProcs].fwd := FALSE;
+    procs[nProcs].everFwd := FALSE;
+    procs[nProcs].fHead := -1;
+    procs[nProcs].fTail := -1;
+    procs[nProcs].dHead := -1;
+    procs[nProcs].dTail := -1;
+    RETURN VAL(INTEGER, nProcs)
+  END AllocInitNum;
+
 PROCEDURE IsForward (name: ARRAY OF CHAR): BOOLEAN;
 PROCEDURE IsForward (name: ARRAY OF CHAR): BOOLEAN;
   VAR idx : INTEGER;
   VAR idx : INTEGER;
   BEGIN
   BEGIN

BIN
SymTab.o


+ 16 - 0
docs/summary_step10.md

@@ -48,3 +48,19 @@ exactly one error: `b_bad_ret` 232, `b_bad_nest` 230, `b_bad_dup` 200.
 
 
 `DEFINITION`/`IMPLEMENTATION` split (the remaining big item), `WITH`-field
 `DEFINITION`/`IMPLEMENTATION` split (the remaining big item), `WITH`-field
 `VAR` actuals, `CHAR`/`BOOLEAN` tails as `VAR` actuals, open-to-open.
 `VAR` actuals, `CHAR`/`BOOLEAN` tails as `VAR` actuals, open-to-open.
+
+## Post-step fix — init/proc table-slot collision (new commit on top)
+
+Showcase3 (`A.x*100 + B.y + C.cnt + D.val` = 168) exposed a real bug:
+with an early plain init plus a later procedure-owning module, the run
+printed 72 (`A.x` = 0, `C.cnt` = 15). Root cause: `ModInitBegin` took
+`maxNum+1` from emission order while `SymTab` numbers procedures from a
+separate declaration-order pool — so module A's init stole `C.Inc`'s
+number 1, and `ProcEntry(1)` for `Inc` overwrote the init's table cell
+(startup then called `Inc` instead of A's init: 5 + 10 = 15 in `cnt`,
+`x` never assigned). Single-module tests passed only because each
+procedure body was emitted before its module's init. Fix: init numbers
+now come from `SymTab.AllocInitNum()`, sharing the `nProcs` pool
+(`procs[]` entry initialized like a parameterless proper procedure).
+Regression `b_mix` (110) locks the shape; suite 144/144, Showcases
+unchanged (157, 83), Showcase3 prints 168.

+ 1 - 0
run_tests.sh

@@ -277,6 +277,7 @@ echo "=== Step-10 run tests (module BEGIN bodies) ==="
 expect_run b_init.mod 42
 expect_run b_init.mod 42
 expect_run b_order.mod 111
 expect_run b_order.mod 111
 expect_run b_call.mod 15
 expect_run b_call.mod 15
+expect_run b_mix.mod 110
 
 
 echo "=== Step-10 rejection tests ==="
 echo "=== Step-10 rejection tests ==="
 expect_fail b_bad_ret.mod "bad RETURN"
 expect_fail b_bad_ret.mod "bad RETURN"

+ 29 - 0
tests/b_mix.LST

@@ -0,0 +1,29 @@
+Listing:
+
+    1  MODULE BMix;
+    2  (* Init body in an earlier module + procedure-owning module with its
+    3     own init later: the early init must not steal the procedure's
+    4     proc-table slot (regression: A.x read 0, C.cnt read 15). *)
+    5  MODULE A EXPORT x;
+    6  VAR x : INTEGER;
+    7  BEGIN
+    8    x := 1
+    9  END A;
+   10  MODULE C EXPORT cnt, Inc;
+   11  VAR cnt : INTEGER;
+   12  PROCEDURE Inc;
+   13  BEGIN
+   14    cnt := cnt + 5
+   15  END Inc;
+   16  BEGIN
+   17    Inc();
+   18    C.Inc()
+   19  END C;
+   20  VAR ExitCode : INTEGER;
+   21  BEGIN
+   22    ExitCode := A.x * 100 + C.cnt
+   23  END BMix.
+
+    0 errors
+
+

+ 23 - 0
tests/b_mix.mod

@@ -0,0 +1,23 @@
+MODULE BMix;
+(* Init body in an earlier module + procedure-owning module with its
+   own init later: the early init must not steal the procedure's
+   proc-table slot (regression: A.x read 0, C.cnt read 15). *)
+MODULE A EXPORT x;
+VAR x : INTEGER;
+BEGIN
+  x := 1
+END A;
+MODULE C EXPORT cnt, Inc;
+VAR cnt : INTEGER;
+PROCEDURE Inc;
+BEGIN
+  cnt := cnt + 5
+END Inc;
+BEGIN
+  Inc();
+  C.Inc()
+END C;
+VAR ExitCode : INTEGER;
+BEGIN
+  ExitCode := A.x * 100 + C.cnt
+END BMix.