Tag v3-class-lowering. Suite 141/141; fixpoint OK
(image 2,188,065 bytes).
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:
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 *)
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.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.)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).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).VIRTUAL is accepted and dispatched statically for now
(no vtable yet).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.VIRTUAL (currently static).Add(a) rather than obj.Add(a)); call through an object.BEGIN … END initialisation body (still 230).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.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).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.