cgen: resolve local fn-ptr callees for variadic arg prep

The wwstage variadic call classification was name-keyed: a fn-ptr
FIELD call whose local base shadowed the current module name
(lib/log's log.println(log, args...)) picked the module fn's
signature — nfixed off by one, the fixed arg boxed into the gather,
the spread emitted as zeros — and a no-collision fn-ptr callee missed
the registry entirely, leaking the raw N_SPREAD as a single $0 word
(SIGSEGV / exit 255 in 8 of 11 logtest tests on the wwstage leg).
fnptrcalleetfn resolves a local fn-ptr callee (bare local or struct
field) to its N_TFN once, shared by the CALL-target choice,
callee_variadic_param (with the []T wrap registry params get from
installparams), calleecvariadic, and the widening param lookup, so
target and arg prep can never disagree. Graduates the #59.8 logtest
pin — DATABYTEID_DIVERGED-era M_DIVERGE count is now zero.
This commit is contained in:
2026-08-08 01:07:23 +09:00
parent c18005d833
commit 3f7452814b
5 changed files with 167 additions and 51 deletions

View File

@@ -7830,7 +7830,16 @@ fn cgcall(c: *cgen, n: *syntax.node) void = {
// the name-driven registry entirely (cmd/w6c/cgen.c:4161-4165).
let calleeparams: *syntax.node = nil;
if (callee != nil) {
if (callee.kind == syntax.nkind.N_IDENT) {
// #59.8: a LOCAL fn-ptr callee (bare local or struct-field)
// reads its own N_TFN params — the registry lookup below
// mis-hits when the local's name shadows the current module
// (lib/log's `log.println(log, args...)`), handing the
// module fn's signature to arg prep while the CALL target
// resolved the field. One resolver for both (fnptrcalleetfn).
let lft: *syntax.node = fnptrcalleetfn(c, callee);
if (lft != nil) {
calleeparams = lft.list;
} else { if (callee.kind == syntax.nkind.N_IDENT) {
calleeparams = fnparamslookup(c, callee.str);
} else { if (callee.kind == syntax.nkind.N_DOT) {
let cmod: str;
@@ -7841,7 +7850,7 @@ fn cgcall(c: *cgen, n: *syntax.node) void = {
};
};
calleeparams = fnparamslookupmod(c, callee.str, cmod);
}; };
}; }; };
};
// Hare-style variadic last param: gather N tail args into a
// frame-resident [N]T (vararg_d slot) plus a 24B slice
@@ -8419,48 +8428,11 @@ fn cgcall(c: *cgen, n: *syntax.node) void = {
isfnptrcall = true;
};
if (callee.kind == syntax.nkind.N_DOT) {
let base: *syntax.node = callee.lhs;
let fld: str = callee.str;
if (base != nil) {
if (base.kind == syntax.nkind.N_IDENT) {
let bn: str = base.str;
let lc: *local = localfindnode(c, bn);
if (lc != nil) {
let tn: *syntax.node = lc.tnode;
if (tn != nil) {
let lkind: syntax.nkind = tn.kind;
let sname: str;
sname.ptr = nil; sname.len = 0;
if (lkind == syntax.nkind.N_TNAME) { sname = tn.str; };
if (lkind == syntax.nkind.N_TPTR) {
let inner: *syntax.node = tn.lhs;
if (inner != nil) {
if (inner.kind == syntax.nkind.N_TNAME) { sname = inner.str; };
};
};
if (sname.len > 0) {
let si: *structinfo = structlookup(c, sname);
if (si != nil) {
let fi: *fieldinfo = si.fields;
for (fi != nil) {
let fn_: str = fi.fname;
if (syntax.streq(fn_, fld)) {
let ft: *syntax.node = fi.tnode;
if (ft != nil) {
if (ft.kind == syntax.nkind.N_TFN) {
isfnptrcall = true;
};
};
fi = nil;
} else {
fi = fi.finext;
};
};
};
};
};
};
};
// #59.8: same resolver as the arg-classification sites
// (fnptrcalleetfn) so CALL target and arg prep cannot
// disagree within one call.
if (fnptrcalleetfn(c, callee) != nil) {
isfnptrcall = true;
};
};
};