# Step: Clarion CLASS lowering — design (not yet implemented) Status: **not started** (scoped here). Suite **137/137**; fixpoint OK. ## Current state Classes *parse, scope and check* — fields, methods, `=` consts, single/multiple parent lists, `VIRTUAL`, and `CLASS IMPLEMENTATION` blocks with nested procedures — but every class/method ends in error **230** and method bodies are suppressed (`QbeGen.SetNoEmit(TRUE)` + `AbortFunc` in `MethodImpl`). See `docs/OOP.txt` for the grammar. ## Goal Make a class a usable **value** with callable methods: ```modula-2 TYPE CLASS Point; x, y : INTEGER; PROCEDURE Set(a, b : INTEGER); PROCEDURE Get() : INTEGER; END Point; CLASS IMPLEMENTATION Point; PROCEDURE Set(a, b : INTEGER); BEGIN x := a; y := b END Set; PROCEDURE Get() : INTEGER; BEGIN RETURN x + y END Get; END Point; VAR p : Point; BEGIN p.Set(3, 4); ExitCode := p.Get() END ``` ## Design sketch 1. **Layout** — a class is a record: fields get declaration-order offsets via the existing `ComputeOffsets`/`FieldOffset` machinery (already shared with `RECORD`; `PushRecord` already accepts `FClass`). `obj.field` then works through the record field path. Single inheritance prepends the parent's fields (offset by the parent's size); multiple parents stay 230. 2. **Receiver** — each method gets a **hidden first parameter** `THIS` of type `POINTER TO `. Inside a method body, a bare field name `f` is an implicit `THIS^.f`; the class scope already holds the fields as `KindField`, so `Design` needs a "current receiver" addressing mode (like `WITH`): push the receiver register at method entry, add `FieldOffset` to it, load/store. 3. **Calls** — `obj.M(args)` binds statically to the method's mangled symbol and passes `ADR obj` as the hidden first argument, then the user arguments. `QbeGen.CallBegin`/`ArgList` gain a flag for the implicit receiver. Non-virtual first; `VIRTUAL` accepts but dispatches statically with a `230`-free path. 4. **Dispatch (later)** — a per-class vtable (one static array of code pointers, parent entries first) stored as a hidden first field; `VIRTUAL` calls load the slot and call indirectly (procedure types + indirect calls already exist from step 8.5). This can be a follow-on increment. 5. **`WITH`-style field binding** — reuse `PushRecord`/`PushWith`; the receiver is the `WITH` base, so `WITH p DO x := 1 END` works for free once (2) lands. ## Acceptance (planned) - The `Point` program above compiles and exits 7. - `obj.field` read/write, method calls with value and `VAR` params, methods returning values. - Single inheritance: child methods see parent fields. - Suite green; fixpoint still byte-identical. ## Risks - `Design`'s addressing is threaded through several productions; adding a receiver mode must not change existing `RECORD`/`WITH` codegen (fixpoint is the guard). - `VIRTUAL`/vtable must not perturb layout for non-virtual classes. ## Files (planned) `compiler/src/M2.atg` (`ClassRest`/`ClassImplRest`/`MethodImpl` lowering, `Design` receiver mode, call-site receiver), `compiler/src/SymTab.def`/`.mod` (receiver scope, parent field offsets, vtable), `compiler/src/QbeGen.def`/`.mod` (receiver param, vtable emission, indirect `VIRTUAL` calls), `compiler/tests/t_class*.mod`, `docs/OOP.txt`.