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:
2026-05-28 23:17:22 +09:00
parent 635429bef8
commit b2ac8cbf81
5 changed files with 407 additions and 6 deletions

View File

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