From 67f39256f65369673d0f0a34e13bff0fe7215400 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Sat, 8 Aug 2026 02:48:05 +0900 Subject: [PATCH] cgen: resolve chained-DOT fn-ptr field callees MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- docs/test-system-v2.md | 4 +- internal/wwfixture/types.ww | 8 +-- selfhost/cmd/wcc/cgenutil.ww | 67 ++++++++++++++++++-------- test/wcc/data/fnptrfield_chain/case.ww | 27 +++++++++++ 4 files changed, 81 insertions(+), 25 deletions(-) create mode 100644 test/wcc/data/fnptrfield_chain/case.ww diff --git a/docs/test-system-v2.md b/docs/test-system-v2.md index d7862dea..47881653 100644 --- a/docs/test-system-v2.md +++ b/docs/test-system-v2.md @@ -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 diff --git a/internal/wwfixture/types.ww b/internal/wwfixture/types.ww index 12210d32..a22d9ca1 100644 --- a/internal/wwfixture/types.ww +++ b/internal/wwfixture/types.ww @@ -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, diff --git a/selfhost/cmd/wcc/cgenutil.ww b/selfhost/cmd/wcc/cgenutil.ww index 1b4e3c00..acd9921e 100644 --- a/selfhost/cmd/wcc/cgenutil.ww +++ b/selfhost/cmd/wcc/cgenutil.ww @@ -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 (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) { diff --git a/test/wcc/data/fnptrfield_chain/case.ww b/test/wcc/data/fnptrfield_chain/case.ww new file mode 100644 index 00000000..4eb8fc04 --- /dev/null +++ b/test/wcc/data/fnptrfield_chain/case.ww @@ -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; +};