Tag v3-variant-records. Suite 132/132; fixpoint OK
(image 2,135,590 bytes).
RECORD
id : INTEGER; (* plain fields *)
; CASE k : Kind OF (* the variant part *)
circle : radius : INTEGER;
| square : side : INTEGER;
| rect : w, h : INTEGER;
END
END
RecordType = RECORD [ RecItem { ";" [ RecItem ] } ] END, where
RecItem is a plain RecField or a CaseField. Because a variant
part starts with the CASE keyword it is unambiguously
distinguishable from a field (which starts with an identifier), so
the grammar stays LL(1).CaseField = CASE tag : Type OF RecFieldList { "|" RecFieldList } END.
Each RecFieldList is VarLabel [ ".." VarLabel ] ":" RecField …;
labels are parsed but not interpreted (the layout overlays
regardless).SymTab)SetVariantTag, and the record is flagged with MarkVariant.ComputeOffsets: fields declared before the tag pack normally;
the tag gets the next slot; every branch field overlays in the
region after the tag. (Ranks are reverse-declaration, so "before the
tag" is ord < tag^.ord.)TypeSizeD: plainSize + tagSize + max(branch field sizes).Example (Kind, then Shape): id@0, k@4, radius/side/w,h
all @8; size 12. Branch fields share storage, so assigning w then
h leaves h, and switching k reuses the slot — exactly the
variant-record contract.
ResetFields clears a record's pending-field buffer and layout at each
| so later branches re-register; field names stay visible for
WITH/.field access.
WITH over a variant's fields works (verified).t_variant.mod (exit 31): plain field + tag + three branches, overlay
(w/h share), branch switch (square reuses the slot).
compiler/src/M2.atg (RecItem/CaseField/VarLabel,
RecFieldList), compiler/src/SymTab.def/.mod
(IsOrdinal, SetVariantTag, MarkVariant, IsVariant,
ResetFields, ComputeOffsets, TypeSizeD),
compiler/src/compiler.frm (message 236 bad variant record),
compiler/tests/t_variant.mod, compiler/run_tests.sh,
docs/features.md.