Files
ww/test/wcc/data/r598_fnptr_field_variadic/case.ww
Hojun-Cho 3f7452814b 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.
2026-08-08 01:07:23 +09:00

37 lines
860 B
Plaintext

//ww:run
// #59.8: a variadic call through a struct fn-pointer FIELD with a
// spread-forwarded tail (`v.cb(v, args...)`). The pre-fix wwstage
// classified the callee by NAME: with no registry hit the raw
// N_SPREAD leaked to pushargsrev as a single $0 word (callee saw
// args.ptr=0), and when the base local shadowed the module name it
// picked the module fn's signature and mis-counted nfixed.
package main;
type vt = struct {
cb: fn(v: *vt, args: i64...) void,
acc: i64,
};
fn sum(v: *vt, args: i64...) void = {
let i: i32 = 0;
for (i < args.len) {
v.acc += args[i];
i += 1;
};
};
fn dispatch(v: *vt, args: i64...) void = {
v.cb(v, args...);
};
fn main() i32 = {
let v: vt;
v.cb = sum;
v.acc = 0i64;
dispatch(&v, 1i64, 2i64, 3i64);
if (v.acc != 6i64) { return 1; };
v.cb(&v, 10i64);
if (v.acc != 16i64) { return 2; };
return 0;
};