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

@@ -32,9 +32,9 @@ categories out of the ordinary developer target.
| Fixed point and self-host | `test-bootstrap` |
| Host linker/platform behavior | `test-platform` |
The live declarative compiler corpus has 1,486 fixtures and 2,972 C/WW cells:
The live declarative compiler corpus has 1,487 fixtures and 2,974 C/WW cells:
338 expected rejections (314 shared and 24 stage-specific), 17 compile-only
successes, 191 exit-zero programs, and 940 explicit-exit programs.
successes, 191 exit-zero programs, and 941 explicit-exit programs.
147 native C carriers remain. They are partitioned exactly once as five
in-process units, 24 byte/artifact gates, six bootstrap gates, one platform

View File

@@ -1,13 +1,13 @@
package wwfixture;
def protocolversion: i32 = 1;
def corpuscount: i32 = 1486;
def corpuscount: i32 = 1487;
def errorcount: i32 = 338;
def compilecount: i32 = 17;
def runcount: i32 = 191;
def runexitcount: i32 = 940;
def nativecount: i32 = 2972;
def corpushash: str = "ab4b1d1ffc078b23de0a70911740511e873cd8d6f76009c4719d704bb36e6133";
def runexitcount: i32 = 941;
def nativecount: i32 = 2974;
def corpushash: str = "2520ada0b1687fab6eed6073a263c620fa4cbab6dc8156f7d51bcf8b14a9ec4a";
type directive = enum i32 {
ERROR = 0,

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) {

View File

@@ -0,0 +1,27 @@
//ww:run-exit 36
package main;
// A call through a chained-DOT fn-ptr field (a.b.cb(...)). The wwstage
// callee resolver only accepted an N_IDENT base, so the chained shape
// fell to the name registry with an empty module hint and emitted
// CALL cb(SB) — the last open shape of the #59.8 name-keyed family.
type bstr = struct {
cb: *fn(x: i32) i32,
pad: i32,
};
type astr = struct {
n: i32,
b: bstr,
};
fn twice(x: i32) i32 = { return x * 2; };
fn main() i32 = {
let a: astr;
a.n = 1;
a.b.cb = &twice;
a.b.pad = 2;
return a.b.cb(20) + a.n + a.b.pad - 7;
};