cgen: resolve chained-DOT fn-ptr field callees

fnptrcalleetfn's N_DOT arm required an N_IDENT base, so a.b.cb(...)
fell to the name registry with an empty module hint and emitted
CALL cb(SB) (undefined symbol; cstage calls the stamped ptr indirect).
dotbasestructinfo resolves the base chain through the struct registry
— each link a struct- or *struct-typed field — and the single-dot
path routes through the same resolver unchanged. Closes the last open
shape of the #59.8 name-keyed callee family. Fixture fnptrfield_chain;
corpus pin 1487/2974.
This commit is contained in:
2026-08-08 02:48:05 +09:00
parent 430c7e0546
commit 67f39256f6
4 changed files with 81 additions and 25 deletions

View File

@@ -64,6 +64,53 @@ fn findvariadicparam(ps: *syntax.node, nfixed_out: *i32) *syntax.node = {
// lookup missed entirely and the raw N_SPREAD leaked to pushargsrev
// as a single $0 word). Cstage mirror: cgcall's callee_params from
// the checker-stamped n->lhs->type (cmd/w6c/cgen.c TY_FN cu->params).
// dotbasestructinfo — the struct registry entry for a DOT base: a
// local ident (struct value or *struct), or a nested field chain
// (`a.b.cb(...)` — each link a struct- or *struct-typed field). The
// chained form previously fell to the name registry with an empty
// module hint (the #59.8 family's last open shape) and emitted
// CALL <leaf>(SB).
fn dotbasestructinfo(c: *cgen, base: *syntax.node) *structinfo = {
if (base == nil) { return nil; };
if (base.kind == syntax.nkind.N_IDENT) {
let lc: *local = localfindnode(c, base.str);
if (lc == nil) { return nil; };
let tn: *syntax.node = lc.tnode;
if (tn == nil) { return nil; };
let sname: str;
sname.ptr = nil; sname.len = 0;
if (tn.kind == syntax.nkind.N_TNAME) { sname = tn.str; };
if (tn.kind == syntax.nkind.N_TPTR) {
if (tn.lhs != nil) {
if (tn.lhs.kind == syntax.nkind.N_TNAME) {
sname = tn.lhs.str;
};
};
};
if (sname.len == 0) { return nil; };
return structlookup(c, sname);
};
if (base.kind != syntax.nkind.N_DOT) { return nil; };
let outer: *structinfo = dotbasestructinfo(c, base.lhs);
if (outer == nil) { return nil; };
let fi: *fieldinfo = outer.fields;
for (fi != nil) {
if (syntax.streq(fi.fname, base.str)) {
let ft: *syntax.node = fi.tnode;
if (ft == nil) { return nil; };
if (ft.kind == syntax.nkind.N_TPTR) {
if (ft.lhs != nil) { ft = ft.lhs; };
};
if (ft.kind == syntax.nkind.N_TNAME) {
return structlookup(c, ft.str);
};
return nil;
};
fi = fi.finext;
};
return nil;
};
fn fnptrcalleetfn(c: *cgen, callee: *syntax.node) *syntax.node = {
if (callee == nil) { return nil; };
if (callee.kind == syntax.nkind.N_IDENT) {
@@ -82,25 +129,7 @@ fn fnptrcalleetfn(c: *cgen, callee: *syntax.node) *syntax.node = {
return nil;
};
if (callee.kind != syntax.nkind.N_DOT) { return nil; };
let base: *syntax.node = callee.lhs;
if (base == nil) { return nil; };
if (base.kind != syntax.nkind.N_IDENT) { return nil; };
let lc: *local = localfindnode(c, base.str);
if (lc == nil) { return nil; };
let tn: *syntax.node = lc.tnode;
if (tn == nil) { return nil; };
let sname: str;
sname.ptr = nil; sname.len = 0;
if (tn.kind == syntax.nkind.N_TNAME) { sname = tn.str; };
if (tn.kind == syntax.nkind.N_TPTR) {
if (tn.lhs != nil) {
if (tn.lhs.kind == syntax.nkind.N_TNAME) {
sname = tn.lhs.str;
};
};
};
if (sname.len == 0) { return nil; };
let si: *structinfo = structlookup(c, sname);
let si: *structinfo = dotbasestructinfo(c, callee.lhs);
if (si == nil) { return nil; };
let fi: *fieldinfo = si.fields;
for (fi != nil) {