wcc: TYPE-based forward-tagged predicate in wwstage cgreturn (#201)
cgreturn's forwardtagged detection was keyed on the CALLEE NAME (N_IDENT/N_DOT only via fnretlookupmod), so any other callee shape fell through to the variant-tag synthesis path — clobbering the just-returned AX/DX/CX/R8 tagged-ABI words. The deref-call case `(*r)(...)` (impl-e1-resume STOP, 994 w6c_ww byte-id red) was the proximate trigger. Replace with a TYPE-BASED predicate over the checker-stamped tinfos (rhs.type_ vs c.fnret.type_), mirroring cstage cgen.c:8007 passthrough. Peel TY_NAMED on both sides then identity-check the underlying TY_TAGGED — sufficient for the NAMED case because tinfocache memoizes per typedecl (#191 lineage). Variant-pointer fallback walks the params chain when identity fails so anonymous unions like the cross-module (i32 | void) shared between strings.byteindex and bytes.index still forward correctly; full recursive tinfo structural-eq is gated by #178 (typeeqast's TY_TAGGED arm conservatively returns false today). Probe 770_return_tagged_forward covers 6 rows — IDENT forward, widen non-matching, deref-call (the bug), scalar (sanity), nested call, cross-module forward — each gated on cstage runtime + wwstage runtime + cs.s == ww.s byte-identity.
This commit is contained in:
@@ -25018,32 +25018,42 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
if (istaggedtype(c, c.fnret)) {
|
||||
// Forwarding a fallible call: `return f();` where f
|
||||
// also returns a tagged union. The result is already
|
||||
// in (AX=tag, DX=v0, CX=v1) — no shuffle, no tag.
|
||||
// Mirrors the rhsreturnstagged path in cglet and the
|
||||
// !type_istagged guard in C cgen's N_RETURN.
|
||||
// in (AX=tag, DX=v0, CX=v1, R8=v2) — no shuffle, no
|
||||
// tag synthesis. Mirrors cstage cgen.c:8007 passthrough
|
||||
// = istagged && (vu == rt || type_eq(vt, cg_ret_type)).
|
||||
// TYPE-BASED predicate (was name-keyed via fnretlookupmod
|
||||
// IDENT/DOT-only) covers all callee shapes — including
|
||||
// deref-call N_UN(TK_STAR) per #201. Identity-on-peeled
|
||||
// handles the NAMED case (tinfocache memoizes per typedecl,
|
||||
// #191 lineage); the variant-pointer fallback handles the
|
||||
// anonymous case (each anonymous `(A|B)` decl gets its own
|
||||
// NAMED-less tinfo, so identity fails — e.g. cross-module
|
||||
// strings.byteindex returns the same anonymous (i32|void)
|
||||
// as bytes.index). Variant-pointer equality on the params
|
||||
// chain suffices because variants are primitives (single
|
||||
// tctx tinfo) or NAMED (per-decl identity); a full recursive
|
||||
// tinfo structural-eq helper is gated by #178.
|
||||
let forwardtagged: bool = false;
|
||||
if (rhs.kind == nkind.N_CALL) {
|
||||
let callee: *node = rhs.lhs;
|
||||
if (callee != nil) {
|
||||
let calleename: str;
|
||||
calleename.ptr = nil; calleename.len = 0;
|
||||
let cmod: str;
|
||||
cmod.ptr = nil; cmod.len = 0;
|
||||
if (callee.kind == nkind.N_IDENT) {
|
||||
calleename = callee.str;
|
||||
cmod = c.curmod;
|
||||
};
|
||||
if (callee.kind == nkind.N_DOT) {
|
||||
calleename = callee.str;
|
||||
if (callee.lhs != nil) {
|
||||
if (callee.lhs.kind == nkind.N_IDENT) {
|
||||
cmod = callee.lhs.str;
|
||||
};
|
||||
if (rhs.kind == nkind.N_CALL && rhs.type_ != nil && c.fnret != nil && c.fnret.type_ != nil) {
|
||||
let ru: *tinfo = rhs.type_: *tinfo;
|
||||
for (ru != nil && ru.kind == tykind.TY_NAMED) { ru = ru.under; };
|
||||
let fu: *tinfo = c.fnret.type_: *tinfo;
|
||||
for (fu != nil && fu.kind == tykind.TY_NAMED) { fu = fu.under; };
|
||||
if (ru != nil && fu != nil && ru.kind == tykind.TY_TAGGED && fu.kind == tykind.TY_TAGGED) {
|
||||
if (ru == fu) {
|
||||
forwardtagged = true;
|
||||
} else if (ru.nullable == fu.nullable) {
|
||||
let pa: *tparam = ru.params;
|
||||
let pb: *tparam = fu.params;
|
||||
let same: bool = true;
|
||||
for (pa != nil && pb != nil) {
|
||||
if (pa.type_ != pb.type_) { same = false; };
|
||||
pa = pa.tnext;
|
||||
pb = pb.tnext;
|
||||
};
|
||||
if (same && pa == nil && pb == nil) {
|
||||
forwardtagged = true;
|
||||
};
|
||||
};
|
||||
if (calleename.len > 0) {
|
||||
let rtyp: *node = fnretlookupmod(c, calleename, cmod);
|
||||
if (istaggedtype(c, rtyp)) { forwardtagged = true; };
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -357,32 +357,42 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
if (istaggedtype(c, c.fnret)) {
|
||||
// Forwarding a fallible call: `return f();` where f
|
||||
// also returns a tagged union. The result is already
|
||||
// in (AX=tag, DX=v0, CX=v1) — no shuffle, no tag.
|
||||
// Mirrors the rhsreturnstagged path in cglet and the
|
||||
// !type_istagged guard in C cgen's N_RETURN.
|
||||
// in (AX=tag, DX=v0, CX=v1, R8=v2) — no shuffle, no
|
||||
// tag synthesis. Mirrors cstage cgen.c:8007 passthrough
|
||||
// = istagged && (vu == rt || type_eq(vt, cg_ret_type)).
|
||||
// TYPE-BASED predicate (was name-keyed via fnretlookupmod
|
||||
// IDENT/DOT-only) covers all callee shapes — including
|
||||
// deref-call N_UN(TK_STAR) per #201. Identity-on-peeled
|
||||
// handles the NAMED case (tinfocache memoizes per typedecl,
|
||||
// #191 lineage); the variant-pointer fallback handles the
|
||||
// anonymous case (each anonymous `(A|B)` decl gets its own
|
||||
// NAMED-less tinfo, so identity fails — e.g. cross-module
|
||||
// strings.byteindex returns the same anonymous (i32|void)
|
||||
// as bytes.index). Variant-pointer equality on the params
|
||||
// chain suffices because variants are primitives (single
|
||||
// tctx tinfo) or NAMED (per-decl identity); a full recursive
|
||||
// tinfo structural-eq helper is gated by #178.
|
||||
let forwardtagged: bool = false;
|
||||
if (rhs.kind == nkind.N_CALL) {
|
||||
let callee: *node = rhs.lhs;
|
||||
if (callee != nil) {
|
||||
let calleename: str;
|
||||
calleename.ptr = nil; calleename.len = 0;
|
||||
let cmod: str;
|
||||
cmod.ptr = nil; cmod.len = 0;
|
||||
if (callee.kind == nkind.N_IDENT) {
|
||||
calleename = callee.str;
|
||||
cmod = c.curmod;
|
||||
};
|
||||
if (callee.kind == nkind.N_DOT) {
|
||||
calleename = callee.str;
|
||||
if (callee.lhs != nil) {
|
||||
if (callee.lhs.kind == nkind.N_IDENT) {
|
||||
cmod = callee.lhs.str;
|
||||
};
|
||||
if (rhs.kind == nkind.N_CALL && rhs.type_ != nil && c.fnret != nil && c.fnret.type_ != nil) {
|
||||
let ru: *tinfo = rhs.type_: *tinfo;
|
||||
for (ru != nil && ru.kind == tykind.TY_NAMED) { ru = ru.under; };
|
||||
let fu: *tinfo = c.fnret.type_: *tinfo;
|
||||
for (fu != nil && fu.kind == tykind.TY_NAMED) { fu = fu.under; };
|
||||
if (ru != nil && fu != nil && ru.kind == tykind.TY_TAGGED && fu.kind == tykind.TY_TAGGED) {
|
||||
if (ru == fu) {
|
||||
forwardtagged = true;
|
||||
} else if (ru.nullable == fu.nullable) {
|
||||
let pa: *tparam = ru.params;
|
||||
let pb: *tparam = fu.params;
|
||||
let same: bool = true;
|
||||
for (pa != nil && pb != nil) {
|
||||
if (pa.type_ != pb.type_) { same = false; };
|
||||
pa = pa.tnext;
|
||||
pb = pb.tnext;
|
||||
};
|
||||
if (same && pa == nil && pb == nil) {
|
||||
forwardtagged = true;
|
||||
};
|
||||
};
|
||||
if (calleename.len > 0) {
|
||||
let rtyp: *node = fnretlookupmod(c, calleename, cmod);
|
||||
if (istaggedtype(c, rtyp)) { forwardtagged = true; };
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -25018,32 +25018,42 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
if (istaggedtype(c, c.fnret)) {
|
||||
// Forwarding a fallible call: `return f();` where f
|
||||
// also returns a tagged union. The result is already
|
||||
// in (AX=tag, DX=v0, CX=v1) — no shuffle, no tag.
|
||||
// Mirrors the rhsreturnstagged path in cglet and the
|
||||
// !type_istagged guard in C cgen's N_RETURN.
|
||||
// in (AX=tag, DX=v0, CX=v1, R8=v2) — no shuffle, no
|
||||
// tag synthesis. Mirrors cstage cgen.c:8007 passthrough
|
||||
// = istagged && (vu == rt || type_eq(vt, cg_ret_type)).
|
||||
// TYPE-BASED predicate (was name-keyed via fnretlookupmod
|
||||
// IDENT/DOT-only) covers all callee shapes — including
|
||||
// deref-call N_UN(TK_STAR) per #201. Identity-on-peeled
|
||||
// handles the NAMED case (tinfocache memoizes per typedecl,
|
||||
// #191 lineage); the variant-pointer fallback handles the
|
||||
// anonymous case (each anonymous `(A|B)` decl gets its own
|
||||
// NAMED-less tinfo, so identity fails — e.g. cross-module
|
||||
// strings.byteindex returns the same anonymous (i32|void)
|
||||
// as bytes.index). Variant-pointer equality on the params
|
||||
// chain suffices because variants are primitives (single
|
||||
// tctx tinfo) or NAMED (per-decl identity); a full recursive
|
||||
// tinfo structural-eq helper is gated by #178.
|
||||
let forwardtagged: bool = false;
|
||||
if (rhs.kind == nkind.N_CALL) {
|
||||
let callee: *node = rhs.lhs;
|
||||
if (callee != nil) {
|
||||
let calleename: str;
|
||||
calleename.ptr = nil; calleename.len = 0;
|
||||
let cmod: str;
|
||||
cmod.ptr = nil; cmod.len = 0;
|
||||
if (callee.kind == nkind.N_IDENT) {
|
||||
calleename = callee.str;
|
||||
cmod = c.curmod;
|
||||
};
|
||||
if (callee.kind == nkind.N_DOT) {
|
||||
calleename = callee.str;
|
||||
if (callee.lhs != nil) {
|
||||
if (callee.lhs.kind == nkind.N_IDENT) {
|
||||
cmod = callee.lhs.str;
|
||||
};
|
||||
if (rhs.kind == nkind.N_CALL && rhs.type_ != nil && c.fnret != nil && c.fnret.type_ != nil) {
|
||||
let ru: *tinfo = rhs.type_: *tinfo;
|
||||
for (ru != nil && ru.kind == tykind.TY_NAMED) { ru = ru.under; };
|
||||
let fu: *tinfo = c.fnret.type_: *tinfo;
|
||||
for (fu != nil && fu.kind == tykind.TY_NAMED) { fu = fu.under; };
|
||||
if (ru != nil && fu != nil && ru.kind == tykind.TY_TAGGED && fu.kind == tykind.TY_TAGGED) {
|
||||
if (ru == fu) {
|
||||
forwardtagged = true;
|
||||
} else if (ru.nullable == fu.nullable) {
|
||||
let pa: *tparam = ru.params;
|
||||
let pb: *tparam = fu.params;
|
||||
let same: bool = true;
|
||||
for (pa != nil && pb != nil) {
|
||||
if (pa.type_ != pb.type_) { same = false; };
|
||||
pa = pa.tnext;
|
||||
pb = pb.tnext;
|
||||
};
|
||||
if (same && pa == nil && pb == nil) {
|
||||
forwardtagged = true;
|
||||
};
|
||||
};
|
||||
if (calleename.len > 0) {
|
||||
let rtyp: *node = fnretlookupmod(c, calleename, cmod);
|
||||
if (istaggedtype(c, rtyp)) { forwardtagged = true; };
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user