selfhost+test: route N_DOT callee through fnparamslookupmod (#28)
Wwstage cgcall now mirrors cstage's typed-AST callee-params lookup for module-qualified mod.fn(...) calls, restoring tagged- union widening on cross-module slice args. Class A runtime miscompile — masked from 995_self_rebuild because wwstage tools don't call bytes.index directly; surfaced by lib/strings landing dragging utf8 + bytes into the wwstage-tool dep chain via strings.byteindex's `bytes.X(toutf8(...), n)` call sites. Pre-fix: wwstage's cgcall (cgenexpr.ww) looked up calleeparams only when callee.kind == N_IDENT. For N_DOT callees (the module-qualified mod.fn() form), calleeparams stayed nil → pushargsrev's widening detection gated on param != nil never fired → wwstage fell through to the N_IDENT-slice fast path pushing only 3 slot words (cap, len, ptr) WITHOUT the variant tag. Receiving fn's `match (needle)` then dispatched on (needle.ptr in CX) instead of needle.tag, with R8/R9 carrying .len/.cap instead of .ptr/.len. Wrong arm + wrong payload. Cstage handles N_DOT natively via the checker-set type on n->lhs->type (cmd/w6c/cgen.c:4156-4165), so cg_widen_tagged_push slice path pushes 4 words including tag. Polarity catalog: wwstage UNDER — calleeparams lookup missing N_DOT dispatch arm. Sister to #19 (N_TSLICE variantindex arm), #21 (N_CALL pushargsrev arm), #24 (N_CALL nodeisslice arm), #27 (aliaslookup same-mod-first). The pattern: wwstage dispatchers keep missing arms cstage has natively via typed-AST resolution. Convergence wwstage → cstage (rule 10's spirit overrides letter when correctness is at stake — Path 2 of aligning cstage DOWN would create a runtime miscompile in both stages). Fix: cgcall N_DOT branch pulls module from callee.lhs.str and function name from callee.str, calls new fnparamslookupmod helper. Helper does same-module-first walk then existing first-match fallback (mirrors #27's aliaslookup fix shape). New fnret.fmod field carries module identity; collectfnrets sets f.fmod = d.module at registration. Module-qualified pkg.fn path unchanged. Tests: - 727_modcall_widen_slice pins MOVQ $1 + PUSHQ AX (tag-synth) before the receiving fn's CALL on canonical mod.fn(slice, ...) shape with the callee param widened to a tagged union. Three assertions per row: cstage tag-synth presence, wwstage tag-synth presence, cstage↔wwstage cmp -s byte-id. Sentinel- flip-verified: comment out fnparamslookupmod call → wwstage tag-synth absent + cmp diverges. 99/99 ok. 995_self_rebuild stays green (ww2==ww3==ww4 byte-id).
This commit is contained in:
@@ -2856,13 +2856,28 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
|
||||
// Look up the callee's declared params for tagged-union widening.
|
||||
// fn-pointer calls (callee is a local) don't get widening — the
|
||||
// user must build the tagged value explicitly. Matches the most
|
||||
// common case (direct named calls).
|
||||
// user must build the tagged value explicitly.
|
||||
//
|
||||
// N_DOT (`mod.fn(...)`) covers cross-module calls; pre-#28 wwstage
|
||||
// only handled N_IDENT, leaving N_DOT calls without widening
|
||||
// detection — pushargsrev then fell through to the N_IDENT-slice
|
||||
// fast path and dropped the variant tag word on widened slice args.
|
||||
// Cstage finds params via the checker-set `n->lhs->type`, sidestepping
|
||||
// the name-driven registry entirely (cmd/w6c/cgen.c:4161-4165).
|
||||
let calleeparams: *node = nil;
|
||||
if (callee != nil) {
|
||||
if (callee.kind == nkind.N_IDENT) {
|
||||
calleeparams = fnparamslookup(c, callee.str);
|
||||
};
|
||||
} else { if (callee.kind == nkind.N_DOT) {
|
||||
let cmod: str;
|
||||
cmod.ptr = nil; cmod.len = 0;
|
||||
if (callee.lhs != nil) {
|
||||
if (callee.lhs.kind == nkind.N_IDENT) {
|
||||
cmod = callee.lhs.str;
|
||||
};
|
||||
};
|
||||
calleeparams = fnparamslookupmod(c, callee.str, cmod);
|
||||
}; };
|
||||
};
|
||||
// Hare-style variadic last param: gather N tail args into a
|
||||
// frame-resident [N]T (`@vararg_d_<seq>`) plus a 24B slice
|
||||
|
||||
Reference in New Issue
Block a user