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:
@@ -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;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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