selfhost/cmd/wcc/cgenutil: tagged-store machinery reads tinfo.params (#68)

cgwidentaggedstore/storebp/cgwidentagremap took a type *node and re-derived
the tagged shape via resolvetagged + N_TTAGGED.list walks. Migrate them onto
the stamped tinfo (the store-side parallel of #66's match-side flip): dst is
now the tagged *tinfo (peel TY_NAMED->du, gate TY_TAGGED), variant lookup +
tag-remap read tinfo.params by typeeq, mirroring cstage cg_widen_tagged_store
/ cg_widen_tag_remap / cg_tag_for_variant (cmd/w6c/cgen.c:1273/1177/503). The
N_CAST widening test flips from surface-name streq to `castu==dt || (castu
tagged && typeeq(castt,dst))` (cgen.c:1295).

Node-form flatvariantidx/taggedvariantindex become thin shims over new tinfo
cores (flatvariantidxt/taggedvariantindext) so node-side callers (cgreturn
cgenstmt:269, pushargs cgenutil:192) are untouched. The 9 cgwidentaggedstore
callers pass node.type_ (each already istaggedtype/slotsize-gated). resolvetagged
is retained for its 7 match-side callers.

Incidentally retires two latent ww-vs-cstage divergences, both byte-id-neutral
on the corpus: the old N_CAST test set castisdst for ANY N_TTAGGED regardless
of type equality (cstage guards on type_eq), and the old remap walked the
UNflattened src.list (cstage walks the flattened params). Unblocks cgassign's
indexvaluetnode drop (#69/#61d). make test 134/134, byte-id 990-997 hold.
This commit is contained in:
2026-05-23 22:57:13 +09:00
parent aec48e8c13
commit 901ddf20b9
5 changed files with 354 additions and 288 deletions

View File

@@ -10767,7 +10767,7 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node) i32 = {
emitline("(BP)\n");
zz += 8;
};
cgwidentaggedstore(c, ptype, arg, "BP", scroff, widensz);
cgwidentaggedstore(c, ptype.type_: *tinfo, arg, "BP", scroff, widensz);
let pp: i32 = widensz - 8;
for (pp >= 0) {
emitline("\tMOVQ\t");
@@ -12732,65 +12732,48 @@ fn rhstargetname(c: *cgen, rhs: *node) str = {
// "any str-shape variant matches an str-typed value".
fn taggedvariantindex(c: *cgen, tagged: *node, rhs: *node) i32 = {
if (tagged == nil) { return -1; };
if (rhs == nil) { return -1; };
// Alias-unwrap: wwstage has no typed AST, so an aliased tagged
// return (`type ft = (i64|str|bool); fn f() ft = ...`) reaches
// here as N_TNAME("ft"), not N_TTAGGED. flatvariantidx and the
// fallback both gate on N_TTAGGED → -1 → caller maps to 0,
// silently emitting `MOVQ $0, AX` for every non-leading variant.
// Cstage's check.c canonicalizes N_TNAME → underlying upfront;
// every wwstage cgen consumer of a type-bearing node has to
// remember this step itself. TODO(#11): a wwstage check pass
// between parse and cgen would replace the per-site unwrap with
// a single canonicalization. Same shape of fix as nodeisstr.
let resolved: *node = resolvetagged(c, tagged);
if (resolved != nil) { tagged = resolved; };
// #66 Phase-N step 3: match the value's stamped type against the
// variant types by typeeq (flatvariantidx), replacing the
// rhstargetname surface-name compare. Untyped/loose values (whose
// .type_ is untyped_* and can't typeeq a concrete variant) return
// -1 here and drop to the str/slice shape scan below — ww has no
// type_assignable to mirror cg_variant_match's untyped-src arm.
let r: i32 = flatvariantidx(c, tagged, rhs);
return taggedvariantindext(c, tagged.type_: *tinfo, rhs);
};
// taggedvariantindext — tinfo-keyed core of taggedvariantindex. Given
// the dst tagged tinfo `du` (NAMED-peeled internally, gated TY_TAGGED)
// and the source value node, returns the 0-based variant index. #68: the
// tagged-store machinery reads tinfo directly (no type node), so the
// node-keyed taggedvariantindex delegates here off `tagged.type_`.
//
// #66 Phase-N step 3: match the value's stamped type against the variant
// types by typeeq (flatvariantidxt), replacing the rhstargetname surface-
// name compare. Untyped/loose values (whose .type_ is untyped_* and can't
// typeeq a concrete variant) return -1 there and drop to the str/slice
// shape scan below — ww has no type_assignable to mirror cg_variant_match's
// untyped-src arm.
fn taggedvariantindext(c: *cgen, du: *tinfo, rhs: *node) i32 = {
if (du == nil) { return -1; };
if (rhs == nil) { return -1; };
let ti: *tinfo = du;
for (ti != nil && ti.kind == tykind.TY_NAMED) { ti = ti.under; };
if (ti == nil) { return -1; };
if (ti.kind != tykind.TY_TAGGED) { return -1; };
let r: i32 = flatvariantidxt(ti, rhs.type_: *tinfo);
if (r >= 0) { return r; };
// Shape fallback: classify rhs as (str, slice, scalar/other) and
// pick the first variant of matching shape. Stands in for cstage's
// type_assignable on an untyped src — the typeeq pass above can't
// match untyped_* against a concrete variant, and the slice axis
// keeps a (u8 | []u8) widen off the leading scalar variant (task
// #19). Walks the spread-flattened list so a `(...inner | str)`
// outer agrees with the inner's str / slice positions.
// #19). tinfo.params is already spread-flattened (#61a — `...inner`
// inlined in declaration order), so the old N_TTAGGED.list spread-
// walk collapses to a flat scan over p.type_.
let wantstr: bool = nodeisstr(c, rhs);
let wantslice: bool = nodeisslice(c, rhs);
let v: *node = tagged.list;
let p: *tparam = ti.params;
let idx: i32 = 0;
for (v != nil) {
let isspread: bool = (v.op == tkind.TK_ELLIPSIS);
if (isspread) {
let inner: *node = v;
if (inner.kind == nkind.N_TNAME) {
let a: *node = aliaslookup(c, inner.str);
if (a != nil) { inner = a; };
};
if (inner != nil) {
if (inner.kind == nkind.N_TTAGGED) {
let iv: *node = inner.list;
for (iv != nil) {
let ivisstr: bool = isstrtype(c, iv);
let ivisslice: bool = isslicetype(c, iv);
if (ivisstr == wantstr && ivisslice == wantslice) { return idx; };
iv = iv.next;
idx += 1;
};
v = v.next;
continue;
};
};
};
let visstr: bool = isstrtype(c, v);
let visslice: bool = isslicetype(c, v);
for (p != nil) {
let vt: *tinfo = p.type_;
let visstr: bool = typeisstr(vt);
let visslice: bool = typeisslice(vt);
if (visstr == wantstr && visslice == wantslice) { return idx; };
v = v.next;
p = p.tnext;
idx += 1;
};
return -1;
@@ -12813,9 +12796,21 @@ fn taggedvariantindex(c: *cgen, tagged: *node, rhs: *node) i32 = {
fn flatvariantidx(c: *cgen, tagged: *node, pat: *node) i32 = {
if (tagged == nil) { return -1; };
if (pat == nil) { return -1; };
let want: *tinfo = pat.type_: *tinfo;
return flatvariantidxt(tagged.type_: *tinfo, pat.type_: *tinfo);
};
// flatvariantidxt — tinfo-keyed core of flatvariantidx: flat 0-based
// index of the variant whose type typeeq's `want`, over the pre-
// flattened tinfo.params chain (#61a). Peels TY_NAMED then gates
// TY_TAGGED. #68: the tagged-store machinery reads tinfo directly and
// has no type node to hand the node-keyed flatvariantidx, so the typeeq
// core lives here; flatvariantidx + taggedvariantindext + cgwidentagremap
// all funnel through it. Mirrors cstage cg_tag_for_variant
// (cmd/w6c/cgen.c:503) + cg_variant_match's both-NAMED ptr-id / typeeq
// arm (:451).
fn flatvariantidxt(tagged: *tinfo, want: *tinfo) i32 = {
if (want == nil) { return -1; };
let ti: *tinfo = tagged.type_: *tinfo;
let ti: *tinfo = tagged;
for (ti != nil && ti.kind == tykind.TY_NAMED) { ti = ti.under; };
if (ti == nil) { return -1; };
if (ti.kind != tykind.TY_TAGGED) { return -1; };
@@ -12874,31 +12869,39 @@ fn flatslicevariantidx(c: *cgen, tagged: *node, elem: *node) i32 = {
// cgwidentagremap — when widening from one tagged union to a wider one,
// rewrite the source's variant tag at slot_off+0 to use the destination's
// variant indices. No-op when src and dst index orders coincide.
// Mirrors cg_widen_tag_remap in cmd/w6c/cgen.c.
fn cgwidentagremap(c: *cgen, dst: *node, src: *node, slot_off: i32) void = {
if (dst == nil) { return; };
if (src == nil) { return; };
if (dst.kind != nkind.N_TTAGGED) { return; };
if (src.kind != nkind.N_TTAGGED) { return; };
//
// #68: both `du` (dst) and `su` (src) are now the tagged tinfos — peel
// TY_NAMED then walk su.params, mapping each source variant to its dst
// index by typeeq (flatvariantidxt). Mirrors cg_widen_tag_remap
// (cmd/w6c/cgen.c:1177) over su->params + cg_tag_for_variant (:503).
fn cgwidentagremap(c: *cgen, du: *tinfo, su: *tinfo, slot_off: i32) void = {
let dt: *tinfo = du;
for (dt != nil && dt.kind == tykind.TY_NAMED) { dt = dt.under; };
if (dt == nil) { return; };
if (dt.kind != tykind.TY_TAGGED) { return; };
let st: *tinfo = su;
for (st != nil && st.kind == tykind.TY_NAMED) { st = st.under; };
if (st == nil) { return; };
if (st.kind != tykind.TY_TAGGED) { return; };
let identity: bool = true;
let v: *node = src.list;
let p: *tparam = st.params;
let idx: i32 = 0;
for (v != nil) {
let di: i32 = cgtagvariantidx(c, dst, v);
for (p != nil) {
let di: i32 = flatvariantidxt(dt, p.type_);
if (di < 0) { di = 0; };
if (di != idx) { identity = false; v = nil; }
else { v = v.next; idx += 1; };
if (di != idx) { identity = false; p = nil; }
else { p = p.tnext; idx += 1; };
};
if (identity) { return; };
let done: str = mklabel(c, "remap_done");
emitline("\tMOVQ\t");
emitoff(slot_off: i64);
emitline("(BP), AX\n");
v = src.list;
p = st.params;
idx = 0;
for (v != nil) {
for (p != nil) {
let next: str = mklabel(c, "remap_next");
let di: i32 = cgtagvariantidx(c, dst, v);
let di: i32 = flatvariantidxt(dt, p.type_);
if (di < 0) { di = 0; };
emitline("\tCMPQ\t$");
emitint(idx: i64);
@@ -12916,7 +12919,7 @@ fn cgwidentagremap(c: *cgen, dst: *node, src: *node, slot_off: i32) void = {
emitline(done);
emitline("\n");
emitlabel(next);
v = v.next;
p = p.tnext;
idx += 1;
};
emitlabel(done);
@@ -13145,7 +13148,7 @@ fn cgloadtaggedfield(c: *cgen, basereg: str, foff: i32, slot_sz: i32) void = {
// tag last.
// - str src: tag@+0, ptr@+8, len@+16.
// - scalar src: tag@+0, value@+8.
fn cgwidentaggedstore(c: *cgen, dst: *node, src: *node,
fn cgwidentaggedstore(c: *cgen, dst: *tinfo, src: *node,
basereg: str, slot_off: i32, slot_sz: i32) void = {
if (streq(basereg, "BP")) {
cgwidentaggedstorebp(c, dst, src, slot_off, slot_sz);
@@ -13195,11 +13198,18 @@ fn cgwidentaggedstore(c: *cgen, dst: *node, src: *node,
// for the natural "BP" case and via the wrapper's scratch path for
// pointer-rooted dst. Direct callers exist only in case of future
// inlined uses inside this file; new code should call the wrapper.
fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz: i32) void = {
let dt: *node = resolvetagged(c, dst);
fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_sz: i32) void = {
// #68: dst is the tagged tinfo. Peel TY_NAMED → du and gate
// TY_TAGGED, mirroring cstage cg_widen_tagged_store's
// `du = (dst->kind==TY_NAMED)?dst->under:dst` + TY_TAGGED guard
// (cmd/w6c/cgen.c:1273). resolvetagged's N_TBANG unwrap is already
// handled upstream by tinfofornode (check.ww:1203-1210).
let dt: *tinfo = dst;
for (dt != nil && dt.kind == tykind.TY_NAMED) { dt = dt.under; };
if (dt == nil) { return; };
if (dt.kind != tykind.TY_TAGGED) { return; };
// Nullable fold: one 8B word holding the pointer (or 0 for void).
if (isnullabletype(dst)) {
if (dt.nullable != 0) {
cgexpr(c, src);
emitline("\tMOVQ\tAX, ");
emitoff(slot_off: i64);
@@ -13229,22 +13239,32 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
};
// Cast's destination = the dst tagged union
// itself? The rhs of N_CAST holds the target
// type. Compare nominally via str match on
// the tagged-alias name.
// type. #68: compare on the stamped tinfos —
// `castu == dt` (peeled-underlying ptr-id) ||
// (castu tagged && typeeq(castt, dst)) — mirroring
// cstage cg_widen_tagged_store's
// `cast_is_widen = (castu==du) || (castu->kind==
// TY_TAGGED && type_eq(castt, dst))`
// (cmd/w6c/cgen.c:1295-1296). Replaces the prior
// surface-name streq; same-alias TNAMEs share one
// NAMED tinfo (check.ww:1160-1173) so typeeq hits
// the a==b fast path.
let castisdst: bool = false;
let castrhs: *node = src.rhs;
if (castrhs != nil) {
if (castrhs.kind == nkind.N_TTAGGED) {
castisdst = true;
let castt: *tinfo = castrhs.type_: *tinfo;
let castu: *tinfo = castt;
for (castu != nil && castu.kind == tykind.TY_NAMED) {
castu = castu.under;
};
if (castrhs.kind == nkind.N_TNAME) {
if (dst != nil) {
if (dst.kind == nkind.N_TNAME) {
if (streq(castrhs.str, dst.str)) {
castisdst = true;
};
if (castu != nil) {
if (castu == dt) {
castisdst = true;
} else { if (castu.kind == tykind.TY_TAGGED) {
if (typeeq(castt, dst)) {
castisdst = true;
};
};
}; };
};
};
if (castisdst && !inneristagged) {
@@ -13254,6 +13274,8 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
};
};
// Tagged source ident: byte-copy slot words then tag-remap.
// rhstaggedident gates "src is a tagged-typed local ident"; the
// remap reads the source tagged tinfo off the local's tnode (#68).
let st: *node = rhstaggedident(c, src);
if (st != nil) {
let lc: *local = localfindnode(c, src.str);
@@ -13279,7 +13301,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
p += 8;
};
};
cgwidentagremap(c, dt, st, slot_off);
cgwidentagremap(c, dt, lc.tnode.type_: *tinfo, slot_off);
return;
};
// Tagged source via AX/DX/CX/R8 register ABI (N_CALL, N_INDEX
@@ -13320,7 +13342,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
emitline("(BP)\n");
zoff += 8;
};
let tag: i32 = taggedvariantindex(c, dt, src);
let tag: i32 = taggedvariantindext(c, dt, src);
if (tag < 0) { tag = 0; };
if (src.kind == nkind.N_STRUCTLIT) {
let fnode: *node = src.list;
@@ -13414,7 +13436,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
emitline("\tMOVQ\tBX, ");
emitoff((slot_off + 16): i64);
emitline("(BP)\n");
let tag: i32 = taggedvariantindex(c, dt, src);
let tag: i32 = taggedvariantindext(c, dt, src);
if (tag < 0) { tag = 0; };
emitline("\tMOVQ\t$");
emitint(tag: i64);
@@ -13436,7 +13458,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
emitline("\tMOVQ\tCX, ");
emitoff((slot_off + 24): i64);
emitline("(BP)\n");
let tag: i32 = taggedvariantindex(c, dt, src);
let tag: i32 = taggedvariantindext(c, dt, src);
if (tag < 0) { tag = 0; };
emitline("\tMOVQ\t$");
emitint(tag: i64);
@@ -13472,7 +13494,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
// like the slice axis, not nominal identity.
let wantf32: bool = (fkind == 1);
let ftag: i32 = -1;
let fti: *tinfo = dt.type_: *tinfo;
let fti: *tinfo = dt;
for (fti != nil && fti.kind == tykind.TY_NAMED) { fti = fti.under; };
if (fti != nil) { if (fti.kind == tykind.TY_TAGGED) {
let fp: *tparam = fti.params;
@@ -13502,7 +13524,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
emitline("\tMOVQ\tAX, ");
emitoff((slot_off + 8): i64);
emitline("(BP)\n");
let tag: i32 = taggedvariantindex(c, dt, src);
let tag: i32 = taggedvariantindext(c, dt, src);
if (tag < 0) { tag = 0; };
emitline("\tMOVQ\t$");
emitint(tag: i64);
@@ -13780,7 +13802,7 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *node,
emitsymname(c, srcname);
emitline("(SB), BX\n");
};
cgwidentaggedstore(c, fi.tnode,
cgwidentaggedstore(c, fi.tnode.type_: *tinfo,
fieldnode.lhs, basereg,
disp + fi.foff, fi.fsz);
fi = nil;
@@ -17149,7 +17171,7 @@ fn cgcall(c: *cgen, n: *node) void = {
// dst is the per-element tagged type;
// pass velem (cstage cgen.c:4382 passes
// velem, not the slice wrap vsu).
cgwidentaggedstore(c, velem,
cgwidentaggedstore(c, velem.type_: *tinfo,
aa2, "BP", slot, esz);
} else { if (velemstr) {
cgexpr(c, aa2);
@@ -17484,7 +17506,7 @@ fn cgassign(c: *cgen, n: *node) void = {
if (lc != nil) {
if (istaggedtype(c, lc.tnode)) {
let lsz: i32 = slotsize(c, lc.tnode);
cgwidentaggedstore(c, lc.tnode,
cgwidentaggedstore(c, lc.tnode.type_: *tinfo,
n.rhs, "BP", lc.off, lsz);
return;
};
@@ -17764,7 +17786,7 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("(BP)\n");
zz += 8;
};
cgwidentaggedstore(c, elemtn, n.rhs,
cgwidentaggedstore(c, elemtn.type_: *tinfo, n.rhs,
"BP", scroff, slot_sz);
cgexpr(c, idx);
if (slot_sz > 1) {
@@ -18142,7 +18164,7 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\tMOVQ\t");
emitoff(lc.off: i64);
emitline("(BP), BX\n");
cgwidentaggedstore(c, fi.tnode,
cgwidentaggedstore(c, fi.tnode.type_: *tinfo,
n.rhs, "BX", fi.foff, fsz);
return;
};
@@ -18379,7 +18401,7 @@ fn cgassign(c: *cgen, n: *node) void = {
if (n.op == tkind.TK_ASSIGN
&& istaggedtype(c, fi.tnode)) {
let fsz: i32 = slotsize(c, fi.tnode);
cgwidentaggedstore(c, fi.tnode,
cgwidentaggedstore(c, fi.tnode.type_: *tinfo,
n.rhs, "BP", lc.off + fi.foff, fsz);
return;
};
@@ -20062,7 +20084,7 @@ fn cgreturn(c: *cgen, n: *node) void = {
emitline("(BP)\n");
zz += 8;
};
cgwidentaggedstore(c, c.fnret, rhs, "BP",
cgwidentaggedstore(c, c.fnret.type_: *tinfo, rhs, "BP",
scroff, rsz);
emitline("\tMOVQ\t");
emitoff(scroff: i64);
@@ -20552,7 +20574,7 @@ fn cglet(c: *cgen, n: *node) void = {
// ABI call), struct payload (literal/ident), str payload,
// scalar payload — with tag remap for tagged-subset widening.
if (istaggedtype(c, tn)) {
cgwidentaggedstore(c, tn, rhs, "BP", off, sz);
cgwidentaggedstore(c, tn.type_: *tinfo, rhs, "BP", off, sz);
c.lastwasreturn = 0;
return;
};

View File

@@ -3144,7 +3144,7 @@ fn cgcall(c: *cgen, n: *node) void = {
// dst is the per-element tagged type;
// pass velem (cstage cgen.c:4382 passes
// velem, not the slice wrap vsu).
cgwidentaggedstore(c, velem,
cgwidentaggedstore(c, velem.type_: *tinfo,
aa2, "BP", slot, esz);
} else { if (velemstr) {
cgexpr(c, aa2);
@@ -3479,7 +3479,7 @@ fn cgassign(c: *cgen, n: *node) void = {
if (lc != nil) {
if (istaggedtype(c, lc.tnode)) {
let lsz: i32 = slotsize(c, lc.tnode);
cgwidentaggedstore(c, lc.tnode,
cgwidentaggedstore(c, lc.tnode.type_: *tinfo,
n.rhs, "BP", lc.off, lsz);
return;
};
@@ -3759,7 +3759,7 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("(BP)\n");
zz += 8;
};
cgwidentaggedstore(c, elemtn, n.rhs,
cgwidentaggedstore(c, elemtn.type_: *tinfo, n.rhs,
"BP", scroff, slot_sz);
cgexpr(c, idx);
if (slot_sz > 1) {
@@ -4137,7 +4137,7 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\tMOVQ\t");
emitoff(lc.off: i64);
emitline("(BP), BX\n");
cgwidentaggedstore(c, fi.tnode,
cgwidentaggedstore(c, fi.tnode.type_: *tinfo,
n.rhs, "BX", fi.foff, fsz);
return;
};
@@ -4374,7 +4374,7 @@ fn cgassign(c: *cgen, n: *node) void = {
if (n.op == tkind.TK_ASSIGN
&& istaggedtype(c, fi.tnode)) {
let fsz: i32 = slotsize(c, fi.tnode);
cgwidentaggedstore(c, fi.tnode,
cgwidentaggedstore(c, fi.tnode.type_: *tinfo,
n.rhs, "BP", lc.off + fi.foff, fsz);
return;
};

View File

@@ -225,7 +225,7 @@ fn cgreturn(c: *cgen, n: *node) void = {
emitline("(BP)\n");
zz += 8;
};
cgwidentaggedstore(c, c.fnret, rhs, "BP",
cgwidentaggedstore(c, c.fnret.type_: *tinfo, rhs, "BP",
scroff, rsz);
emitline("\tMOVQ\t");
emitoff(scroff: i64);
@@ -715,7 +715,7 @@ fn cglet(c: *cgen, n: *node) void = {
// ABI call), struct payload (literal/ident), str payload,
// scalar payload — with tag remap for tagged-subset widening.
if (istaggedtype(c, tn)) {
cgwidentaggedstore(c, tn, rhs, "BP", off, sz);
cgwidentaggedstore(c, tn.type_: *tinfo, rhs, "BP", off, sz);
c.lastwasreturn = 0;
return;
};

View File

@@ -222,7 +222,7 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node) i32 = {
emitline("(BP)\n");
zz += 8;
};
cgwidentaggedstore(c, ptype, arg, "BP", scroff, widensz);
cgwidentaggedstore(c, ptype.type_: *tinfo, arg, "BP", scroff, widensz);
let pp: i32 = widensz - 8;
for (pp >= 0) {
emitline("\tMOVQ\t");
@@ -2187,65 +2187,48 @@ fn rhstargetname(c: *cgen, rhs: *node) str = {
// "any str-shape variant matches an str-typed value".
fn taggedvariantindex(c: *cgen, tagged: *node, rhs: *node) i32 = {
if (tagged == nil) { return -1; };
if (rhs == nil) { return -1; };
// Alias-unwrap: wwstage has no typed AST, so an aliased tagged
// return (`type ft = (i64|str|bool); fn f() ft = ...`) reaches
// here as N_TNAME("ft"), not N_TTAGGED. flatvariantidx and the
// fallback both gate on N_TTAGGED → -1 → caller maps to 0,
// silently emitting `MOVQ $0, AX` for every non-leading variant.
// Cstage's check.c canonicalizes N_TNAME → underlying upfront;
// every wwstage cgen consumer of a type-bearing node has to
// remember this step itself. TODO(#11): a wwstage check pass
// between parse and cgen would replace the per-site unwrap with
// a single canonicalization. Same shape of fix as nodeisstr.
let resolved: *node = resolvetagged(c, tagged);
if (resolved != nil) { tagged = resolved; };
// #66 Phase-N step 3: match the value's stamped type against the
// variant types by typeeq (flatvariantidx), replacing the
// rhstargetname surface-name compare. Untyped/loose values (whose
// .type_ is untyped_* and can't typeeq a concrete variant) return
// -1 here and drop to the str/slice shape scan below — ww has no
// type_assignable to mirror cg_variant_match's untyped-src arm.
let r: i32 = flatvariantidx(c, tagged, rhs);
return taggedvariantindext(c, tagged.type_: *tinfo, rhs);
};
// taggedvariantindext — tinfo-keyed core of taggedvariantindex. Given
// the dst tagged tinfo `du` (NAMED-peeled internally, gated TY_TAGGED)
// and the source value node, returns the 0-based variant index. #68: the
// tagged-store machinery reads tinfo directly (no type node), so the
// node-keyed taggedvariantindex delegates here off `tagged.type_`.
//
// #66 Phase-N step 3: match the value's stamped type against the variant
// types by typeeq (flatvariantidxt), replacing the rhstargetname surface-
// name compare. Untyped/loose values (whose .type_ is untyped_* and can't
// typeeq a concrete variant) return -1 there and drop to the str/slice
// shape scan below — ww has no type_assignable to mirror cg_variant_match's
// untyped-src arm.
fn taggedvariantindext(c: *cgen, du: *tinfo, rhs: *node) i32 = {
if (du == nil) { return -1; };
if (rhs == nil) { return -1; };
let ti: *tinfo = du;
for (ti != nil && ti.kind == tykind.TY_NAMED) { ti = ti.under; };
if (ti == nil) { return -1; };
if (ti.kind != tykind.TY_TAGGED) { return -1; };
let r: i32 = flatvariantidxt(ti, rhs.type_: *tinfo);
if (r >= 0) { return r; };
// Shape fallback: classify rhs as (str, slice, scalar/other) and
// pick the first variant of matching shape. Stands in for cstage's
// type_assignable on an untyped src — the typeeq pass above can't
// match untyped_* against a concrete variant, and the slice axis
// keeps a (u8 | []u8) widen off the leading scalar variant (task
// #19). Walks the spread-flattened list so a `(...inner | str)`
// outer agrees with the inner's str / slice positions.
// #19). tinfo.params is already spread-flattened (#61a — `...inner`
// inlined in declaration order), so the old N_TTAGGED.list spread-
// walk collapses to a flat scan over p.type_.
let wantstr: bool = nodeisstr(c, rhs);
let wantslice: bool = nodeisslice(c, rhs);
let v: *node = tagged.list;
let p: *tparam = ti.params;
let idx: i32 = 0;
for (v != nil) {
let isspread: bool = (v.op == tkind.TK_ELLIPSIS);
if (isspread) {
let inner: *node = v;
if (inner.kind == nkind.N_TNAME) {
let a: *node = aliaslookup(c, inner.str);
if (a != nil) { inner = a; };
};
if (inner != nil) {
if (inner.kind == nkind.N_TTAGGED) {
let iv: *node = inner.list;
for (iv != nil) {
let ivisstr: bool = isstrtype(c, iv);
let ivisslice: bool = isslicetype(c, iv);
if (ivisstr == wantstr && ivisslice == wantslice) { return idx; };
iv = iv.next;
idx += 1;
};
v = v.next;
continue;
};
};
};
let visstr: bool = isstrtype(c, v);
let visslice: bool = isslicetype(c, v);
for (p != nil) {
let vt: *tinfo = p.type_;
let visstr: bool = typeisstr(vt);
let visslice: bool = typeisslice(vt);
if (visstr == wantstr && visslice == wantslice) { return idx; };
v = v.next;
p = p.tnext;
idx += 1;
};
return -1;
@@ -2268,9 +2251,21 @@ fn taggedvariantindex(c: *cgen, tagged: *node, rhs: *node) i32 = {
fn flatvariantidx(c: *cgen, tagged: *node, pat: *node) i32 = {
if (tagged == nil) { return -1; };
if (pat == nil) { return -1; };
let want: *tinfo = pat.type_: *tinfo;
return flatvariantidxt(tagged.type_: *tinfo, pat.type_: *tinfo);
};
// flatvariantidxt — tinfo-keyed core of flatvariantidx: flat 0-based
// index of the variant whose type typeeq's `want`, over the pre-
// flattened tinfo.params chain (#61a). Peels TY_NAMED then gates
// TY_TAGGED. #68: the tagged-store machinery reads tinfo directly and
// has no type node to hand the node-keyed flatvariantidx, so the typeeq
// core lives here; flatvariantidx + taggedvariantindext + cgwidentagremap
// all funnel through it. Mirrors cstage cg_tag_for_variant
// (cmd/w6c/cgen.c:503) + cg_variant_match's both-NAMED ptr-id / typeeq
// arm (:451).
fn flatvariantidxt(tagged: *tinfo, want: *tinfo) i32 = {
if (want == nil) { return -1; };
let ti: *tinfo = tagged.type_: *tinfo;
let ti: *tinfo = tagged;
for (ti != nil && ti.kind == tykind.TY_NAMED) { ti = ti.under; };
if (ti == nil) { return -1; };
if (ti.kind != tykind.TY_TAGGED) { return -1; };
@@ -2329,31 +2324,39 @@ fn flatslicevariantidx(c: *cgen, tagged: *node, elem: *node) i32 = {
// cgwidentagremap — when widening from one tagged union to a wider one,
// rewrite the source's variant tag at slot_off+0 to use the destination's
// variant indices. No-op when src and dst index orders coincide.
// Mirrors cg_widen_tag_remap in cmd/w6c/cgen.c.
fn cgwidentagremap(c: *cgen, dst: *node, src: *node, slot_off: i32) void = {
if (dst == nil) { return; };
if (src == nil) { return; };
if (dst.kind != nkind.N_TTAGGED) { return; };
if (src.kind != nkind.N_TTAGGED) { return; };
//
// #68: both `du` (dst) and `su` (src) are now the tagged tinfos — peel
// TY_NAMED then walk su.params, mapping each source variant to its dst
// index by typeeq (flatvariantidxt). Mirrors cg_widen_tag_remap
// (cmd/w6c/cgen.c:1177) over su->params + cg_tag_for_variant (:503).
fn cgwidentagremap(c: *cgen, du: *tinfo, su: *tinfo, slot_off: i32) void = {
let dt: *tinfo = du;
for (dt != nil && dt.kind == tykind.TY_NAMED) { dt = dt.under; };
if (dt == nil) { return; };
if (dt.kind != tykind.TY_TAGGED) { return; };
let st: *tinfo = su;
for (st != nil && st.kind == tykind.TY_NAMED) { st = st.under; };
if (st == nil) { return; };
if (st.kind != tykind.TY_TAGGED) { return; };
let identity: bool = true;
let v: *node = src.list;
let p: *tparam = st.params;
let idx: i32 = 0;
for (v != nil) {
let di: i32 = cgtagvariantidx(c, dst, v);
for (p != nil) {
let di: i32 = flatvariantidxt(dt, p.type_);
if (di < 0) { di = 0; };
if (di != idx) { identity = false; v = nil; }
else { v = v.next; idx += 1; };
if (di != idx) { identity = false; p = nil; }
else { p = p.tnext; idx += 1; };
};
if (identity) { return; };
let done: str = mklabel(c, "remap_done");
emitline("\tMOVQ\t");
emitoff(slot_off: i64);
emitline("(BP), AX\n");
v = src.list;
p = st.params;
idx = 0;
for (v != nil) {
for (p != nil) {
let next: str = mklabel(c, "remap_next");
let di: i32 = cgtagvariantidx(c, dst, v);
let di: i32 = flatvariantidxt(dt, p.type_);
if (di < 0) { di = 0; };
emitline("\tCMPQ\t$");
emitint(idx: i64);
@@ -2371,7 +2374,7 @@ fn cgwidentagremap(c: *cgen, dst: *node, src: *node, slot_off: i32) void = {
emitline(done);
emitline("\n");
emitlabel(next);
v = v.next;
p = p.tnext;
idx += 1;
};
emitlabel(done);
@@ -2600,7 +2603,7 @@ fn cgloadtaggedfield(c: *cgen, basereg: str, foff: i32, slot_sz: i32) void = {
// tag last.
// - str src: tag@+0, ptr@+8, len@+16.
// - scalar src: tag@+0, value@+8.
fn cgwidentaggedstore(c: *cgen, dst: *node, src: *node,
fn cgwidentaggedstore(c: *cgen, dst: *tinfo, src: *node,
basereg: str, slot_off: i32, slot_sz: i32) void = {
if (streq(basereg, "BP")) {
cgwidentaggedstorebp(c, dst, src, slot_off, slot_sz);
@@ -2650,11 +2653,18 @@ fn cgwidentaggedstore(c: *cgen, dst: *node, src: *node,
// for the natural "BP" case and via the wrapper's scratch path for
// pointer-rooted dst. Direct callers exist only in case of future
// inlined uses inside this file; new code should call the wrapper.
fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz: i32) void = {
let dt: *node = resolvetagged(c, dst);
fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_sz: i32) void = {
// #68: dst is the tagged tinfo. Peel TY_NAMED → du and gate
// TY_TAGGED, mirroring cstage cg_widen_tagged_store's
// `du = (dst->kind==TY_NAMED)?dst->under:dst` + TY_TAGGED guard
// (cmd/w6c/cgen.c:1273). resolvetagged's N_TBANG unwrap is already
// handled upstream by tinfofornode (check.ww:1203-1210).
let dt: *tinfo = dst;
for (dt != nil && dt.kind == tykind.TY_NAMED) { dt = dt.under; };
if (dt == nil) { return; };
if (dt.kind != tykind.TY_TAGGED) { return; };
// Nullable fold: one 8B word holding the pointer (or 0 for void).
if (isnullabletype(dst)) {
if (dt.nullable != 0) {
cgexpr(c, src);
emitline("\tMOVQ\tAX, ");
emitoff(slot_off: i64);
@@ -2684,22 +2694,32 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
};
// Cast's destination = the dst tagged union
// itself? The rhs of N_CAST holds the target
// type. Compare nominally via str match on
// the tagged-alias name.
// type. #68: compare on the stamped tinfos —
// `castu == dt` (peeled-underlying ptr-id) ||
// (castu tagged && typeeq(castt, dst)) — mirroring
// cstage cg_widen_tagged_store's
// `cast_is_widen = (castu==du) || (castu->kind==
// TY_TAGGED && type_eq(castt, dst))`
// (cmd/w6c/cgen.c:1295-1296). Replaces the prior
// surface-name streq; same-alias TNAMEs share one
// NAMED tinfo (check.ww:1160-1173) so typeeq hits
// the a==b fast path.
let castisdst: bool = false;
let castrhs: *node = src.rhs;
if (castrhs != nil) {
if (castrhs.kind == nkind.N_TTAGGED) {
castisdst = true;
let castt: *tinfo = castrhs.type_: *tinfo;
let castu: *tinfo = castt;
for (castu != nil && castu.kind == tykind.TY_NAMED) {
castu = castu.under;
};
if (castrhs.kind == nkind.N_TNAME) {
if (dst != nil) {
if (dst.kind == nkind.N_TNAME) {
if (streq(castrhs.str, dst.str)) {
castisdst = true;
};
if (castu != nil) {
if (castu == dt) {
castisdst = true;
} else { if (castu.kind == tykind.TY_TAGGED) {
if (typeeq(castt, dst)) {
castisdst = true;
};
};
}; };
};
};
if (castisdst && !inneristagged) {
@@ -2709,6 +2729,8 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
};
};
// Tagged source ident: byte-copy slot words then tag-remap.
// rhstaggedident gates "src is a tagged-typed local ident"; the
// remap reads the source tagged tinfo off the local's tnode (#68).
let st: *node = rhstaggedident(c, src);
if (st != nil) {
let lc: *local = localfindnode(c, src.str);
@@ -2734,7 +2756,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
p += 8;
};
};
cgwidentagremap(c, dt, st, slot_off);
cgwidentagremap(c, dt, lc.tnode.type_: *tinfo, slot_off);
return;
};
// Tagged source via AX/DX/CX/R8 register ABI (N_CALL, N_INDEX
@@ -2775,7 +2797,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
emitline("(BP)\n");
zoff += 8;
};
let tag: i32 = taggedvariantindex(c, dt, src);
let tag: i32 = taggedvariantindext(c, dt, src);
if (tag < 0) { tag = 0; };
if (src.kind == nkind.N_STRUCTLIT) {
let fnode: *node = src.list;
@@ -2869,7 +2891,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
emitline("\tMOVQ\tBX, ");
emitoff((slot_off + 16): i64);
emitline("(BP)\n");
let tag: i32 = taggedvariantindex(c, dt, src);
let tag: i32 = taggedvariantindext(c, dt, src);
if (tag < 0) { tag = 0; };
emitline("\tMOVQ\t$");
emitint(tag: i64);
@@ -2891,7 +2913,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
emitline("\tMOVQ\tCX, ");
emitoff((slot_off + 24): i64);
emitline("(BP)\n");
let tag: i32 = taggedvariantindex(c, dt, src);
let tag: i32 = taggedvariantindext(c, dt, src);
if (tag < 0) { tag = 0; };
emitline("\tMOVQ\t$");
emitint(tag: i64);
@@ -2927,7 +2949,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
// like the slice axis, not nominal identity.
let wantf32: bool = (fkind == 1);
let ftag: i32 = -1;
let fti: *tinfo = dt.type_: *tinfo;
let fti: *tinfo = dt;
for (fti != nil && fti.kind == tykind.TY_NAMED) { fti = fti.under; };
if (fti != nil) { if (fti.kind == tykind.TY_TAGGED) {
let fp: *tparam = fti.params;
@@ -2957,7 +2979,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
emitline("\tMOVQ\tAX, ");
emitoff((slot_off + 8): i64);
emitline("(BP)\n");
let tag: i32 = taggedvariantindex(c, dt, src);
let tag: i32 = taggedvariantindext(c, dt, src);
if (tag < 0) { tag = 0; };
emitline("\tMOVQ\t$");
emitint(tag: i64);
@@ -3235,7 +3257,7 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *node,
emitsymname(c, srcname);
emitline("(SB), BX\n");
};
cgwidentaggedstore(c, fi.tnode,
cgwidentaggedstore(c, fi.tnode.type_: *tinfo,
fieldnode.lhs, basereg,
disp + fi.foff, fi.fsz);
fi = nil;

View File

@@ -10767,7 +10767,7 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node) i32 = {
emitline("(BP)\n");
zz += 8;
};
cgwidentaggedstore(c, ptype, arg, "BP", scroff, widensz);
cgwidentaggedstore(c, ptype.type_: *tinfo, arg, "BP", scroff, widensz);
let pp: i32 = widensz - 8;
for (pp >= 0) {
emitline("\tMOVQ\t");
@@ -12732,65 +12732,48 @@ fn rhstargetname(c: *cgen, rhs: *node) str = {
// "any str-shape variant matches an str-typed value".
fn taggedvariantindex(c: *cgen, tagged: *node, rhs: *node) i32 = {
if (tagged == nil) { return -1; };
if (rhs == nil) { return -1; };
// Alias-unwrap: wwstage has no typed AST, so an aliased tagged
// return (`type ft = (i64|str|bool); fn f() ft = ...`) reaches
// here as N_TNAME("ft"), not N_TTAGGED. flatvariantidx and the
// fallback both gate on N_TTAGGED → -1 → caller maps to 0,
// silently emitting `MOVQ $0, AX` for every non-leading variant.
// Cstage's check.c canonicalizes N_TNAME → underlying upfront;
// every wwstage cgen consumer of a type-bearing node has to
// remember this step itself. TODO(#11): a wwstage check pass
// between parse and cgen would replace the per-site unwrap with
// a single canonicalization. Same shape of fix as nodeisstr.
let resolved: *node = resolvetagged(c, tagged);
if (resolved != nil) { tagged = resolved; };
// #66 Phase-N step 3: match the value's stamped type against the
// variant types by typeeq (flatvariantidx), replacing the
// rhstargetname surface-name compare. Untyped/loose values (whose
// .type_ is untyped_* and can't typeeq a concrete variant) return
// -1 here and drop to the str/slice shape scan below — ww has no
// type_assignable to mirror cg_variant_match's untyped-src arm.
let r: i32 = flatvariantidx(c, tagged, rhs);
return taggedvariantindext(c, tagged.type_: *tinfo, rhs);
};
// taggedvariantindext — tinfo-keyed core of taggedvariantindex. Given
// the dst tagged tinfo `du` (NAMED-peeled internally, gated TY_TAGGED)
// and the source value node, returns the 0-based variant index. #68: the
// tagged-store machinery reads tinfo directly (no type node), so the
// node-keyed taggedvariantindex delegates here off `tagged.type_`.
//
// #66 Phase-N step 3: match the value's stamped type against the variant
// types by typeeq (flatvariantidxt), replacing the rhstargetname surface-
// name compare. Untyped/loose values (whose .type_ is untyped_* and can't
// typeeq a concrete variant) return -1 there and drop to the str/slice
// shape scan below — ww has no type_assignable to mirror cg_variant_match's
// untyped-src arm.
fn taggedvariantindext(c: *cgen, du: *tinfo, rhs: *node) i32 = {
if (du == nil) { return -1; };
if (rhs == nil) { return -1; };
let ti: *tinfo = du;
for (ti != nil && ti.kind == tykind.TY_NAMED) { ti = ti.under; };
if (ti == nil) { return -1; };
if (ti.kind != tykind.TY_TAGGED) { return -1; };
let r: i32 = flatvariantidxt(ti, rhs.type_: *tinfo);
if (r >= 0) { return r; };
// Shape fallback: classify rhs as (str, slice, scalar/other) and
// pick the first variant of matching shape. Stands in for cstage's
// type_assignable on an untyped src — the typeeq pass above can't
// match untyped_* against a concrete variant, and the slice axis
// keeps a (u8 | []u8) widen off the leading scalar variant (task
// #19). Walks the spread-flattened list so a `(...inner | str)`
// outer agrees with the inner's str / slice positions.
// #19). tinfo.params is already spread-flattened (#61a — `...inner`
// inlined in declaration order), so the old N_TTAGGED.list spread-
// walk collapses to a flat scan over p.type_.
let wantstr: bool = nodeisstr(c, rhs);
let wantslice: bool = nodeisslice(c, rhs);
let v: *node = tagged.list;
let p: *tparam = ti.params;
let idx: i32 = 0;
for (v != nil) {
let isspread: bool = (v.op == tkind.TK_ELLIPSIS);
if (isspread) {
let inner: *node = v;
if (inner.kind == nkind.N_TNAME) {
let a: *node = aliaslookup(c, inner.str);
if (a != nil) { inner = a; };
};
if (inner != nil) {
if (inner.kind == nkind.N_TTAGGED) {
let iv: *node = inner.list;
for (iv != nil) {
let ivisstr: bool = isstrtype(c, iv);
let ivisslice: bool = isslicetype(c, iv);
if (ivisstr == wantstr && ivisslice == wantslice) { return idx; };
iv = iv.next;
idx += 1;
};
v = v.next;
continue;
};
};
};
let visstr: bool = isstrtype(c, v);
let visslice: bool = isslicetype(c, v);
for (p != nil) {
let vt: *tinfo = p.type_;
let visstr: bool = typeisstr(vt);
let visslice: bool = typeisslice(vt);
if (visstr == wantstr && visslice == wantslice) { return idx; };
v = v.next;
p = p.tnext;
idx += 1;
};
return -1;
@@ -12813,9 +12796,21 @@ fn taggedvariantindex(c: *cgen, tagged: *node, rhs: *node) i32 = {
fn flatvariantidx(c: *cgen, tagged: *node, pat: *node) i32 = {
if (tagged == nil) { return -1; };
if (pat == nil) { return -1; };
let want: *tinfo = pat.type_: *tinfo;
return flatvariantidxt(tagged.type_: *tinfo, pat.type_: *tinfo);
};
// flatvariantidxt — tinfo-keyed core of flatvariantidx: flat 0-based
// index of the variant whose type typeeq's `want`, over the pre-
// flattened tinfo.params chain (#61a). Peels TY_NAMED then gates
// TY_TAGGED. #68: the tagged-store machinery reads tinfo directly and
// has no type node to hand the node-keyed flatvariantidx, so the typeeq
// core lives here; flatvariantidx + taggedvariantindext + cgwidentagremap
// all funnel through it. Mirrors cstage cg_tag_for_variant
// (cmd/w6c/cgen.c:503) + cg_variant_match's both-NAMED ptr-id / typeeq
// arm (:451).
fn flatvariantidxt(tagged: *tinfo, want: *tinfo) i32 = {
if (want == nil) { return -1; };
let ti: *tinfo = tagged.type_: *tinfo;
let ti: *tinfo = tagged;
for (ti != nil && ti.kind == tykind.TY_NAMED) { ti = ti.under; };
if (ti == nil) { return -1; };
if (ti.kind != tykind.TY_TAGGED) { return -1; };
@@ -12874,31 +12869,39 @@ fn flatslicevariantidx(c: *cgen, tagged: *node, elem: *node) i32 = {
// cgwidentagremap — when widening from one tagged union to a wider one,
// rewrite the source's variant tag at slot_off+0 to use the destination's
// variant indices. No-op when src and dst index orders coincide.
// Mirrors cg_widen_tag_remap in cmd/w6c/cgen.c.
fn cgwidentagremap(c: *cgen, dst: *node, src: *node, slot_off: i32) void = {
if (dst == nil) { return; };
if (src == nil) { return; };
if (dst.kind != nkind.N_TTAGGED) { return; };
if (src.kind != nkind.N_TTAGGED) { return; };
//
// #68: both `du` (dst) and `su` (src) are now the tagged tinfos — peel
// TY_NAMED then walk su.params, mapping each source variant to its dst
// index by typeeq (flatvariantidxt). Mirrors cg_widen_tag_remap
// (cmd/w6c/cgen.c:1177) over su->params + cg_tag_for_variant (:503).
fn cgwidentagremap(c: *cgen, du: *tinfo, su: *tinfo, slot_off: i32) void = {
let dt: *tinfo = du;
for (dt != nil && dt.kind == tykind.TY_NAMED) { dt = dt.under; };
if (dt == nil) { return; };
if (dt.kind != tykind.TY_TAGGED) { return; };
let st: *tinfo = su;
for (st != nil && st.kind == tykind.TY_NAMED) { st = st.under; };
if (st == nil) { return; };
if (st.kind != tykind.TY_TAGGED) { return; };
let identity: bool = true;
let v: *node = src.list;
let p: *tparam = st.params;
let idx: i32 = 0;
for (v != nil) {
let di: i32 = cgtagvariantidx(c, dst, v);
for (p != nil) {
let di: i32 = flatvariantidxt(dt, p.type_);
if (di < 0) { di = 0; };
if (di != idx) { identity = false; v = nil; }
else { v = v.next; idx += 1; };
if (di != idx) { identity = false; p = nil; }
else { p = p.tnext; idx += 1; };
};
if (identity) { return; };
let done: str = mklabel(c, "remap_done");
emitline("\tMOVQ\t");
emitoff(slot_off: i64);
emitline("(BP), AX\n");
v = src.list;
p = st.params;
idx = 0;
for (v != nil) {
for (p != nil) {
let next: str = mklabel(c, "remap_next");
let di: i32 = cgtagvariantidx(c, dst, v);
let di: i32 = flatvariantidxt(dt, p.type_);
if (di < 0) { di = 0; };
emitline("\tCMPQ\t$");
emitint(idx: i64);
@@ -12916,7 +12919,7 @@ fn cgwidentagremap(c: *cgen, dst: *node, src: *node, slot_off: i32) void = {
emitline(done);
emitline("\n");
emitlabel(next);
v = v.next;
p = p.tnext;
idx += 1;
};
emitlabel(done);
@@ -13145,7 +13148,7 @@ fn cgloadtaggedfield(c: *cgen, basereg: str, foff: i32, slot_sz: i32) void = {
// tag last.
// - str src: tag@+0, ptr@+8, len@+16.
// - scalar src: tag@+0, value@+8.
fn cgwidentaggedstore(c: *cgen, dst: *node, src: *node,
fn cgwidentaggedstore(c: *cgen, dst: *tinfo, src: *node,
basereg: str, slot_off: i32, slot_sz: i32) void = {
if (streq(basereg, "BP")) {
cgwidentaggedstorebp(c, dst, src, slot_off, slot_sz);
@@ -13195,11 +13198,18 @@ fn cgwidentaggedstore(c: *cgen, dst: *node, src: *node,
// for the natural "BP" case and via the wrapper's scratch path for
// pointer-rooted dst. Direct callers exist only in case of future
// inlined uses inside this file; new code should call the wrapper.
fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz: i32) void = {
let dt: *node = resolvetagged(c, dst);
fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_sz: i32) void = {
// #68: dst is the tagged tinfo. Peel TY_NAMED → du and gate
// TY_TAGGED, mirroring cstage cg_widen_tagged_store's
// `du = (dst->kind==TY_NAMED)?dst->under:dst` + TY_TAGGED guard
// (cmd/w6c/cgen.c:1273). resolvetagged's N_TBANG unwrap is already
// handled upstream by tinfofornode (check.ww:1203-1210).
let dt: *tinfo = dst;
for (dt != nil && dt.kind == tykind.TY_NAMED) { dt = dt.under; };
if (dt == nil) { return; };
if (dt.kind != tykind.TY_TAGGED) { return; };
// Nullable fold: one 8B word holding the pointer (or 0 for void).
if (isnullabletype(dst)) {
if (dt.nullable != 0) {
cgexpr(c, src);
emitline("\tMOVQ\tAX, ");
emitoff(slot_off: i64);
@@ -13229,22 +13239,32 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
};
// Cast's destination = the dst tagged union
// itself? The rhs of N_CAST holds the target
// type. Compare nominally via str match on
// the tagged-alias name.
// type. #68: compare on the stamped tinfos —
// `castu == dt` (peeled-underlying ptr-id) ||
// (castu tagged && typeeq(castt, dst)) — mirroring
// cstage cg_widen_tagged_store's
// `cast_is_widen = (castu==du) || (castu->kind==
// TY_TAGGED && type_eq(castt, dst))`
// (cmd/w6c/cgen.c:1295-1296). Replaces the prior
// surface-name streq; same-alias TNAMEs share one
// NAMED tinfo (check.ww:1160-1173) so typeeq hits
// the a==b fast path.
let castisdst: bool = false;
let castrhs: *node = src.rhs;
if (castrhs != nil) {
if (castrhs.kind == nkind.N_TTAGGED) {
castisdst = true;
let castt: *tinfo = castrhs.type_: *tinfo;
let castu: *tinfo = castt;
for (castu != nil && castu.kind == tykind.TY_NAMED) {
castu = castu.under;
};
if (castrhs.kind == nkind.N_TNAME) {
if (dst != nil) {
if (dst.kind == nkind.N_TNAME) {
if (streq(castrhs.str, dst.str)) {
castisdst = true;
};
if (castu != nil) {
if (castu == dt) {
castisdst = true;
} else { if (castu.kind == tykind.TY_TAGGED) {
if (typeeq(castt, dst)) {
castisdst = true;
};
};
}; };
};
};
if (castisdst && !inneristagged) {
@@ -13254,6 +13274,8 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
};
};
// Tagged source ident: byte-copy slot words then tag-remap.
// rhstaggedident gates "src is a tagged-typed local ident"; the
// remap reads the source tagged tinfo off the local's tnode (#68).
let st: *node = rhstaggedident(c, src);
if (st != nil) {
let lc: *local = localfindnode(c, src.str);
@@ -13279,7 +13301,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
p += 8;
};
};
cgwidentagremap(c, dt, st, slot_off);
cgwidentagremap(c, dt, lc.tnode.type_: *tinfo, slot_off);
return;
};
// Tagged source via AX/DX/CX/R8 register ABI (N_CALL, N_INDEX
@@ -13320,7 +13342,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
emitline("(BP)\n");
zoff += 8;
};
let tag: i32 = taggedvariantindex(c, dt, src);
let tag: i32 = taggedvariantindext(c, dt, src);
if (tag < 0) { tag = 0; };
if (src.kind == nkind.N_STRUCTLIT) {
let fnode: *node = src.list;
@@ -13414,7 +13436,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
emitline("\tMOVQ\tBX, ");
emitoff((slot_off + 16): i64);
emitline("(BP)\n");
let tag: i32 = taggedvariantindex(c, dt, src);
let tag: i32 = taggedvariantindext(c, dt, src);
if (tag < 0) { tag = 0; };
emitline("\tMOVQ\t$");
emitint(tag: i64);
@@ -13436,7 +13458,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
emitline("\tMOVQ\tCX, ");
emitoff((slot_off + 24): i64);
emitline("(BP)\n");
let tag: i32 = taggedvariantindex(c, dt, src);
let tag: i32 = taggedvariantindext(c, dt, src);
if (tag < 0) { tag = 0; };
emitline("\tMOVQ\t$");
emitint(tag: i64);
@@ -13472,7 +13494,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
// like the slice axis, not nominal identity.
let wantf32: bool = (fkind == 1);
let ftag: i32 = -1;
let fti: *tinfo = dt.type_: *tinfo;
let fti: *tinfo = dt;
for (fti != nil && fti.kind == tykind.TY_NAMED) { fti = fti.under; };
if (fti != nil) { if (fti.kind == tykind.TY_TAGGED) {
let fp: *tparam = fti.params;
@@ -13502,7 +13524,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
emitline("\tMOVQ\tAX, ");
emitoff((slot_off + 8): i64);
emitline("(BP)\n");
let tag: i32 = taggedvariantindex(c, dt, src);
let tag: i32 = taggedvariantindext(c, dt, src);
if (tag < 0) { tag = 0; };
emitline("\tMOVQ\t$");
emitint(tag: i64);
@@ -13780,7 +13802,7 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *node,
emitsymname(c, srcname);
emitline("(SB), BX\n");
};
cgwidentaggedstore(c, fi.tnode,
cgwidentaggedstore(c, fi.tnode.type_: *tinfo,
fieldnode.lhs, basereg,
disp + fi.foff, fi.fsz);
fi = nil;
@@ -17149,7 +17171,7 @@ fn cgcall(c: *cgen, n: *node) void = {
// dst is the per-element tagged type;
// pass velem (cstage cgen.c:4382 passes
// velem, not the slice wrap vsu).
cgwidentaggedstore(c, velem,
cgwidentaggedstore(c, velem.type_: *tinfo,
aa2, "BP", slot, esz);
} else { if (velemstr) {
cgexpr(c, aa2);
@@ -17484,7 +17506,7 @@ fn cgassign(c: *cgen, n: *node) void = {
if (lc != nil) {
if (istaggedtype(c, lc.tnode)) {
let lsz: i32 = slotsize(c, lc.tnode);
cgwidentaggedstore(c, lc.tnode,
cgwidentaggedstore(c, lc.tnode.type_: *tinfo,
n.rhs, "BP", lc.off, lsz);
return;
};
@@ -17764,7 +17786,7 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("(BP)\n");
zz += 8;
};
cgwidentaggedstore(c, elemtn, n.rhs,
cgwidentaggedstore(c, elemtn.type_: *tinfo, n.rhs,
"BP", scroff, slot_sz);
cgexpr(c, idx);
if (slot_sz > 1) {
@@ -18142,7 +18164,7 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\tMOVQ\t");
emitoff(lc.off: i64);
emitline("(BP), BX\n");
cgwidentaggedstore(c, fi.tnode,
cgwidentaggedstore(c, fi.tnode.type_: *tinfo,
n.rhs, "BX", fi.foff, fsz);
return;
};
@@ -18379,7 +18401,7 @@ fn cgassign(c: *cgen, n: *node) void = {
if (n.op == tkind.TK_ASSIGN
&& istaggedtype(c, fi.tnode)) {
let fsz: i32 = slotsize(c, fi.tnode);
cgwidentaggedstore(c, fi.tnode,
cgwidentaggedstore(c, fi.tnode.type_: *tinfo,
n.rhs, "BP", lc.off + fi.foff, fsz);
return;
};
@@ -20062,7 +20084,7 @@ fn cgreturn(c: *cgen, n: *node) void = {
emitline("(BP)\n");
zz += 8;
};
cgwidentaggedstore(c, c.fnret, rhs, "BP",
cgwidentaggedstore(c, c.fnret.type_: *tinfo, rhs, "BP",
scroff, rsz);
emitline("\tMOVQ\t");
emitoff(scroff: i64);
@@ -20552,7 +20574,7 @@ fn cglet(c: *cgen, n: *node) void = {
// ABI call), struct payload (literal/ident), str payload,
// scalar payload — with tag remap for tagged-subset widening.
if (istaggedtype(c, tn)) {
cgwidentaggedstore(c, tn, rhs, "BP", off, sz);
cgwidentaggedstore(c, tn.type_: *tinfo, rhs, "BP", off, sz);
c.lastwasreturn = 0;
return;
};