summary_generics-inc3-2026-10-16.md 4.2 KB

Session summary — 2026-10-16 (c): generics increment 3

Branch resolve-check, tree clean. Suite 213/213; fixpoint OK (3,706,134 bytes); corpus 372 OK / 0 crash.

Tags: v3-generics-value-params, v3-generics-inc3, v3-generics-proc-params, v3-generics-constraints.

What inc3 added

ISO 10514-2 refinement generics now accept value and procedure parameters, an explicit constraint model, and actual-kind constraints.

1. Value (constant) parameters

GENERIC DEFINITION MODULE Buf (size : CARDINAL);
TYPE T = ARRAY [0..size] OF CHAR;
END Buf.
DEFINITION MODULE Buf10 = Buf(10); END Buf10.

GenericFormal collects its names, then takes : TYPE (type formal) or : TypeIdent (value formal). Actual accepts an integer, charConst, or an identifier. Refinement actuals carry a kind (0 type / 1 value / 2 proc) and a value text (refKind/refVal, instKind/instVal + accessors). A value formal binds to a CONST with the actual value at instantiation; in the abstract template it is a CONST placeholder ("0") — templates are never lowered, so only instances matter. Verified with two instances of different sizes.

2. Actual-kind constraints

At instantiation the actual's kind must match the formal: a type formal needs a type actual, a value formal a value actual, else 233 (g_badbuf).

3. Procedure parameters

GENERIC DEFINITION MODULE Sort
  (Element : TYPE; Compare : PROCEDURE(a, b : Element) : INTEGER);
PROCEDURE Smaller(x, y : Element) : Element;
END Sort.
DEFINITION/IMPLEMENTATION MODULE IntSort = Sort(INTEGER, Cmp.IntCompare);

The formal's type is an inline ProcType; the actual is a qualified procedure name (the refinement parses actuals before any import, so qualification is the only way to reach a proc without one). In the template the formal is a proc-typed callable (the body checks, via an indirect call there); at instantiation it is aliased to the actual:

  • SymTab.AliasProc(formal, mod, actual) — clones the actual's SymNode under the formal's name (keeps uid/mod/sym).
  • Lower.AliasProc(...) — a scope-neutral proc-table entry, because Lower resolves call targets from its own table, not SymTab (the first attempt emitted call $Compare, an unresolved symbol).
  • SymTab.ProcBaseByUid + a uid→base map, so QbeGen.Mangled recovers the base name after the instance scope has been popped.

Refinement forms also accept { Import } now. Verified end-to-end (Smaller(7,3) through IntSort, IntCompare in a separate module, returns 3).

4. Constraint model

A type formal may carry an explicit constraint:

GENERIC DEFINITION MODULE OrdBox (T : TYPE (ORDINAL));
TYPE Box = ARRAY [0..3] OF T;
END OrdBox.
DEFINITION MODULE IntBox = OrdBox(INTEGER);   (* ok *)
DEFINITION MODULE RealBox = OrdBox(REAL);     (* rejected: 233 *)

GenericFormal's TYPE branch takes an optional ( name ); ConsCode maps ORDINAL/INTEGER/REAL → 0/1/2/3 and ConsOk tests the actual (ORDINAL → SymTab.IsOrdinal; INTEGER → ClInt; REAL → ClReal). The constraint is read from the formal's source on both passes (template and re-parse), so no extra state is needed. Verified: OrdBox(INTEGER) compiles and runs (b[0]+b[3] → 42); OrdBox(REAL) is rejected.

Verification

  • Suite 213/213 (BufProg, g_badbuf, SortProg, OrdProg, g_realbox); forward tests, two-phase gates, and exit-code gates all green.
  • Fixpoint OK (3,706,134 bytes).
  • Corpus unchanged: 372 OK, 0 crash (generics are not in the corpus).

Remaining in the generics thread

  • GENERIC CLASS; instance dedup; nested generics as actuals; further constraint names (POINTER, SCALAR).

Resume

cd .../m2compiler-V3/compiler
./M2 tests/g_cmp.def tests/g_cmp.mod tests/g_sort.def tests/g_sort.mod \
    tests/g_isort.def tests/g_isort.mod tests/g_sortprog.mod   # SortProg -> 3
./M2 tests/g_buf.def tests/g_buf.mod tests/g_buf10.def \
    tests/g_buf10.mod tests/g_bufprog.mod                      # BufProg -> 42
./M2 tests/g_ordbox.def tests/g_ordbox.mod tests/g_intbox.def \
    tests/g_intbox.mod tests/g_ordprog.mod                     # OrdProg -> 42