wcc: dot-lhs prefers SK_USE module over same-leaf type/fn name
The wwstage checker resolved a module-qualified call/access mod.x by the same-module preference in scopelookupprefer, so when the importing package's name collides with a type/fn of the same leaf (package fnmatch with fn fnmatch; package random with type random), the dot-lhs mod resolved to the same-leaf SK_TYPE/SK_FN instead of the coexisting SK_USE import — the N_DOT module-qual arm never fired and the call went nil-stamped (the D class of the asserttyped gap audit: fnmatch 2, random 16). cstage resolves this via Sym.use_alias; this ports the equivalent to wwstage. Add scopelookupuselocal (a single-scope SK_USE lookup, twin of scopelookuptype) and prefer SK_USE for a dot-lhs in exprtype's N_CALL and N_DOT arms, keyed on the scope where scopelookupprefer landed so a local binding sharing a module's leaf keeps value semantics. Scope-layer only — no type-identity touch (cstage use_alias never reaches type_eq). Drives the 901 gap-corpus D count to 0 (random_test now byte-id cs==ww). Compiler binary unchanged (no such collision in its own source); 990-997 hold. The separate fnmatch bare-enum-member cgen cs!=ww is unrelated (filed).
This commit is contained in:
@@ -126,6 +126,47 @@ export fn scopelookuptype(s: *scope, name: str) *sym = {
|
||||
return nil;
|
||||
};
|
||||
|
||||
// scopelookupuselocal — find a same-leaf SK_USE entry within ONE scope.
|
||||
//
|
||||
// Same FNV bucket + hashnext chain as scopelookuplocal, with a
|
||||
// `skind == SK_USE` filter and NO parent walk. The dot-lhs twin of
|
||||
// scopelookuptype: when a `use mod;` and a colliding top-level
|
||||
// `fn mod` / `type mod` of the same leaf coexist (random.random,
|
||||
// fnmatch.fnmatch), the mod-preferring scopelookupprefer returns the
|
||||
// SK_FN/SK_TYPE whose mod matches the importing unit's package, masking
|
||||
// the SK_USE. A dot-lhs `mod.x` must resolve `mod` to the SK_USE for the
|
||||
// module-qualified arm to fire, so the resolver re-resolves through this
|
||||
// filter — keyed on the scope where scopelookupprefer LANDED — when it
|
||||
// lands on a non-USE same-leaf entry.
|
||||
//
|
||||
// Single-scope (not a parent walk) so a local binding that shares a leaf
|
||||
// with a top-level `use` keeps value semantics: scopelookupprefer
|
||||
// resolves the local in its inner scope, whose bucket holds no SK_USE,
|
||||
// so this returns nil and the dot stays field access. Only a genuine
|
||||
// same-scope coexistence (top-level use + top-level type/fn) re-resolves.
|
||||
//
|
||||
// This is the coexistence-equivalent of cstage's Sym.use_alias bit
|
||||
// (cmd/wcc/check.c: set at the SK_USE→SK_X promotion sites, consulted by
|
||||
// the `kind == SK_USE || use_alias` dot guards in resolve_typename and
|
||||
// cexpr's N_DOT arm). Wwstage installs the SK_USE and the same-leaf
|
||||
// type/fn as SEPARATE coexisting entries (see selfhost/cmd/wcc/check.ww
|
||||
// installdecl), so no flag is needed — the SK_USE is never overwritten,
|
||||
// only out-preferred. Cite: project memory module_type_name_collision
|
||||
// (cstage fix 2026-05-13).
|
||||
export fn scopelookupuselocal(s: *scope, name: str) *sym = {
|
||||
if (s == nil) { return nil; };
|
||||
let h: u64 = hashstr(name);
|
||||
let bi: i32 = (h % (s.nbuckets: u64)): i32;
|
||||
let b: *sym = s.buckets[bi];
|
||||
for (b != nil) {
|
||||
if (streq(b.name, name)) {
|
||||
if (b.skind == skind.SK_USE) { return b; };
|
||||
};
|
||||
b = b.hashnext;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// scopelookupinmodule — module-filtered chain walk.
|
||||
//
|
||||
// Same FNV bucket + hashnext chain + parent walk as scopelookup, plus
|
||||
|
||||
Reference in New Issue
Block a user