summary_generics-closed-2026-10-16.md 3.9 KB

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:
  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:

  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é

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