From e3237d1bcb316999e6af78ba607f809f8252b1ff Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Fri, 22 May 2026 08:59:02 +0900 Subject: [PATCH] selfhost/cmd/wcc/check: resolvealias prefer same-module (#53) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bare-leaf TNAME lookup in resolvealias used flat scopelookup, which bucket-walks all matching names and returns whichever entry hashed in first. Two modules each declaring `type invalid = ...` collided in the same flat scope: utf8.invalid (`!void`) and strconv.invalid (`!i32`) resolved to whichever registered first. That drove a localloadop divergence at the 994/995 byte-id gates — MOVSXD vs MOVQ — depending on which alias the checker happened to pick for a given site. Switch to scopelookupprefer(c.cur, c.curmod, nm), mirroring cstage cmd/wcc/check.c:66 (scope_lookup_prefer at sym.c:103): when the current module matches the bucket entry's b.mod, prefer it; else fall back to first-found. The SK_USE→scopelookuptype fallback for the #61 A.5 bare-TNAME-vs-imported-module collision case is unchanged. Six remaining bare-leaf scopelookup sites in this file (exprtype N_IDENT, N_DOT-callee leaf, varianterr, scruttype, exprtypeoftry N_IDENT + N_CALL, walker N_IDENT) are punted to #55 — this commit fixes the path the reproducer surfaced and leaves the rest behind an explicit follow-up so the byte-id corpus stays the test for each conversion. --- selfhost/cmd/w6c/main.combined.ww | 11 ++++++++++- selfhost/cmd/wcc/check.ww | 11 ++++++++++- selfhost/cmd/wwdump/main.combined.ww | 11 ++++++++++- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index bb00d4a9..f01a0f77 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -7607,7 +7607,16 @@ fn resolvealias(c: *checker, n: *node) *node = { leaf.len = nm.len - dotidx - 1; s = scopelookupinmodule(c.cur, head, leaf); } else { - s = scopelookup(c.cur, nm); + // #53: same-module preference. Mirrors cstage + // cmd/wcc/check.c:66 scope_lookup_prefer. Without this, + // two modules each declaring `type invalid = ...` collide + // on the head-first bucket walk: e.g. utf8.invalid `!void` + // vs strconv.invalid `!i32` resolves to whichever + // registered first, driving localloadop MOVSXD/MOVQ + // divergence at 994/995. Other bare-leaf callers in this + // file (L1597 exprtype N_IDENT, L1795/L2720 N_DOT-callee + // leaf, L600 varianterr, L647 scruttype) tracked as #55. + s = scopelookupprefer(c.cur, c.curmod, nm); // #61 A.5: bare TNAME that collides with an imported // module bareword. Two shapes hit this: // - `let l: lex;` where `lex` struct lives in diff --git a/selfhost/cmd/wcc/check.ww b/selfhost/cmd/wcc/check.ww index 9cb23d6f..747d24f7 100644 --- a/selfhost/cmd/wcc/check.ww +++ b/selfhost/cmd/wcc/check.ww @@ -537,7 +537,16 @@ fn resolvealias(c: *checker, n: *node) *node = { leaf.len = nm.len - dotidx - 1; s = scopelookupinmodule(c.cur, head, leaf); } else { - s = scopelookup(c.cur, nm); + // #53: same-module preference. Mirrors cstage + // cmd/wcc/check.c:66 scope_lookup_prefer. Without this, + // two modules each declaring `type invalid = ...` collide + // on the head-first bucket walk: e.g. utf8.invalid `!void` + // vs strconv.invalid `!i32` resolves to whichever + // registered first, driving localloadop MOVSXD/MOVQ + // divergence at 994/995. Other bare-leaf callers in this + // file (L1597 exprtype N_IDENT, L1795/L2720 N_DOT-callee + // leaf, L600 varianterr, L647 scruttype) tracked as #55. + s = scopelookupprefer(c.cur, c.curmod, nm); // #61 A.5: bare TNAME that collides with an imported // module bareword. Two shapes hit this: // - `let l: lex;` where `lex` struct lives in diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 87dcf619..35091826 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -7607,7 +7607,16 @@ fn resolvealias(c: *checker, n: *node) *node = { leaf.len = nm.len - dotidx - 1; s = scopelookupinmodule(c.cur, head, leaf); } else { - s = scopelookup(c.cur, nm); + // #53: same-module preference. Mirrors cstage + // cmd/wcc/check.c:66 scope_lookup_prefer. Without this, + // two modules each declaring `type invalid = ...` collide + // on the head-first bucket walk: e.g. utf8.invalid `!void` + // vs strconv.invalid `!i32` resolves to whichever + // registered first, driving localloadop MOVSXD/MOVQ + // divergence at 994/995. Other bare-leaf callers in this + // file (L1597 exprtype N_IDENT, L1795/L2720 N_DOT-callee + // leaf, L600 varianterr, L647 scruttype) tracked as #55. + s = scopelookupprefer(c.cur, c.curmod, nm); // #61 A.5: bare TNAME that collides with an imported // module bareword. Two shapes hit this: // - `let l: lex;` where `lex` struct lives in