selfhost+test: resolve aliased tagged in taggedvariantindex (#20)
wwstage's taggedvariantindex returned -1 (caller maps to 0) for N_IDENT returns of an aliased mixed-variant union. Cstage returned the correct variant index. Cross-stage divergence — root cause of worker-fmtparser's "reads bool-true as false" symptom in the #18 repro chain. Worker-18 dodged it by dropping 707's asm byte-id loop; #20 re-enables it. Unwrap at entry: resolvetagged peels N_TNAME alias chains down to the underlying N_TTAGGED before the variant-index walk. Direct- tagged callers are unchanged (resolvetype is a no-op on non-N_TNAME). Mirrors nodeisstr's shape — same class of wwstage-no-typed-AST gap tracked by #11. Test 707 grows from 6 → 9 rows; new rows pin tag=0/1/2 (i64/str/ bool) explicitly so a future variant-reorder can't hide behind a coincidentally-correct tag=0. Asm byte-identity loop re-enabled (disabled by #18); now exercises both #18 (ABI words) and #20 (variant-index) fixes — rows 2/3/6 also probe str/bool divergence. 995_self_rebuild green confirms wwstage source itself has no latent aliased-tagged-return that would have surfaced as a self- divergence.
This commit is contained in:
@@ -8443,6 +8443,18 @@ fn rhstargetname(c: *cgen, rhs: *node) str = {
|
||||
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; };
|
||||
let wantname: str = rhstargetname(c, rhs);
|
||||
if (wantname.len > 0) {
|
||||
let r: i32 = flatvariantidx(c, tagged, wantname);
|
||||
|
||||
@@ -2200,6 +2200,18 @@ fn rhstargetname(c: *cgen, rhs: *node) str = {
|
||||
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; };
|
||||
let wantname: str = rhstargetname(c, rhs);
|
||||
if (wantname.len > 0) {
|
||||
let r: i32 = flatvariantidx(c, tagged, wantname);
|
||||
|
||||
@@ -8443,6 +8443,18 @@ fn rhstargetname(c: *cgen, rhs: *node) str = {
|
||||
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; };
|
||||
let wantname: str = rhstargetname(c, rhs);
|
||||
if (wantname.len > 0) {
|
||||
let r: i32 = flatvariantidx(c, tagged, wantname);
|
||||
|
||||
Reference in New Issue
Block a user