# Showcase 23 — generic doubly linked list (2026-10-16) Branch **`resolve-check`**, tree clean. Suite **218/218**; fixpoint **OK (3,711,046 bytes)**; corpus **372 OK / 0 crash**. ## The showcase `showcase23.mod` builds a doubly linked list with the generic module `DList (T : TYPE)` instantiated as `IntList = DList(INTEGER)`: ```modula2 GENERIC DEFINITION MODULE DList (T : TYPE); TYPE Node = POINTER TO NodeRec; NodeRec = RECORD val : T; prev, next : Node; END; List = RECORD head, tail : Node; count : INTEGER; END; PROCEDURE Init(VAR l : List); PROCEDURE Append(VAR l : List; v : T); PROCEDURE Prepend(VAR l : List; v : T); PROCEDURE Count(VAR l : List) : INTEGER; PROCEDURE Get(VAR l : List; i : INTEGER) : T; PROCEDURE First / Last(VAR l : List) : T; PROCEDURE Reverse(VAR l : List); END DList. ``` Run wired as `expect_run_files_out Showcase23 35 "list: 5 10 20"`: ``` list: 5 10 20 reversed: 20 10 5 sum=35 ``` Files: `showcase23lib.def/.mod` (the generic), `showcase23int.def/.mod` (`IntList = DList(INTEGER)`), `showcase23.mod` (the program). ## Bug found and fixed (general, not generic-specific) While writing it, `WHILE i < IntList.Count(l) DO … END` returned 0. The cause was in `QbeGen.RecItems`: a record's **static data initialiser** emitted `w 0` (4 bytes) for *every* scalar field, including **pointers** and `LONGINT`/proc (8-byte) fields, while the record layout gives them 8 bytes. A global of such a record was therefore **under-sized**, and the next globals overlapped and clobbered its tail — here `List.count`. ```modula2 VAR g : L; a : INTEGER; (* L = RECORD head, tail : P; n : INTEGER END *) g.n := 5; a := 37; (* a clobbered g.n, at offset 16 *) ``` Fixed by emitting `l 0` for `ClPtr`/`ClLong`/`ClProc` fields (real/char/ set/array/record were already size-correct). Regression: `t_recptr` (exits 42 only when `g.n` survives). `$g` now emits `{ l 0, l 0, w 0 }` instead of `{ w 0, w 0, w 0 }`. ## Limitations met (not fixed — recorded) 1. **Arithmetic on a type formal in a template.** `Sum` over `T` (`s : T; s := s + n^.val`) is `211` at *template* parse: an unbound formal alias is class `ClPtr` (pointer-like), so numeric checks fail. Workaround used: do the arithmetic in the concrete program (`acc := acc + Get(l, i)`). A principled fix is to let checks skip an *unbound type formal* (a distinct form from an opaque alias). 2. **Proc-typed formals in a DEFINITION/IMPLEMENTATION pair** trip a false `235` ("implementation does not match definition"): proc types are not deduped, so the def's and impl's constructed proc type have different `TypeIndex`es and the signature snapshot compares unequal. Not used in the showcase for that reason. Both are follow-ups; neither affects the showcase. ## Verify ```sh cd .../m2compiler-V3/compiler ./M2 ../stdlib/sysio.def ../stdlib/sysio.mod \ tests/showcase23lib.def tests/showcase23lib.mod \ tests/showcase23int.def tests/showcase23int.mod tests/showcase23.mod ```