cgen: peel *fn field types for local struct-field callees

fnptrcalleetfn's N_DOT arm accepted only a bare N_TFN field tnode, so
a call through a field declared `cb: *fn(...)` missed, fell through to
the name registry, and emitted CALL cb(SB) — an undefined symbol
(loud at link; cstage calls the stamped ptr-to-fn indirectly). Same
TPTR peel the N_IDENT arm already had. Fixture fnptrfield_call covers
the by-value and via-pointer shapes; corpus pin 1478/2956.
This commit is contained in:
2026-08-08 02:28:33 +09:00
parent 939d0938e2
commit 6553d60e91
4 changed files with 40 additions and 6 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,477 fixtures and 2,954 C/WW cells:
The live declarative compiler corpus has 1,478 fixtures and 2,956 C/WW cells:
335 expected rejections (311 shared and 24 stage-specific), 17 compile-only
successes, 191 exit-zero programs, and 934 explicit-exit programs.
successes, 191 exit-zero programs, and 935 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 = 1477;
def corpuscount: i32 = 1478;
def errorcount: i32 = 335;
def compilecount: i32 = 17;
def runcount: i32 = 191;
def runexitcount: i32 = 934;
def nativecount: i32 = 2954;
def corpushash: str = "64258f4de9ac7d71c4ad6221dd2ae7338a38cd5c620fc4ce22c1105021559e83";
def runexitcount: i32 = 935;
def nativecount: i32 = 2956;
def corpushash: str = "2ac2342a80c4148968250bf4ce93852b78cdefb6af96622fa533eea4a68837c6";
type directive = enum i32 {
ERROR = 0,

View File

@@ -108,6 +108,18 @@ fn fnptrcalleetfn(c: *cgen, callee: *syntax.node) *syntax.node = {
let ft: *syntax.node = fi.tnode;
if (ft != nil) {
if (ft.kind == syntax.nkind.N_TFN) { return ft; };
// `cb: *fn(...)` — same TPTR peel as the N_IDENT
// arm above; without it the field-call fell through
// to the name registry and emitted CALL <leaf>(SB)
// (undefined symbol — loud at link, but cstage
// resolves the stamped ptr-to-fn and calls indirect).
if (ft.kind == syntax.nkind.N_TPTR) {
if (ft.lhs != nil) {
if (ft.lhs.kind == syntax.nkind.N_TFN) {
return ft.lhs;
};
};
};
};
return nil;
};

View File

@@ -0,0 +1,22 @@
//ww:run-exit 36
package main;
// A call through a struct field declared `*fn(...)`, by value and via
// pointer. The wwstage fnptrcalleetfn N_DOT arm lacked the TPTR peel
// its N_IDENT arm had, so the callee fell through to the name registry
// and emitted CALL cb(SB) — an undefined symbol.
type cbs = struct {
cb: *fn(x: i32) i32,
k: i32,
};
fn twice(x: i32) i32 = { return x * 2; };
fn main() i32 = {
let o: cbs;
o.cb = &twice;
o.k = 4;
let p: *cbs = &o;
return o.cb(10) + p.cb(6) + o.k;
};