|
@@ -287,22 +287,48 @@ cases (`subrange4`/`7`/`11`, `shift3`/`4`, `constsize2`, `setoverflow`,
|
|
|
`setconst3`) are **not standard Modula-2** — only a `POINTER`'s base type
|
|
`setconst3`) are **not standard Modula-2** — only a `POINTER`'s base type
|
|
|
may be declared later.
|
|
may be declared later.
|
|
|
|
|
|
|
|
-## 10. Finding while building `showcase24` (generics)
|
|
|
|
|
|
|
+## 10. Finding while building `showcase24` (generics) — FIXED
|
|
|
|
|
|
|
|
-A **generic module may not be instantiated twice with type arguments
|
|
|
|
|
|
|
+A **generic module could not be instantiated twice with type arguments
|
|
|
from different families** in one session. With
|
|
from different families** in one session. With
|
|
|
|
|
|
|
|
```mod
|
|
```mod
|
|
|
GENERIC DEFINITION MODULE GStack (T : TYPE);
|
|
GENERIC DEFINITION MODULE GStack (T : TYPE);
|
|
|
-TYPE NodeRec = RECORD val : T; next : Node END; ...
|
|
|
|
|
|
|
+TYPE Node = POINTER TO NodeRec;
|
|
|
|
|
+ NodeRec = RECORD val : T; next : Node END; ...
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-`CharStack = GStack(CHAR)` compiles **alone**, and `GStack(INTEGER)`
|
|
|
|
|
-works; but `GStack(INTEGER)` **plus** `GStack(CHAR)` fails on the second
|
|
|
|
|
-instantiation (`incompatible assignment` at `n^.val := v`). The
|
|
|
|
|
-`INTEGER`+`CARDINAL` pair happens to pass (both `ClInt`, so the wrong
|
|
|
|
|
-binding still type-checks), which suggests the instantiations **share
|
|
|
|
|
-the formal `T`'s binding** instead of getting independent substitutions.
|
|
|
|
|
-Repro: `/tmp`-style five-file session (generic def/mod, two
|
|
|
|
|
-instantiations, a program) — see `showcase24` (which therefore uses one
|
|
|
|
|
-`GStack` instantiation plus the two-parameter `GPair(K,V)`).
|
|
|
|
|
|
|
+`CharStack = GStack(CHAR)` compiled **alone**, and `GStack(INTEGER)`
|
|
|
|
|
+worked; but `GStack(INTEGER)` **plus** `GStack(CHAR)` failed on the
|
|
|
|
|
+second instantiation (`incompatible assignment` at `n^.val := v`). The
|
|
|
|
|
+`INTEGER`+`CARDINAL` pair happened to pass (both `ClInt`, so the wrong
|
|
|
|
|
+binding still type-checked).
|
|
|
|
|
+
|
|
|
|
|
+**Root cause (2026-10-19).** Not the formal `T` binding: each instance
|
|
|
|
|
+re-binds `T` in its own module scope correctly. The fault was in
|
|
|
|
|
+**lowering**, not parsing. After a unit's `END`, `SymTab.EndUnit` resets
|
|
|
|
|
+`curScope` to `globScope`, and `Lower` re-emits the unit from the AST in
|
|
|
|
|
+that scope. To make the unit's names resolvable there,
|
|
|
|
|
+`Lower.MaterializeGlobals` copied the unit's **types** into `globScope`.
|
|
|
|
|
+For a generic instance that leaked `<Instance>.NodeRec` into the global
|
|
|
|
|
+scope. When the second instance's definition was re-parsed, its `TYPE
|
|
|
|
|
+Node = POINTER TO NodeRec` hit `ForwardType`, whose `Find("NodeRec")`
|
|
|
|
|
+walked up to `globScope` and found the **first** instance's record — so
|
|
|
|
|
+`Node` (and every `n^.val` through it) resolved to the first argument,
|
|
|
|
|
+while the procedure parameters used the second. A forward pointer base
|
|
|
|
|
+is what made the leak observable (direct records worked).
|
|
|
|
|
+
|
|
|
|
|
+**Fix.** `Lower` now re-emits each unit in its **own module scope**
|
|
|
|
|
+(`SymTab.EnterLowerScope`/`LeaveLowerScope` around `LowerOneUnit`, and
|
|
|
|
|
+around the `ExitCode` probe in `EndSession`), so nothing needs to be
|
|
|
|
|
+copied into `globScope` and no type leaks between units. Because the
|
|
|
|
|
+nested-module scope is popped before lowering, a nested module's
|
|
|
|
|
+FROM-imports are re-materialised in the enclosing scope by the new
|
|
|
|
|
+`Lower.ReenterImports` (mirroring the existing `ReenterGlobals`).
|
|
|
|
|
+
|
|
|
|
|
+Regression: `compiler/tests/g_twice{,i,c,p}` — one `GTwice(T)` generic
|
|
|
|
|
+instantiated as `GTwiceInt = GTwice(INTEGER)` **and** `GTwiceChar =
|
|
|
|
|
+GTwice(CHAR)`, both exercised at run time (`GTwiceProg`, exit code 42).
|
|
|
|
|
+`showcase24` can now use two instantiations if wanted.
|
|
|
|
|
+Repro during diagnosis: a five-file session (generic def/mod, two
|
|
|
|
|
+instantiations, a program).
|