# Step: Clarion CLASS lowering Tag `v3-class-lowering`. Suite **141/141**; fixpoint **OK** (image **2,188,065 bytes**). ## What "CLASS lowering" means Clarion (a 1990s PC Modula-2 dialect) added OOP to classic Modula-2 — the syntax is preserved in `docs/OOP.txt`. "Lowering" is translating a high-level construct into what the backend already understands. Previously V3 *parsed and checked* classes but ended every one in error 230 and emitted no method bodies ("declared + checked only"). This step makes **non-virtual** classes fully executable: ```modula-2 TYPE CLASS Point; x, y : INTEGER; PROCEDURE Set(a, b : INTEGER); PROCEDURE Sum() : INTEGER; END Point; CLASS IMPLEMENTATION Point; PROCEDURE Set(a, b : INTEGER); BEGIN x := a; y := b END Set; PROCEDURE Sum() : INTEGER; BEGIN RETURN x + y END Sum; END Point; VAR p : Point; BEGIN p.Set(3, 4); ExitCode := p.Sum() END (* -> 7 *) ``` ## How it works 1. **Layout** — a class is a record: `ComputeOffsets` gives fields declaration-order offsets. Single inheritance lays the child's own fields after the parent's (`baseOff`), and `FindField` / `FieldExists` / `FieldOffset` walk up the parent chain, so `obj.field` works through the existing record path. 2. **Receiver (`THIS`)** — each method gets a **hidden first `VAR` parameter `THIS`** of the class type. At the top of the body it is pushed as the current `WITH` base, so a bare field name resolves to `THIS^.field` — reusing all the `WITH` addressing machinery. (`SymTab.EnterThisParam`, `QbeGen.ThisBase`/`PushWith`.) 3. **Calls** — `obj.M(args)` binds statically: `Design` recognises a class method, arms the receiver (`QbeGen.ArmRecv`), and `ArgList` emits the call with `obj`'s address as the hidden first argument (`CallBegin` prepends the armed receiver). Formal types come from `ClassMethodParamType`/`…IsVar` (user index 0 skips `THIS`). 4. **Implementation binding** — `ResumeMethod` reuses the method symbol declared in the class scope (so the call-site uid matches the definition's, and `MethUid` reports it since the class scope is detached by then). 5. **`VIRTUAL`** is accepted and **dispatched statically** for now (no vtable yet). ## Supported / not yet - ✅ Fields (`obj.field`, `WITH obj`, inherited fields), methods (value + `VAR` formals, value-returning), single inheritance (inherited fields and methods; a child method sees both), classes in a `DEFINITION`/`IMPLEMENTATION` pair. - ⏸ **Dispatch** for `VIRTUAL` (currently static). - ⏸ **Bare sibling-method calls** inside a method body (`Add(a)` rather than `obj.Add(a)`); call through an object. - ⏸ A class's `BEGIN … END` initialisation body (still 230). - ⏸ Multiple parents (still 230, as designed). ## Implementation notes - V3 is **single-pass** (declaration before use), so the class helpers had to be ordered: `Resolve`/`ParentOf`/`PushProc` early, `LayoutClass` after `ComputeOffsets`, `EnterThisParam` after `EnterParam`. (gm2 tolerates forward references; the fixpoint's V3 stage is the guard.) - `MethodImpl` now emits the body: `EndFuncHeader` (was missing, so bodies leaked to top level), the receiver binding, `Block`, then `PopWith`/`CloseProc`/`EndFunc`. - `EnterThisParam` clears the pending-formal buffer so `THIS` isn't emitted twice by `ParamSection`. ## Tests - `t_classmethod.mod` (7): fields via the receiver, value + `VAR` formals, value-returning method, `VIRTUAL` static dispatch. - `t_classinherit.mod` (7): inherited method + inherited field seen from a child method. - `TClassDefProg` (8): a class declared in a DEFINITION used as data. - `t_class.mod` still rejects (its `BEGIN` init body is 230). ## Files `compiler/src/SymTab.def`/`.mod` (parent field inheritance, `ParentOf`/`EnterFieldsOf`, `EnterThisParam`, `ResumeMethod`, `MethUid`, `ClassMethod*`, `LayoutClass`), `compiler/src/QbeGen.def`/`.mod` (`ThisBase`, `ArmRecv`, receiver argument in `CallBegin`), `compiler/src/M2.atg` (`ClassRest`/ `ClassImplRest`/`MethodImpl`, `ProcHeading` receiver + method resume, `Design` method branch, `ActParam`/`ArgList` method formals), `compiler/tests/{t_classmethod,t_classinherit,t_classdef_prog}.mod`, `docs/features.md`.