selfhost/cmd/wcc/check: resolvealias prefer same-module (#53)

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.
This commit is contained in:
2026-05-22 08:59:02 +09:00
parent b8e5a921f8
commit e3237d1bcb
3 changed files with 30 additions and 3 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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