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:
2026-05-16 13:58:22 +09:00
parent 28f36d84d8
commit 09ce249226
4 changed files with 168 additions and 17 deletions

View File

@@ -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);