wcc: peel N_TNAME alias chain on cgdot receiver before lkind decision (#191)
`type vs = *vt; fn(s: vs) s.field` linked-failed in wwstage with `undefined reference to field' — cgdot read lc.tnode.kind without first walking N_TNAME aliases, so lkind stayed N_TNAME (not the underlying N_TPTR), structlookupchain missed (`vs` isn't a struct alias), and the lookup fell through to the SB-global fallback that emits `MOVQ <field>(SB), AX`. Mirror cstage type_chase_named (cmd/w6c/cgen.c:144-155) via an aliaslookup loop, stopping at struct aliases so the existing direct-struct N_TNAME arm stays byte-id with pre-fix #22 callers. LOOP (not single-peel) — Phase-N builds N_TNAME chains (project_tinfo_lossy_nominal), depth-2+ aliases require iteration. Inner peel on the pointee is unnecessary: the existing structlookupchain already walks N_TNAME chains via aliaslookup (cgenutil.ww:1266-1276); row 4 of probe 769 proves the inner-chain depth-3 path stays green without an explicit inner peel. Probe test/wcc/769_dot_aliased_ptr.c covers 4 rows (fn-param read, let-binding read, double-alias receiver, pointee-alias chain), per-row runtime + byte-id gates. Files inline two sibling bugs surfaced during impl (cgassign write-side silent-drop, chained-N_DOT spine link-fail) plus a cstage checker assignability gap on chain-depth-2 aliases — all out-of-scope per rule 11 split.
This commit is contained in:
@@ -19284,8 +19284,24 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
let lc: *local = localfindnode(c, nm);
|
||||
if (lc != nil) {
|
||||
let tn: *node = lc.tnode;
|
||||
let lkind: nkind = nkind.N_NONE;
|
||||
if (tn != nil) { lkind = tn.kind; };
|
||||
// Receiver is a NAMED alias chain — peel via aliaslookup
|
||||
// until tn exposes a non-N_TNAME kind (or a struct alias).
|
||||
// Without this, `type vs = *vt` leaves lkind == N_TNAME and
|
||||
// structlookupchain misses (vs is not a struct), so we fall
|
||||
// through to the SB-global fallback and emit a wrong
|
||||
// `MOVQ <fld>(SB), AX`. Mirror cstage type_chase_named
|
||||
// (cmd/w6c/cgen.c:144-155); LOOP, not single-peel — Phase-N
|
||||
// builds NAMED chains (project_tinfo_lossy_nominal). Stops
|
||||
// at struct aliases so the existing direct-struct arm below
|
||||
// stays byte-id with pre-fix #22 callers. #191.
|
||||
for (tn != nil && tn.kind == nkind.N_TNAME) {
|
||||
if (structlookup(c, tn.str) != nil) { break; };
|
||||
let nx: *node = aliaslookup(c, tn.str);
|
||||
if (nx == nil) { break; };
|
||||
tn = nx;
|
||||
};
|
||||
if (tn == nil) { return; };
|
||||
let lkind: nkind = tn.kind;
|
||||
// Pointer-to-struct: deref then field load.
|
||||
if (lkind == nkind.N_TPTR) {
|
||||
let inner: *node = tn.lhs;
|
||||
|
||||
@@ -1672,8 +1672,24 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
let lc: *local = localfindnode(c, nm);
|
||||
if (lc != nil) {
|
||||
let tn: *node = lc.tnode;
|
||||
let lkind: nkind = nkind.N_NONE;
|
||||
if (tn != nil) { lkind = tn.kind; };
|
||||
// Receiver is a NAMED alias chain — peel via aliaslookup
|
||||
// until tn exposes a non-N_TNAME kind (or a struct alias).
|
||||
// Without this, `type vs = *vt` leaves lkind == N_TNAME and
|
||||
// structlookupchain misses (vs is not a struct), so we fall
|
||||
// through to the SB-global fallback and emit a wrong
|
||||
// `MOVQ <fld>(SB), AX`. Mirror cstage type_chase_named
|
||||
// (cmd/w6c/cgen.c:144-155); LOOP, not single-peel — Phase-N
|
||||
// builds NAMED chains (project_tinfo_lossy_nominal). Stops
|
||||
// at struct aliases so the existing direct-struct arm below
|
||||
// stays byte-id with pre-fix #22 callers. #191.
|
||||
for (tn != nil && tn.kind == nkind.N_TNAME) {
|
||||
if (structlookup(c, tn.str) != nil) { break; };
|
||||
let nx: *node = aliaslookup(c, tn.str);
|
||||
if (nx == nil) { break; };
|
||||
tn = nx;
|
||||
};
|
||||
if (tn == nil) { return; };
|
||||
let lkind: nkind = tn.kind;
|
||||
// Pointer-to-struct: deref then field load.
|
||||
if (lkind == nkind.N_TPTR) {
|
||||
let inner: *node = tn.lhs;
|
||||
|
||||
@@ -19284,8 +19284,24 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
let lc: *local = localfindnode(c, nm);
|
||||
if (lc != nil) {
|
||||
let tn: *node = lc.tnode;
|
||||
let lkind: nkind = nkind.N_NONE;
|
||||
if (tn != nil) { lkind = tn.kind; };
|
||||
// Receiver is a NAMED alias chain — peel via aliaslookup
|
||||
// until tn exposes a non-N_TNAME kind (or a struct alias).
|
||||
// Without this, `type vs = *vt` leaves lkind == N_TNAME and
|
||||
// structlookupchain misses (vs is not a struct), so we fall
|
||||
// through to the SB-global fallback and emit a wrong
|
||||
// `MOVQ <fld>(SB), AX`. Mirror cstage type_chase_named
|
||||
// (cmd/w6c/cgen.c:144-155); LOOP, not single-peel — Phase-N
|
||||
// builds NAMED chains (project_tinfo_lossy_nominal). Stops
|
||||
// at struct aliases so the existing direct-struct arm below
|
||||
// stays byte-id with pre-fix #22 callers. #191.
|
||||
for (tn != nil && tn.kind == nkind.N_TNAME) {
|
||||
if (structlookup(c, tn.str) != nil) { break; };
|
||||
let nx: *node = aliaslookup(c, tn.str);
|
||||
if (nx == nil) { break; };
|
||||
tn = nx;
|
||||
};
|
||||
if (tn == nil) { return; };
|
||||
let lkind: nkind = tn.kind;
|
||||
// Pointer-to-struct: deref then field load.
|
||||
if (lkind == nkind.N_TPTR) {
|
||||
let inner: *node = tn.lhs;
|
||||
|
||||
Reference in New Issue
Block a user