wcc/cgen: #47 gap-A tuple-in-union tagged-element store (both-stage)

A tuple containing a tagged-union element, used as a union member
(e.g. ((void|size),(void|size),size) | error), loud-stopped in the
cgen return-store: the tuple-in-union store walk had scalar/float/
str/slice element arms but no TY_TAGGED-element arm. A PLAIN
tuple-in-union already worked -- the blocker was the tagged element.

Add the recursive two-level widen arm at both stages
(cg_widen_tagged_store / cgwidentaggedstorebp): for each tagged
element, re-enter the tagged-box store (inner tag@slot+0,
payload@slot+8) at the element's tuple-payload offset, then stamp the
outer tuple tag. Slot strides come from the type table
(roundup8(eu->size)) -- the checker already sizes the shape correctly
(tuple->size measured 40, union box 48; check.c:715-720). The
recursion descends a finite type tree (a tagged element is never a
tuple literal, so it can't re-enter the tuple arm); unsupported
deeper nesting still louds via the existing size/tag guards.

Both stages get the same arm -> byte-id (990-997 green; additive,
bootstrap-neutral). cstage runs the full b1c shape
(construct+return+match-extract) as the runtime reference; wwstage's
store rides on byte-id until gap-B. gap-B (wwstage checker
match-acceptance of the tuple-with-tagged case pattern) is a separate
commit -- wwstage still louds the match honestly at the checker.
Pin 944_tuple_tagged_union_run.
This commit is contained in:
2026-06-06 16:57:42 +09:00
parent 351abb0ab3
commit 6a5bb3efc9
6 changed files with 429 additions and 41 deletions

View File

@@ -20212,21 +20212,19 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
let ttotal: i32 = 0;
let ce: *node = tupsrc.list;
for (ce != nil) {
// #22a (rule 7): a tagged element's box can't
// ride the scalar/wide store arms below —
// pre-guard it silently stored word0. Nested
// tagged-in-tuple-in-union packing is the
// #242/#22b family. Mirrors cstage.
// #47 gap-A: a tagged element rides its OWN
// box (tag + payload, roundup8) per tuple slot
// — NOT one 8B word. Mirror the checker's
// N_TTUPLE accumulation (check.ww:1640-1646);
// the store loop below boxes it recursively
// (two-level widen). Symmetric with cstage.
let ceti: *tinfo = ce.type_: *tinfo;
ceti = tichase(ceti);
if (ceti != nil && ceti.kind == tykind.TY_TAGGED) {
let m22: str = "cgwidentaggedstore: tagged element in a tuple-in-union payload unwired (see #242/#22b)\n";
os.write(2, m22.ptr, m22.len: u64);
os.exit(1);
};
if (nodeisstr(c, ce) || nodeisslice(c, ce)) {
ttotal += (ceti.size: i32 + 7) & ~7;
} else { if (nodeisstr(c, ce) || nodeisslice(c, ce)) {
ttotal += 24;
} else { ttotal += 8; };
} else { ttotal += 8; }; };
ce = ce.next;
};
if (8 + ttotal > slot_sz) {
@@ -20245,6 +20243,24 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
let tfoff: i32 = 0;
let te: *node = tupsrc.list;
for (te != nil) {
// #47 gap-A: a tagged element is its own
// tag+payload box. Recurse so the inner box
// (tag@slot+0, payload@slot+8) is built at the
// element's tuple-payload offset, exactly as a
// top-level tagged-store does — two-level widen
// (inner boxes here, outer tuple tag stamped
// below). slot stride = roundup8(box). Symmetric
// with cstage cg_widen_tagged_store.
let teti: *tinfo = te.type_: *tinfo;
teti = tichase(teti);
if (teti != nil && teti.kind == tykind.TY_TAGGED) {
let ebox: i32 = (teti.size: i32 + 7) & ~7;
cgwidentaggedstore(c, te.type_: *tinfo, te,
"BP", slot_off + 8 + tfoff, ebox);
tfoff += ebox;
te = te.next;
continue;
};
let isflt: bool = isfloattype(c, te);
let wide: bool = nodeisstr(c, te) || nodeisslice(c, te);
let esz: i32 = 8;

View File

@@ -4071,21 +4071,19 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
let ttotal: i32 = 0;
let ce: *node = tupsrc.list;
for (ce != nil) {
// #22a (rule 7): a tagged element's box can't
// ride the scalar/wide store arms below —
// pre-guard it silently stored word0. Nested
// tagged-in-tuple-in-union packing is the
// #242/#22b family. Mirrors cstage.
// #47 gap-A: a tagged element rides its OWN
// box (tag + payload, roundup8) per tuple slot
// — NOT one 8B word. Mirror the checker's
// N_TTUPLE accumulation (check.ww:1640-1646);
// the store loop below boxes it recursively
// (two-level widen). Symmetric with cstage.
let ceti: *tinfo = ce.type_: *tinfo;
ceti = tichase(ceti);
if (ceti != nil && ceti.kind == tykind.TY_TAGGED) {
let m22: str = "cgwidentaggedstore: tagged element in a tuple-in-union payload unwired (see #242/#22b)\n";
os.write(2, m22.ptr, m22.len: u64);
os.exit(1);
};
if (nodeisstr(c, ce) || nodeisslice(c, ce)) {
ttotal += (ceti.size: i32 + 7) & ~7;
} else { if (nodeisstr(c, ce) || nodeisslice(c, ce)) {
ttotal += 24;
} else { ttotal += 8; };
} else { ttotal += 8; }; };
ce = ce.next;
};
if (8 + ttotal > slot_sz) {
@@ -4104,6 +4102,24 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
let tfoff: i32 = 0;
let te: *node = tupsrc.list;
for (te != nil) {
// #47 gap-A: a tagged element is its own
// tag+payload box. Recurse so the inner box
// (tag@slot+0, payload@slot+8) is built at the
// element's tuple-payload offset, exactly as a
// top-level tagged-store does — two-level widen
// (inner boxes here, outer tuple tag stamped
// below). slot stride = roundup8(box). Symmetric
// with cstage cg_widen_tagged_store.
let teti: *tinfo = te.type_: *tinfo;
teti = tichase(teti);
if (teti != nil && teti.kind == tykind.TY_TAGGED) {
let ebox: i32 = (teti.size: i32 + 7) & ~7;
cgwidentaggedstore(c, te.type_: *tinfo, te,
"BP", slot_off + 8 + tfoff, ebox);
tfoff += ebox;
te = te.next;
continue;
};
let isflt: bool = isfloattype(c, te);
let wide: bool = nodeisstr(c, te) || nodeisslice(c, te);
let esz: i32 = 8;

View File

@@ -20212,21 +20212,19 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
let ttotal: i32 = 0;
let ce: *node = tupsrc.list;
for (ce != nil) {
// #22a (rule 7): a tagged element's box can't
// ride the scalar/wide store arms below —
// pre-guard it silently stored word0. Nested
// tagged-in-tuple-in-union packing is the
// #242/#22b family. Mirrors cstage.
// #47 gap-A: a tagged element rides its OWN
// box (tag + payload, roundup8) per tuple slot
// — NOT one 8B word. Mirror the checker's
// N_TTUPLE accumulation (check.ww:1640-1646);
// the store loop below boxes it recursively
// (two-level widen). Symmetric with cstage.
let ceti: *tinfo = ce.type_: *tinfo;
ceti = tichase(ceti);
if (ceti != nil && ceti.kind == tykind.TY_TAGGED) {
let m22: str = "cgwidentaggedstore: tagged element in a tuple-in-union payload unwired (see #242/#22b)\n";
os.write(2, m22.ptr, m22.len: u64);
os.exit(1);
};
if (nodeisstr(c, ce) || nodeisslice(c, ce)) {
ttotal += (ceti.size: i32 + 7) & ~7;
} else { if (nodeisstr(c, ce) || nodeisslice(c, ce)) {
ttotal += 24;
} else { ttotal += 8; };
} else { ttotal += 8; }; };
ce = ce.next;
};
if (8 + ttotal > slot_sz) {
@@ -20245,6 +20243,24 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
let tfoff: i32 = 0;
let te: *node = tupsrc.list;
for (te != nil) {
// #47 gap-A: a tagged element is its own
// tag+payload box. Recurse so the inner box
// (tag@slot+0, payload@slot+8) is built at the
// element's tuple-payload offset, exactly as a
// top-level tagged-store does — two-level widen
// (inner boxes here, outer tuple tag stamped
// below). slot stride = roundup8(box). Symmetric
// with cstage cg_widen_tagged_store.
let teti: *tinfo = te.type_: *tinfo;
teti = tichase(teti);
if (teti != nil && teti.kind == tykind.TY_TAGGED) {
let ebox: i32 = (teti.size: i32 + 7) & ~7;
cgwidentaggedstore(c, te.type_: *tinfo, te,
"BP", slot_off + 8 + tfoff, ebox);
tfoff += ebox;
te = te.next;
continue;
};
let isflt: bool = isfloattype(c, te);
let wide: bool = nodeisstr(c, te) || nodeisslice(c, te);
let esz: i32 = 8;