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.
This commit is contained in:
2026-05-24 00:16:14 +09:00
parent 901ddf20b9
commit e5092709e4
4 changed files with 75 additions and 471 deletions

View File

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

View File

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

View File

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

View File

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