summary_generics-closed-2026-10-16.md 3.6 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. Standalone syntax would only matter if a class must be instantiated without a whole generic module; the principled form for that is a type refinement (TYPE IntStack = Stack(INTEGER);), which mirrors MODULE X = G(args) at type level but needs the class's CLASS IMPLEMENTATION block re-parsed/renamed per instance — a design of its own. Recommendation: keep the generic-module pattern.

Minor asymmetry noted: a class is accepted as a top-level item in an IMPLEMENTATION/program DeclSeq but not in a DEFINITION unit's item list (only inside a TYPE block) — harmless, but a tidy-up candidate.

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