Tag v3-inline-array-fields. Main suite 111/111; full compiler source
set still compiles under V3 and links.
V3 stored a record's array fields as 8-byte pointers to static
descriptors, initialised only for static records. Heap records
(ALLOCATE/NEW) never initialised them, so SymTab.RawEnter's
Assign(node^.name, …) dereferenced garbage → the self-compiled
compiler crashed at startup.
Standard Modula-2 (and gm2) store record array fields inline, so V3 now does too.
ArrObjSize = 8 + count*elemBytes), not 8. TypeSizeD's
record branch and ComputeOffsets use FieldSize; new
ArrObjSize/FieldSize; ElemBytes moved earlier.ArrBodyItems (inline descriptor contents:
l <n>, z <bytes> or l <n>, l $sub_i, …) + ArrData (wraps it and
emits nested $name_i). RecItems emits array fields inline;
RecStatics only emits sub-descriptors for nested array fields.Design's .field now passes the field address
for array fields (was ElemLoad of the pointer).CopyArray(fieldAddr, …).ALLOCATE(x, TSIZE(T)) → NEW(x) (typed allocation runs
InitHeap). FROM Storage IMPORT ALLOCATE kept for gm2's NEW.Running the self-compiled compiler still aborts, but now in a
reproducible, understood place: the compiler's string helpers scan an
argument for a trailing NUL (SymTab.Assign, Equal, StrLen, …),
while V3 strings are count + bytes with no terminator and trap when
indexed past the count. Minimal repro:
PROCEDURE Copy (VAR d: ARRAY OF CHAR; s: ARRAY OF CHAR);
BEGIN ... WHILE (i < HIGH(d)) AND (s[i] # CHR(0)) DO ... END ... END Copy;
Copy(p^.n, "hi"); (* s[2] out of bounds -> abort *)
gm2 strings are NUL-terminated so the sources work there. Options:
z (n+1), literals … , b 0). Keeps the
sources unchanged; broad but mechanical.Then: the rest of the bootstrap (cross-stage FileIO, fixpoint.sh).
compiler/src/{M2.atg,SymTab.mod,QbeGen.mod,M2.lst}.