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:
@@ -50,6 +50,72 @@ fn findvariadicparam(ps: *syntax.node, nfixed_out: *i32) *syntax.node = {
|
||||
return nil;
|
||||
};
|
||||
|
||||
// fnptrcalleetfn — resolve a callee that names a LOCAL fn pointer
|
||||
// (bare local `fp(...)`, or field call `w.emit(...)` where w is a
|
||||
// local struct value / *struct and emit is an N_TFN field) to its
|
||||
// N_TFN type node. Returns nil for every other callee shape — the
|
||||
// name registry stays primary for module fns. Extracted from the
|
||||
// cgcall isfnptrcall detection so the CALL-target choice and the
|
||||
// arg classification key off ONE resolution and can never disagree
|
||||
// within a call (#59.8: a local whose name shadows the current
|
||||
// module made fnparamslookupmod match the module's own same-leaf
|
||||
// variadic fn — nfixed off by one, the fixed arg boxed into the
|
||||
// gather and the spread emitted as zeros; with no collision the
|
||||
// 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).
|
||||
fn fnptrcalleetfn(c: *cgen, callee: *syntax.node) *syntax.node = {
|
||||
if (callee == nil) { return nil; };
|
||||
if (callee.kind == syntax.nkind.N_IDENT) {
|
||||
let lc0: *local = localfindnode(c, callee.str);
|
||||
if (lc0 == nil) { return nil; };
|
||||
let tn0: *syntax.node = lc0.tnode;
|
||||
if (tn0 == nil) { return nil; };
|
||||
if (tn0.kind == syntax.nkind.N_TFN) { return tn0; };
|
||||
if (tn0.kind == syntax.nkind.N_TPTR) {
|
||||
if (tn0.lhs != nil) {
|
||||
if (tn0.lhs.kind == syntax.nkind.N_TFN) {
|
||||
return tn0.lhs;
|
||||
};
|
||||
};
|
||||
};
|
||||
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);
|
||||
if (si == nil) { return nil; };
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
if (syntax.streq(fi.fname, callee.str)) {
|
||||
let ft: *syntax.node = fi.tnode;
|
||||
if (ft != nil) {
|
||||
if (ft.kind == syntax.nkind.N_TFN) { return ft; };
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
fi = fi.finext;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// callee_variadic_param — convenience wrapper: looks up the callee
|
||||
// by name and finds its variadic param + nfixed. Returns nil if the
|
||||
// callee isn't registered or has no variadic param.
|
||||
@@ -65,6 +131,34 @@ fn findvariadicparam(ps: *syntax.node, nfixed_out: *i32) *syntax.node = {
|
||||
fn callee_variadic_param(c: *cgen, callee: *syntax.node, nfixed_out: *i32) *syntax.node = {
|
||||
*nfixed_out = 0;
|
||||
if (callee == nil) { return nil; };
|
||||
// #59.8: a LOCAL fn-ptr callee resolves through its own N_TFN,
|
||||
// never the name registry (which the collision shape mis-hits).
|
||||
// A raw field-N_TFN variadic param carries the bare element T
|
||||
// (registry params are installparams-promoted to []T); wrap it
|
||||
// so the vararg_sl descriptor slot's tnode classifies as a
|
||||
// slice downstream (mirror check.ww installparams / cstage
|
||||
// check.c:455).
|
||||
let ft: *syntax.node = fnptrcalleetfn(c, callee);
|
||||
if (ft != nil) {
|
||||
let vp: *syntax.node = findvariadicparam(ft.list, nfixed_out);
|
||||
if (vp == nil) { return nil; };
|
||||
if (vp.lhs != nil) {
|
||||
if (vp.lhs.kind != syntax.nkind.N_TSLICE) {
|
||||
let w: *syntax.node = syntax.newnode(
|
||||
syntax.nkind.N_PARAM, "", 0, 0);
|
||||
w.str = vp.str;
|
||||
w.op = vp.op;
|
||||
let sl: *syntax.node = syntax.newnode(
|
||||
syntax.nkind.N_TSLICE, "", 0, 0);
|
||||
sl.lhs = vp.lhs;
|
||||
sl.type_ = syntax.typeslice(
|
||||
vp.lhs.type_: *syntax.tinfo): *void;
|
||||
w.lhs = sl;
|
||||
return w;
|
||||
};
|
||||
};
|
||||
return vp;
|
||||
};
|
||||
let ps: *syntax.node = nil;
|
||||
if (callee.kind == syntax.nkind.N_IDENT) {
|
||||
if (callee.str.len == 0) { return nil; };
|
||||
@@ -96,7 +190,12 @@ fn calleecvariadic(c: *cgen, callee: *syntax.node, nfixed_out: *i32) bool = {
|
||||
*nfixed_out = 0;
|
||||
if (callee == nil) { return false; };
|
||||
let ps: *syntax.node = nil;
|
||||
if (callee.kind == syntax.nkind.N_IDENT) {
|
||||
// #59.8: a LOCAL fn-ptr callee reads its own N_TFN params — the
|
||||
// name registry mis-hits when the local shadows a module name.
|
||||
let lft: *syntax.node = fnptrcalleetfn(c, callee);
|
||||
if (lft != nil) {
|
||||
ps = lft.list;
|
||||
} else { if (callee.kind == syntax.nkind.N_IDENT) {
|
||||
if (callee.str.len == 0) { return false; };
|
||||
ps = fnparamslookup(c, callee.str);
|
||||
} else { if (callee.kind == syntax.nkind.N_DOT) {
|
||||
@@ -109,7 +208,7 @@ fn calleecvariadic(c: *cgen, callee: *syntax.node, nfixed_out: *i32) bool = {
|
||||
};
|
||||
};
|
||||
ps = fnparamslookupmod(c, callee.str, cmod);
|
||||
}; };
|
||||
}; }; };
|
||||
let p: *syntax.node = ps;
|
||||
for (p != nil) {
|
||||
if (p.kind == syntax.nkind.N_PARAM) {
|
||||
|
||||
Reference in New Issue
Block a user