w6c+selfhost: same-module preference for bare-leaf lookup

This commit is contained in:
2026-05-15 11:24:33 +09:00
parent 35b32c1304
commit e6045f3ada
13 changed files with 382 additions and 12 deletions

View File

@@ -134,6 +134,48 @@ export fn scopelookupinmodule(s: *scope, mod: str, name: str) *sym = {
return nil;
};
// scopelookupprefer — bare-leaf lookup with same-module preference.
//
// Walks the same FNV bucket + hashnext chain + parent walk scopelookup
// uses. Within each scope's bucket: Pass 1 prefers entries whose
// `sym.mod` matches `mod`; Pass 2 falls back to the first match
// regardless of mod (same semantics as scopelookup). We only descend
// to the parent scope when the current scope has no matching entry at
// all — so a local binding in a closer scope still shadows a same-name
// fn from a parent scope, even when the parent entry mod-matches.
//
// When `mod` is empty we just call scopelookup — there's no module
// identity to prefer.
//
// Used at bare-leaf lookup sites inside a known current module so that
// a bare `read` inside lib/os resolves to os.read rather than the
// io.read that happens to hash earlier into the flat scope. Mirrors
// cmd/wcc/sym.c scope_lookup_prefer.
export fn scopelookupprefer(s: *scope, mod: str, name: str) *sym = {
if (mod.len == 0) { return scopelookup(s, name); };
let p: *scope = s;
for (p != nil) {
let h: u64 = hashstr(name);
let bi: i32 = (h % (p.nbuckets: u64)): i32;
let b: *sym = p.buckets[bi];
let fallback: *sym = nil;
for (b != nil) {
if (streq(b.name, name)) {
if (b.mod.len > 0) {
if (streq(b.mod, mod)) {
return b;
};
};
if (fallback == nil) { fallback = b; };
};
b = b.hashnext;
};
if (fallback != nil) { return fallback; };
p = p.parent;
};
return nil;
};
export fn scopedefine(s: *scope, name: str, k: skind, t: *tinfo, decl: *node) *sym = {
let empty: str;
return scopedefineinmodule(s, name, empty, k, t, decl);