# Generics — closing summary (2026-10-16) Branch **`resolve-check`**, tree clean. Suite **214/214**; fixpoint **OK (3,710,201 bytes)**; corpus **372 OK / 0 crash**. ## Supported today ISO 10514-2 **refinement generics**, instantiated by re-parsing the generic source with a substitution scope, driven by the session driver. | Feature | Syntax | Notes | | --- | --- | --- | | Type parameters | `MODULE Box (T : TYPE)` | `Box(INTEGER)` | | Value parameters | `MODULE Buf (size : CARDINAL)` | `Buf(10)`; template binds a placeholder | | Procedure parameters | `MODULE Sort (E : TYPE; Compare : PROCEDURE(a,b : E) : INTEGER)` | actual is `Mod.Proc` | | Constraints | `MODULE OrdBox (T : TYPE (ORDINAL))` | `ORDINAL`, `INTEGER`, `REAL`, `POINTER`, `SCALAR` | | Multiple parameters | `MODULE Pair (T : TYPE; U : TYPE)` | both bound | | Nested generics | `MODULE WrapB = Wrap(IntBox.Box)` | an instance's exported type as an actual | | Def + impl | `DEFINITION`/`IMPLEMENTATION MODULE X = G(...)` | both required; template not emitted | Constraints and actual-kind are checked at instantiation (`233` on mismatch). Suite: `GenericProg`, `PairProg`, `BufProg`, `g_badbuf`, `SortProg`, `OrdProg`, `g_realbox`, `GWrapProg`. ## Mechanism (recap) - Driver records each generic's def/impl file; on a refinement re-opens it, sets the instantiation context, re-parses and lowers the unit. - Formals bind via `SymTab.EnterTypeParamBound` (types), `SetSymVal` on a `CONST` (values), or `SymTab.AliasProc` + `Lower.AliasProc` (procedures; `ProcBaseByUid` fixes mangling after the instance scope is popped). - Qualified actuals (`Mod.Name`) are classified as proc or type, which is how both procedure parameters and nested generics reach a name without an import. ## Deliberately not done - **Instance dedup** — not needed: each refinement declares its own module (`BoxA = OrdBox(INTEGER)`, `BoxB = OrdBox(INTEGER)` coexist cleanly; verified). The only "same instance twice" case is a refinement's DEFINITION and IMPLEMENTATION, which must instantiate the generic's def and impl respectively. - **`GENERIC CLASS`** — **already achievable through generic modules** (verified, suite `GCBoxProg`): declare the class in the generic module's `TYPE` block, implement it in the generic impl, and instantiate the module: ```modula2 GENERIC DEFINITION MODULE GBox (T : TYPE); TYPE CLASS Box; v : T; PROCEDURE Put(x : T); PROCEDURE Get() : T; END Box; END GBox. * GBox's CLASS IMPLEMENTATION Box holds the method bodies; DEFINITION/IMPLEMENTATION MODULE GCInt = GBox(INTEGER); (* program: *) b : GCInt.Box; b.Put(42); ExitCode := b.Get() ``` So **no standalone `GENERIC CLASS` syntax is required** for the capability. It also composes with the increment-3 features: suite `GStkProg` instantiates a class whose **array field is sized by a value parameter** and whose element is the type parameter: ```modula2 GENERIC DEFINITION MODULE GStk (T : TYPE; cap : CARDINAL); TYPE CLASS Stk; a : ARRAY [0..cap] OF T; PROCEDURE Capacity() : INTEGER; END Stk; END GStk. (* class impl: *) PROCEDURE Capacity() : INTEGER; BEGIN RETURN HIGH(a) END Capacity; DEFINITION/IMPLEMENTATION MODULE GIntStk = GStk(INTEGER, 4); (* HIGH(a)=4 *) ``` Small print for the pattern: a class must be declared **inside a `TYPE` block** (a bare `CLASS` item is not accepted in a DEFINITION unit's item list); the `CLASS IMPLEMENTATION` block is a `DeclSeq` item, so it needs a **trailing `;`**; and a class impl's `BEGIN` initialiser cannot assign a *field* (only module globals) — so field state must be set by a method, as above. ## Résumé ```sh cd .../m2compiler-V3/compiler ./M2 tests/g_ordbox.def tests/g_ordbox.mod tests/g_intbox.def \ tests/g_intbox.mod tests/g_wrap.def tests/g_wrap.mod \ tests/g_wrapb.def tests/g_wrapb.mod tests/g_wrapprog.mod # GWrapProg -> 42 ```