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:
2026-05-18 10:20:01 +09:00
parent 85af051cd1
commit c34abf47b1
6 changed files with 355 additions and 12 deletions

View File

@@ -255,6 +255,7 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \
$(BIN)/test_letdecl_zeroinit \
$(BIN)/test_nested_if_labels \
$(BIN)/test_alias_leaf_collision \
$(BIN)/test_modcall_widen_slice \
$(BIN)/test_param_shadow_mod \
$(BIN)/test_localoff_scope \
$(BIN)/test_cast_enum_movl \
@@ -582,6 +583,10 @@ $(BIN)/test_alias_leaf_collision: test/wcc/726_alias_leaf_collision.c \
$(BIN)/w6c $(BIN)/w6c_ww | $(BIN)
$(CC) $(CFLAGS) -o $@ $<
$(BIN)/test_modcall_widen_slice: test/wcc/727_modcall_widen_slice.c \
$(BIN)/w6c $(BIN)/w6c_ww | $(BIN)
$(CC) $(CFLAGS) -o $@ $<
$(BIN)/test_use_promote_alias: test/wcc/699_use_promote_alias.c \
$(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
$(LIB)/libwwrt.a | $(BIN)