From e5092709e40efddf5f961098b99701a35110123a Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Sun, 24 May 2026 00:16:14 +0900 Subject: [PATCH] selfhost/cmd/wcc: drop indexvaluetnode from cgassign + delete 3 retired AST-walkers (#69, #61d) cgassign's two element-store sites (N_DOT and N_INDEX index targets) fed the index node into indexvaluetnode to recover the element type. The tagged-store machinery reads tinfo directly post-#68, so both sites now read the checker- stamped element tinfo via lhs.type_: esz from .size (mirror #60), tagged gate via the shared istaggedtype/slotsize/cgwidentaggedstore path (all NAMED- peeling). cstage parity: cgen.c:3507-3523 (idx_eff(base->type)->sub->size). The new esz reads the element's natural .size, where the old elemsizeofc routed struct elements through slotsize -- so a padded-struct chained-index store (`[][]Point`) now matches cstage's ->size instead of diverging; that shape is untested (#7 indexbaseesz territory), so this is faithfulness, not a corpus change. With cgassign migrated, indexvaluetnode has zero external callers; dotfieldtnode's sole caller was indexvaluetnode; rhstargetname is self-recursive only -- all three retired (-132 LOC). This closes the A.6.3 AST-walker arc: the *node type-resolvers that existed because tinfo was lossy on nominal identity are gone, now that Phase-N (#63-#68) built the TY_NAMED layer and every consumer reads the stamped tinfo. make test 134/134, byte-id 950+990-997 hold. --- selfhost/cmd/w6c/main.combined.ww | 182 ++++----------------------- selfhost/cmd/wcc/cgenexpr.ww | 46 +++---- selfhost/cmd/wcc/cgenutil.ww | 136 +------------------- selfhost/cmd/wwdump/main.combined.ww | 182 ++++----------------------- 4 files changed, 75 insertions(+), 471 deletions(-) diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index 40467141..a7c3e03b 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -10552,7 +10552,7 @@ fn dynamicgrow(m: *state, need: i32) void = { // - index helpers: indexbaseesz, dotinnerstructptr, elemsizeof // - slot sizing: structlookup, primsize, slotsize, fieldsize, // registerstruct, collectstructs -// - rhs helpers: rhstargetname, taggedvariantindex +// - rhs helpers: taggedvariantindex // // Bundler pulls this in transitively via cgen.ww; consumers don't // need to `use cgenutil;` directly. @@ -11605,36 +11605,6 @@ fn elemsizeofc(c: *cgen, t: *node) i32 = { return slotsize(c, elem); }; -// indexvaluetnode — type node of the value produced by an N_INDEX -// expression. Walks base's type and returns its element. Recurses -// through chained N_INDEX so `names[i][k]` (names: **u8) resolves -// the outer base type to *u8 (the post-inner-index value type), so -// cgindex can compute the outer element size honestly. Mirrors -// cstage's `n->lhs->type` via typed-AST (cmd/w6c/cgen.c idx_eff). -// N_DOT base graduated (tasks #28/#30) so `obj.mat[i][k]` reads -// and `obj.arr[i] = v` tagged-element writes route through the -// same helper as the N_IDENT/N_INDEX bases #24/#27 graduated. -fn indexvaluetnode(c: *cgen, n: *node) *node = { - if (n == nil) { return nil; }; - if (n.kind != nkind.N_INDEX) { return nil; }; - let base: *node = n.lhs; - if (base == nil) { return nil; }; - let bt: *node = nil; - if (base.kind == nkind.N_IDENT) { - let lc: *local = localfindnode(c, base.str); - if (lc != nil) { bt = lc.tnode; } - else { bt = letvartnode(c, base.str); }; - }; - if (base.kind == nkind.N_INDEX) { bt = indexvaluetnode(c, base); }; - if (base.kind == nkind.N_DOT) { bt = dotfieldtnode(c, base); }; - if (bt == nil) { return nil; }; - let k: nkind = bt.kind; - if (k == nkind.N_TPTR) { return bt.lhs; }; - if (k == nkind.N_TSLICE) { return bt.lhs; }; - if (k == nkind.N_TARRAY) { return bt.lhs; }; - return nil; -}; - // nodeisunsigned — best-effort cgen-time inference from the AST. We // walk surface nodes (N_DOT now reads n.type_ — #55 A.6.3g): // nkind.N_INTLIT — never marked unsigned (no tsuffix plumbing yet) @@ -12660,72 +12630,6 @@ fn voidvariantindex(tagged: *node) i32 = { return -1; }; -// rhstargetname — for a returned value, what's its declared (or -// surface-inferred) type name? `expr: T` casts dictate T directly; -// bare strlit/intlit fall back to a primitive name. -fn rhstargetname(c: *cgen, rhs: *node) str = { - let nm: str; - nm.ptr = nil; nm.len = 0; - if (rhs == nil) { return nm; }; - // Unary `-` / `+` / `~` inherit the inner expression's type: - // cstage's checker stamps N_UN's type from cunop's inner walk, - // so `-42i64` is ty_i64 there. Wwstage has no checker stage — - // peel the operator here so a typed-int literal under a sign - // reaches its tsuffix branch below instead of falling into - // taggedvariantindex's "first non-str variant" fallback. Mirror - // of cmd/wcc/check.c cunop TK_MINUS/PLUS/TILDE returning t. - if (rhs.kind == nkind.N_UN) { - let op: tkind = rhs.op; - if (op == tkind.TK_MINUS || op == tkind.TK_PLUS - || op == tkind.TK_TILDE) { - if (rhs.lhs != nil) { - return rhstargetname(c, rhs.lhs); - }; - }; - }; - if (rhs.kind == nkind.N_CAST) { - let t: *node = rhs.rhs; - if (t != nil) { - if (t.kind == nkind.N_TNAME) { return t.str; }; - }; - return nm; - }; - if (rhs.kind == nkind.N_STRLIT) { return "str"; }; - if (rhs.kind == nkind.N_TRUE) { return "bool"; }; - if (rhs.kind == nkind.N_FALSE) { return "bool"; }; - if (rhs.kind == nkind.N_RUNELIT) { return "rune"; }; - if (rhs.kind == nkind.N_INTLIT) { - // Typed int literal (`42i64`, `3u8`): suffix names the - // concrete variant so flatvariantidx finds it. Untyped - // literals (tsuffix=="") fall through to the isstr scan. - let s: str = rhs.tsuffix; - if (s.len > 0) { return s; }; - }; - // `T{}` carries its type name on the lhs N_IDENT — the parser - // builds `N_STRUCTLIT{ lhs = N_IDENT("T"), list = fields }`. - // Needed so `return eof{};` (variant of a tagged union) resolves - // to the `eof` variant index rather than falling through to the - // "first non-str variant" fallback in taggedvariantindex. - if (rhs.kind == nkind.N_STRUCTLIT) { - let tref: *node = rhs.lhs; - if (tref != nil) { - if (tref.kind == nkind.N_IDENT) { return tref.str; }; - if (tref.kind == nkind.N_TNAME) { return tref.str; }; - }; - return nm; - }; - if (rhs.kind == nkind.N_IDENT) { - let lc: *local = localfindnode(c, rhs.str); - if (lc != nil) { - let tn: *node = lc.tnode; - if (tn != nil) { - if (tn.kind == nkind.N_TNAME) { return tn.str; }; - }; - }; - }; - return nm; -}; - // taggedvariantindex — given the tagged-union type expr and the // returned value's surface type, find the matching variant's 0-based // index. Compare by exact type name first; if no match, fall back to @@ -12977,44 +12881,6 @@ fn rhstaggedident(c: *cgen, src: *node) *node = { return resolvetagged(c, tn); }; -// dotfieldtnode — for an N_DOT src whose base is a local ident or -// *struct, return the declared type node of the named field, or nil -// if the shape doesn't resolve (e.g. enum-member access, pseudo- -// field `.len`, top-level global). Used by rhstaggedabicall and -// related predicates to walk into the field's tagged type. -fn dotfieldtnode(c: *cgen, n: *node) *node = { - if (n == nil) { return nil; }; - if (n.kind != nkind.N_DOT) { return nil; }; - let base: *node = n.lhs; - let fld: str = n.str; - if (base == nil) { return nil; }; - if (base.kind != nkind.N_IDENT) { return nil; }; - let lc: *local = localfindnode(c, base.str); - let btn: *node = nil; - if (lc != nil) { btn = lc.tnode; } - else { btn = letvartnode(c, base.str); }; - if (btn == nil) { return nil; }; - let bk: nkind = btn.kind; - let sname: str; - sname.ptr = nil; sname.len = 0; - if (bk == nkind.N_TPTR) { - let inner: *node = btn.lhs; - if (inner != nil) { - if (inner.kind == nkind.N_TNAME) { sname = inner.str; }; - }; - }; - if (bk == nkind.N_TNAME) { sname = btn.str; }; - if (sname.len == 0) { return nil; }; - let si: *structinfo = structlookup(c, sname); - if (si == nil) { return nil; }; - let fi: *fieldinfo = si.fields; - for (fi != nil) { - if (streq(fi.fname, fld)) { return fi.tnode; }; - fi = fi.finext; - }; - return nil; -}; - // rhstaggedabicall — does `src` produce a tagged value via the AX/DX/CX // return ABI? True for N_CALL of a tagged-returning fn, N_INDEX of a // tagged-element base, and N_DOT of a tagged-typed struct field (after @@ -17739,30 +17605,32 @@ fn cgassign(c: *cgen, n: *node) void = { }; } else { if (base.kind == nkind.N_DOT) { esz = indexbaseesz(c, base); - // Without this, the tagged-element gate - // below (keyed on elemtn) misses for - // `obj.arr[i] = v` over an [N]Tagged field - // and the store falls through to scalar — - // task #30, sister of the cgindex N_INDEX- - // base fix #24. indexvaluetnode now handles - // N_DOT base, so the element type drops out - // of the same helper. - let bt: *node = indexvaluetnode(c, lhs); - if (bt != nil) { elemtn = bt; }; + // Tagged-element gate (below) keys on the store + // target's element type. lhs.type_ is the + // checker-stamped element tinfo of the N_INDEX, + // so carry lhs and let the gate read it via + // .type_ — same idiom as cgindex's n.type_ read + // (#60). Drops the indexvaluetnode walk for the + // N_DOT base (#69/#61d, was #30). cstage reads + // idx_eff(base->type)->sub (cmd/w6c/cgen.c:3517- + // 3523). + let dt: *tinfo = lhs.type_: *tinfo; + if (dt != nil) { elemtn = lhs; }; } else { if (base.kind == nkind.N_INDEX) { // Chained-write write-side parallel of the - // cgindex N_INDEX-base arm graduated in #24: - // `names[i][k] = v` (names: **u8) — outer - // base is the inner N_INDEX whose value-type - // is *u8, so the outer element is u8 and - // the store is MOVB, not MOVQ. - let bt: *node = indexvaluetnode(c, base); - if (bt != nil) { - esz = elemsizeofc(c, bt); - let bk2: nkind = bt.kind; - if (bk2 == nkind.N_TPTR) { elemtn = bt.lhs; }; - if (bk2 == nkind.N_TSLICE) { elemtn = bt.lhs; }; - if (bk2 == nkind.N_TARRAY) { elemtn = bt.lhs; }; + // cgindex N_INDEX-base arm (#24): `names[i][k] + // = v` (names: **u8) — outer element is u8 so + // the store is MOVB, not MOVQ. lhs.type_ is the + // checker-stamped outer element tinfo; esz is + // its natural size and the gate reads it via + // .type_. Drops the indexvaluetnode walk + // (#69/#61d, mirror #60). cstage: esz = + // idx_eff(base->type)->sub->size + // (cmd/w6c/cgen.c:3517-3518). + let et: *tinfo = lhs.type_: *tinfo; + if (et != nil) { + esz = et.size: i32; + elemtn = lhs; }; };};}; }; diff --git a/selfhost/cmd/wcc/cgenexpr.ww b/selfhost/cmd/wcc/cgenexpr.ww index f3815eaf..1c0acfdd 100644 --- a/selfhost/cmd/wcc/cgenexpr.ww +++ b/selfhost/cmd/wcc/cgenexpr.ww @@ -3712,30 +3712,32 @@ fn cgassign(c: *cgen, n: *node) void = { }; } else { if (base.kind == nkind.N_DOT) { esz = indexbaseesz(c, base); - // Without this, the tagged-element gate - // below (keyed on elemtn) misses for - // `obj.arr[i] = v` over an [N]Tagged field - // and the store falls through to scalar — - // task #30, sister of the cgindex N_INDEX- - // base fix #24. indexvaluetnode now handles - // N_DOT base, so the element type drops out - // of the same helper. - let bt: *node = indexvaluetnode(c, lhs); - if (bt != nil) { elemtn = bt; }; + // Tagged-element gate (below) keys on the store + // target's element type. lhs.type_ is the + // checker-stamped element tinfo of the N_INDEX, + // so carry lhs and let the gate read it via + // .type_ — same idiom as cgindex's n.type_ read + // (#60). Drops the indexvaluetnode walk for the + // N_DOT base (#69/#61d, was #30). cstage reads + // idx_eff(base->type)->sub (cmd/w6c/cgen.c:3517- + // 3523). + let dt: *tinfo = lhs.type_: *tinfo; + if (dt != nil) { elemtn = lhs; }; } else { if (base.kind == nkind.N_INDEX) { // Chained-write write-side parallel of the - // cgindex N_INDEX-base arm graduated in #24: - // `names[i][k] = v` (names: **u8) — outer - // base is the inner N_INDEX whose value-type - // is *u8, so the outer element is u8 and - // the store is MOVB, not MOVQ. - let bt: *node = indexvaluetnode(c, base); - if (bt != nil) { - esz = elemsizeofc(c, bt); - let bk2: nkind = bt.kind; - if (bk2 == nkind.N_TPTR) { elemtn = bt.lhs; }; - if (bk2 == nkind.N_TSLICE) { elemtn = bt.lhs; }; - if (bk2 == nkind.N_TARRAY) { elemtn = bt.lhs; }; + // cgindex N_INDEX-base arm (#24): `names[i][k] + // = v` (names: **u8) — outer element is u8 so + // the store is MOVB, not MOVQ. lhs.type_ is the + // checker-stamped outer element tinfo; esz is + // its natural size and the gate reads it via + // .type_. Drops the indexvaluetnode walk + // (#69/#61d, mirror #60). cstage: esz = + // idx_eff(base->type)->sub->size + // (cmd/w6c/cgen.c:3517-3518). + let et: *tinfo = lhs.type_: *tinfo; + if (et != nil) { + esz = et.size: i32; + elemtn = lhs; }; };};}; }; diff --git a/selfhost/cmd/wcc/cgenutil.ww b/selfhost/cmd/wcc/cgenutil.ww index 6a09c585..73720e1e 100644 --- a/selfhost/cmd/wcc/cgenutil.ww +++ b/selfhost/cmd/wcc/cgenutil.ww @@ -7,7 +7,7 @@ // - index helpers: indexbaseesz, dotinnerstructptr, elemsizeof // - slot sizing: structlookup, primsize, slotsize, fieldsize, // registerstruct, collectstructs -// - rhs helpers: rhstargetname, taggedvariantindex +// - rhs helpers: taggedvariantindex // // Bundler pulls this in transitively via cgen.ww; consumers don't // need to `use cgenutil;` directly. @@ -1060,36 +1060,6 @@ fn elemsizeofc(c: *cgen, t: *node) i32 = { return slotsize(c, elem); }; -// indexvaluetnode — type node of the value produced by an N_INDEX -// expression. Walks base's type and returns its element. Recurses -// through chained N_INDEX so `names[i][k]` (names: **u8) resolves -// the outer base type to *u8 (the post-inner-index value type), so -// cgindex can compute the outer element size honestly. Mirrors -// cstage's `n->lhs->type` via typed-AST (cmd/w6c/cgen.c idx_eff). -// N_DOT base graduated (tasks #28/#30) so `obj.mat[i][k]` reads -// and `obj.arr[i] = v` tagged-element writes route through the -// same helper as the N_IDENT/N_INDEX bases #24/#27 graduated. -fn indexvaluetnode(c: *cgen, n: *node) *node = { - if (n == nil) { return nil; }; - if (n.kind != nkind.N_INDEX) { return nil; }; - let base: *node = n.lhs; - if (base == nil) { return nil; }; - let bt: *node = nil; - if (base.kind == nkind.N_IDENT) { - let lc: *local = localfindnode(c, base.str); - if (lc != nil) { bt = lc.tnode; } - else { bt = letvartnode(c, base.str); }; - }; - if (base.kind == nkind.N_INDEX) { bt = indexvaluetnode(c, base); }; - if (base.kind == nkind.N_DOT) { bt = dotfieldtnode(c, base); }; - if (bt == nil) { return nil; }; - let k: nkind = bt.kind; - if (k == nkind.N_TPTR) { return bt.lhs; }; - if (k == nkind.N_TSLICE) { return bt.lhs; }; - if (k == nkind.N_TARRAY) { return bt.lhs; }; - return nil; -}; - // nodeisunsigned — best-effort cgen-time inference from the AST. We // walk surface nodes (N_DOT now reads n.type_ — #55 A.6.3g): // nkind.N_INTLIT — never marked unsigned (no tsuffix plumbing yet) @@ -2115,72 +2085,6 @@ fn voidvariantindex(tagged: *node) i32 = { return -1; }; -// rhstargetname — for a returned value, what's its declared (or -// surface-inferred) type name? `expr: T` casts dictate T directly; -// bare strlit/intlit fall back to a primitive name. -fn rhstargetname(c: *cgen, rhs: *node) str = { - let nm: str; - nm.ptr = nil; nm.len = 0; - if (rhs == nil) { return nm; }; - // Unary `-` / `+` / `~` inherit the inner expression's type: - // cstage's checker stamps N_UN's type from cunop's inner walk, - // so `-42i64` is ty_i64 there. Wwstage has no checker stage — - // peel the operator here so a typed-int literal under a sign - // reaches its tsuffix branch below instead of falling into - // taggedvariantindex's "first non-str variant" fallback. Mirror - // of cmd/wcc/check.c cunop TK_MINUS/PLUS/TILDE returning t. - if (rhs.kind == nkind.N_UN) { - let op: tkind = rhs.op; - if (op == tkind.TK_MINUS || op == tkind.TK_PLUS - || op == tkind.TK_TILDE) { - if (rhs.lhs != nil) { - return rhstargetname(c, rhs.lhs); - }; - }; - }; - if (rhs.kind == nkind.N_CAST) { - let t: *node = rhs.rhs; - if (t != nil) { - if (t.kind == nkind.N_TNAME) { return t.str; }; - }; - return nm; - }; - if (rhs.kind == nkind.N_STRLIT) { return "str"; }; - if (rhs.kind == nkind.N_TRUE) { return "bool"; }; - if (rhs.kind == nkind.N_FALSE) { return "bool"; }; - if (rhs.kind == nkind.N_RUNELIT) { return "rune"; }; - if (rhs.kind == nkind.N_INTLIT) { - // Typed int literal (`42i64`, `3u8`): suffix names the - // concrete variant so flatvariantidx finds it. Untyped - // literals (tsuffix=="") fall through to the isstr scan. - let s: str = rhs.tsuffix; - if (s.len > 0) { return s; }; - }; - // `T{}` carries its type name on the lhs N_IDENT — the parser - // builds `N_STRUCTLIT{ lhs = N_IDENT("T"), list = fields }`. - // Needed so `return eof{};` (variant of a tagged union) resolves - // to the `eof` variant index rather than falling through to the - // "first non-str variant" fallback in taggedvariantindex. - if (rhs.kind == nkind.N_STRUCTLIT) { - let tref: *node = rhs.lhs; - if (tref != nil) { - if (tref.kind == nkind.N_IDENT) { return tref.str; }; - if (tref.kind == nkind.N_TNAME) { return tref.str; }; - }; - return nm; - }; - if (rhs.kind == nkind.N_IDENT) { - let lc: *local = localfindnode(c, rhs.str); - if (lc != nil) { - let tn: *node = lc.tnode; - if (tn != nil) { - if (tn.kind == nkind.N_TNAME) { return tn.str; }; - }; - }; - }; - return nm; -}; - // taggedvariantindex — given the tagged-union type expr and the // returned value's surface type, find the matching variant's 0-based // index. Compare by exact type name first; if no match, fall back to @@ -2432,44 +2336,6 @@ fn rhstaggedident(c: *cgen, src: *node) *node = { return resolvetagged(c, tn); }; -// dotfieldtnode — for an N_DOT src whose base is a local ident or -// *struct, return the declared type node of the named field, or nil -// if the shape doesn't resolve (e.g. enum-member access, pseudo- -// field `.len`, top-level global). Used by rhstaggedabicall and -// related predicates to walk into the field's tagged type. -fn dotfieldtnode(c: *cgen, n: *node) *node = { - if (n == nil) { return nil; }; - if (n.kind != nkind.N_DOT) { return nil; }; - let base: *node = n.lhs; - let fld: str = n.str; - if (base == nil) { return nil; }; - if (base.kind != nkind.N_IDENT) { return nil; }; - let lc: *local = localfindnode(c, base.str); - let btn: *node = nil; - if (lc != nil) { btn = lc.tnode; } - else { btn = letvartnode(c, base.str); }; - if (btn == nil) { return nil; }; - let bk: nkind = btn.kind; - let sname: str; - sname.ptr = nil; sname.len = 0; - if (bk == nkind.N_TPTR) { - let inner: *node = btn.lhs; - if (inner != nil) { - if (inner.kind == nkind.N_TNAME) { sname = inner.str; }; - }; - }; - if (bk == nkind.N_TNAME) { sname = btn.str; }; - if (sname.len == 0) { return nil; }; - let si: *structinfo = structlookup(c, sname); - if (si == nil) { return nil; }; - let fi: *fieldinfo = si.fields; - for (fi != nil) { - if (streq(fi.fname, fld)) { return fi.tnode; }; - fi = fi.finext; - }; - return nil; -}; - // rhstaggedabicall — does `src` produce a tagged value via the AX/DX/CX // return ABI? True for N_CALL of a tagged-returning fn, N_INDEX of a // tagged-element base, and N_DOT of a tagged-typed struct field (after diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index a91165a0..e48e4593 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -10552,7 +10552,7 @@ fn dynamicgrow(m: *state, need: i32) void = { // - index helpers: indexbaseesz, dotinnerstructptr, elemsizeof // - slot sizing: structlookup, primsize, slotsize, fieldsize, // registerstruct, collectstructs -// - rhs helpers: rhstargetname, taggedvariantindex +// - rhs helpers: taggedvariantindex // // Bundler pulls this in transitively via cgen.ww; consumers don't // need to `use cgenutil;` directly. @@ -11605,36 +11605,6 @@ fn elemsizeofc(c: *cgen, t: *node) i32 = { return slotsize(c, elem); }; -// indexvaluetnode — type node of the value produced by an N_INDEX -// expression. Walks base's type and returns its element. Recurses -// through chained N_INDEX so `names[i][k]` (names: **u8) resolves -// the outer base type to *u8 (the post-inner-index value type), so -// cgindex can compute the outer element size honestly. Mirrors -// cstage's `n->lhs->type` via typed-AST (cmd/w6c/cgen.c idx_eff). -// N_DOT base graduated (tasks #28/#30) so `obj.mat[i][k]` reads -// and `obj.arr[i] = v` tagged-element writes route through the -// same helper as the N_IDENT/N_INDEX bases #24/#27 graduated. -fn indexvaluetnode(c: *cgen, n: *node) *node = { - if (n == nil) { return nil; }; - if (n.kind != nkind.N_INDEX) { return nil; }; - let base: *node = n.lhs; - if (base == nil) { return nil; }; - let bt: *node = nil; - if (base.kind == nkind.N_IDENT) { - let lc: *local = localfindnode(c, base.str); - if (lc != nil) { bt = lc.tnode; } - else { bt = letvartnode(c, base.str); }; - }; - if (base.kind == nkind.N_INDEX) { bt = indexvaluetnode(c, base); }; - if (base.kind == nkind.N_DOT) { bt = dotfieldtnode(c, base); }; - if (bt == nil) { return nil; }; - let k: nkind = bt.kind; - if (k == nkind.N_TPTR) { return bt.lhs; }; - if (k == nkind.N_TSLICE) { return bt.lhs; }; - if (k == nkind.N_TARRAY) { return bt.lhs; }; - return nil; -}; - // nodeisunsigned — best-effort cgen-time inference from the AST. We // walk surface nodes (N_DOT now reads n.type_ — #55 A.6.3g): // nkind.N_INTLIT — never marked unsigned (no tsuffix plumbing yet) @@ -12660,72 +12630,6 @@ fn voidvariantindex(tagged: *node) i32 = { return -1; }; -// rhstargetname — for a returned value, what's its declared (or -// surface-inferred) type name? `expr: T` casts dictate T directly; -// bare strlit/intlit fall back to a primitive name. -fn rhstargetname(c: *cgen, rhs: *node) str = { - let nm: str; - nm.ptr = nil; nm.len = 0; - if (rhs == nil) { return nm; }; - // Unary `-` / `+` / `~` inherit the inner expression's type: - // cstage's checker stamps N_UN's type from cunop's inner walk, - // so `-42i64` is ty_i64 there. Wwstage has no checker stage — - // peel the operator here so a typed-int literal under a sign - // reaches its tsuffix branch below instead of falling into - // taggedvariantindex's "first non-str variant" fallback. Mirror - // of cmd/wcc/check.c cunop TK_MINUS/PLUS/TILDE returning t. - if (rhs.kind == nkind.N_UN) { - let op: tkind = rhs.op; - if (op == tkind.TK_MINUS || op == tkind.TK_PLUS - || op == tkind.TK_TILDE) { - if (rhs.lhs != nil) { - return rhstargetname(c, rhs.lhs); - }; - }; - }; - if (rhs.kind == nkind.N_CAST) { - let t: *node = rhs.rhs; - if (t != nil) { - if (t.kind == nkind.N_TNAME) { return t.str; }; - }; - return nm; - }; - if (rhs.kind == nkind.N_STRLIT) { return "str"; }; - if (rhs.kind == nkind.N_TRUE) { return "bool"; }; - if (rhs.kind == nkind.N_FALSE) { return "bool"; }; - if (rhs.kind == nkind.N_RUNELIT) { return "rune"; }; - if (rhs.kind == nkind.N_INTLIT) { - // Typed int literal (`42i64`, `3u8`): suffix names the - // concrete variant so flatvariantidx finds it. Untyped - // literals (tsuffix=="") fall through to the isstr scan. - let s: str = rhs.tsuffix; - if (s.len > 0) { return s; }; - }; - // `T{}` carries its type name on the lhs N_IDENT — the parser - // builds `N_STRUCTLIT{ lhs = N_IDENT("T"), list = fields }`. - // Needed so `return eof{};` (variant of a tagged union) resolves - // to the `eof` variant index rather than falling through to the - // "first non-str variant" fallback in taggedvariantindex. - if (rhs.kind == nkind.N_STRUCTLIT) { - let tref: *node = rhs.lhs; - if (tref != nil) { - if (tref.kind == nkind.N_IDENT) { return tref.str; }; - if (tref.kind == nkind.N_TNAME) { return tref.str; }; - }; - return nm; - }; - if (rhs.kind == nkind.N_IDENT) { - let lc: *local = localfindnode(c, rhs.str); - if (lc != nil) { - let tn: *node = lc.tnode; - if (tn != nil) { - if (tn.kind == nkind.N_TNAME) { return tn.str; }; - }; - }; - }; - return nm; -}; - // taggedvariantindex — given the tagged-union type expr and the // returned value's surface type, find the matching variant's 0-based // index. Compare by exact type name first; if no match, fall back to @@ -12977,44 +12881,6 @@ fn rhstaggedident(c: *cgen, src: *node) *node = { return resolvetagged(c, tn); }; -// dotfieldtnode — for an N_DOT src whose base is a local ident or -// *struct, return the declared type node of the named field, or nil -// if the shape doesn't resolve (e.g. enum-member access, pseudo- -// field `.len`, top-level global). Used by rhstaggedabicall and -// related predicates to walk into the field's tagged type. -fn dotfieldtnode(c: *cgen, n: *node) *node = { - if (n == nil) { return nil; }; - if (n.kind != nkind.N_DOT) { return nil; }; - let base: *node = n.lhs; - let fld: str = n.str; - if (base == nil) { return nil; }; - if (base.kind != nkind.N_IDENT) { return nil; }; - let lc: *local = localfindnode(c, base.str); - let btn: *node = nil; - if (lc != nil) { btn = lc.tnode; } - else { btn = letvartnode(c, base.str); }; - if (btn == nil) { return nil; }; - let bk: nkind = btn.kind; - let sname: str; - sname.ptr = nil; sname.len = 0; - if (bk == nkind.N_TPTR) { - let inner: *node = btn.lhs; - if (inner != nil) { - if (inner.kind == nkind.N_TNAME) { sname = inner.str; }; - }; - }; - if (bk == nkind.N_TNAME) { sname = btn.str; }; - if (sname.len == 0) { return nil; }; - let si: *structinfo = structlookup(c, sname); - if (si == nil) { return nil; }; - let fi: *fieldinfo = si.fields; - for (fi != nil) { - if (streq(fi.fname, fld)) { return fi.tnode; }; - fi = fi.finext; - }; - return nil; -}; - // rhstaggedabicall — does `src` produce a tagged value via the AX/DX/CX // return ABI? True for N_CALL of a tagged-returning fn, N_INDEX of a // tagged-element base, and N_DOT of a tagged-typed struct field (after @@ -17739,30 +17605,32 @@ fn cgassign(c: *cgen, n: *node) void = { }; } else { if (base.kind == nkind.N_DOT) { esz = indexbaseesz(c, base); - // Without this, the tagged-element gate - // below (keyed on elemtn) misses for - // `obj.arr[i] = v` over an [N]Tagged field - // and the store falls through to scalar — - // task #30, sister of the cgindex N_INDEX- - // base fix #24. indexvaluetnode now handles - // N_DOT base, so the element type drops out - // of the same helper. - let bt: *node = indexvaluetnode(c, lhs); - if (bt != nil) { elemtn = bt; }; + // Tagged-element gate (below) keys on the store + // target's element type. lhs.type_ is the + // checker-stamped element tinfo of the N_INDEX, + // so carry lhs and let the gate read it via + // .type_ — same idiom as cgindex's n.type_ read + // (#60). Drops the indexvaluetnode walk for the + // N_DOT base (#69/#61d, was #30). cstage reads + // idx_eff(base->type)->sub (cmd/w6c/cgen.c:3517- + // 3523). + let dt: *tinfo = lhs.type_: *tinfo; + if (dt != nil) { elemtn = lhs; }; } else { if (base.kind == nkind.N_INDEX) { // Chained-write write-side parallel of the - // cgindex N_INDEX-base arm graduated in #24: - // `names[i][k] = v` (names: **u8) — outer - // base is the inner N_INDEX whose value-type - // is *u8, so the outer element is u8 and - // the store is MOVB, not MOVQ. - let bt: *node = indexvaluetnode(c, base); - if (bt != nil) { - esz = elemsizeofc(c, bt); - let bk2: nkind = bt.kind; - if (bk2 == nkind.N_TPTR) { elemtn = bt.lhs; }; - if (bk2 == nkind.N_TSLICE) { elemtn = bt.lhs; }; - if (bk2 == nkind.N_TARRAY) { elemtn = bt.lhs; }; + // cgindex N_INDEX-base arm (#24): `names[i][k] + // = v` (names: **u8) — outer element is u8 so + // the store is MOVB, not MOVQ. lhs.type_ is the + // checker-stamped outer element tinfo; esz is + // its natural size and the gate reads it via + // .type_. Drops the indexvaluetnode walk + // (#69/#61d, mirror #60). cstage: esz = + // idx_eff(base->type)->sub->size + // (cmd/w6c/cgen.c:3517-3518). + let et: *tinfo = lhs.type_: *tinfo; + if (et != nil) { + esz = et.size: i32; + elemtn = lhs; }; };};}; };