w6c+selfhost: same-module preference for bare-leaf lookup
This commit is contained in:
@@ -4647,6 +4647,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);
|
||||
@@ -4725,6 +4767,10 @@ type checker = struct {
|
||||
errs: i32,
|
||||
verbose: i32, // when non-zero, log each unresolved name
|
||||
fnret: *node, // enclosing fn's return type AST (for `?`)
|
||||
curmod: str, // importing-module bareword for the decl
|
||||
// currently being walked; "" for primary
|
||||
// compilation unit. Drives same-module
|
||||
// preference in bare-leaf lookups.
|
||||
};
|
||||
|
||||
// seedprimitives — install the built-in type names so `i32`, `str`,
|
||||
@@ -4823,7 +4869,7 @@ fn resolvewalk(c: *checker, n: *node) void = {
|
||||
if (k == nkind.N_IDENT) {
|
||||
let nm: str = n.str;
|
||||
if (nm.len > 0) {
|
||||
let s: *sym = scopelookup(c.cur, nm);
|
||||
let s: *sym = scopelookupprefer(c.cur, c.curmod, nm);
|
||||
if (s == nil) {
|
||||
c.nunresolved += 1;
|
||||
if (c.verbose != 0) {
|
||||
@@ -4838,7 +4884,7 @@ fn resolvewalk(c: *checker, n: *node) void = {
|
||||
if (k == nkind.N_TNAME) {
|
||||
let nm: str = n.str;
|
||||
if (nm.len > 0) {
|
||||
let s: *sym = scopelookup(c.cur, nm);
|
||||
let s: *sym = scopelookupprefer(c.cur, c.curmod, nm);
|
||||
// `pkg.Type` — strip the last dot prefix and look up
|
||||
// the leaf with a mod filter so same-leaf-name types
|
||||
// from different imports (`bufio.stream` vs
|
||||
@@ -5637,6 +5683,8 @@ export fn checkinit(c: *checker, a: *arena, tc: *tctx) void = {
|
||||
c.errs = 0;
|
||||
c.verbose = 0;
|
||||
c.fnret = nil;
|
||||
let empty: str;
|
||||
c.curmod = empty;
|
||||
seedprimitives(c);
|
||||
};
|
||||
|
||||
@@ -5652,8 +5700,12 @@ export fn checkfile(c: *checker, file: *node) void = {
|
||||
};
|
||||
|
||||
// Pass 2: walk decl bodies/types and resolve identifiers.
|
||||
// Track the per-decl module bareword so bare-leaf lookups inside
|
||||
// the body prefer same-module entries over alphabetically-earlier
|
||||
// same-leaf imports.
|
||||
d = file.list;
|
||||
for (d != nil) {
|
||||
c.curmod = declmod(file, d);
|
||||
let k: nkind = d.kind;
|
||||
if (k == nkind.N_FNDECL) {
|
||||
if (d.lhs != nil) { resolvewalk(c, d.lhs); }; // return type
|
||||
@@ -5669,6 +5721,8 @@ export fn checkfile(c: *checker, file: *node) void = {
|
||||
};};};};
|
||||
d = d.next;
|
||||
};
|
||||
let empty: str;
|
||||
c.curmod = empty;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -31,6 +31,10 @@ type checker = struct {
|
||||
errs: i32,
|
||||
verbose: i32, // when non-zero, log each unresolved name
|
||||
fnret: *node, // enclosing fn's return type AST (for `?`)
|
||||
curmod: str, // importing-module bareword for the decl
|
||||
// currently being walked; "" for primary
|
||||
// compilation unit. Drives same-module
|
||||
// preference in bare-leaf lookups.
|
||||
};
|
||||
|
||||
// seedprimitives — install the built-in type names so `i32`, `str`,
|
||||
@@ -129,7 +133,7 @@ fn resolvewalk(c: *checker, n: *node) void = {
|
||||
if (k == nkind.N_IDENT) {
|
||||
let nm: str = n.str;
|
||||
if (nm.len > 0) {
|
||||
let s: *sym = scopelookup(c.cur, nm);
|
||||
let s: *sym = scopelookupprefer(c.cur, c.curmod, nm);
|
||||
if (s == nil) {
|
||||
c.nunresolved += 1;
|
||||
if (c.verbose != 0) {
|
||||
@@ -144,7 +148,7 @@ fn resolvewalk(c: *checker, n: *node) void = {
|
||||
if (k == nkind.N_TNAME) {
|
||||
let nm: str = n.str;
|
||||
if (nm.len > 0) {
|
||||
let s: *sym = scopelookup(c.cur, nm);
|
||||
let s: *sym = scopelookupprefer(c.cur, c.curmod, nm);
|
||||
// `pkg.Type` — strip the last dot prefix and look up
|
||||
// the leaf with a mod filter so same-leaf-name types
|
||||
// from different imports (`bufio.stream` vs
|
||||
@@ -943,6 +947,8 @@ export fn checkinit(c: *checker, a: *arena, tc: *tctx) void = {
|
||||
c.errs = 0;
|
||||
c.verbose = 0;
|
||||
c.fnret = nil;
|
||||
let empty: str;
|
||||
c.curmod = empty;
|
||||
seedprimitives(c);
|
||||
};
|
||||
|
||||
@@ -958,8 +964,12 @@ export fn checkfile(c: *checker, file: *node) void = {
|
||||
};
|
||||
|
||||
// Pass 2: walk decl bodies/types and resolve identifiers.
|
||||
// Track the per-decl module bareword so bare-leaf lookups inside
|
||||
// the body prefer same-module entries over alphabetically-earlier
|
||||
// same-leaf imports.
|
||||
d = file.list;
|
||||
for (d != nil) {
|
||||
c.curmod = declmod(file, d);
|
||||
let k: nkind = d.kind;
|
||||
if (k == nkind.N_FNDECL) {
|
||||
if (d.lhs != nil) { resolvewalk(c, d.lhs); }; // return type
|
||||
@@ -975,5 +985,7 @@ export fn checkfile(c: *checker, file: *node) void = {
|
||||
};};};};
|
||||
d = d.next;
|
||||
};
|
||||
let empty: str;
|
||||
c.curmod = empty;
|
||||
|
||||
};
|
||||
|
||||
@@ -4647,6 +4647,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);
|
||||
@@ -4725,6 +4767,10 @@ type checker = struct {
|
||||
errs: i32,
|
||||
verbose: i32, // when non-zero, log each unresolved name
|
||||
fnret: *node, // enclosing fn's return type AST (for `?`)
|
||||
curmod: str, // importing-module bareword for the decl
|
||||
// currently being walked; "" for primary
|
||||
// compilation unit. Drives same-module
|
||||
// preference in bare-leaf lookups.
|
||||
};
|
||||
|
||||
// seedprimitives — install the built-in type names so `i32`, `str`,
|
||||
@@ -4823,7 +4869,7 @@ fn resolvewalk(c: *checker, n: *node) void = {
|
||||
if (k == nkind.N_IDENT) {
|
||||
let nm: str = n.str;
|
||||
if (nm.len > 0) {
|
||||
let s: *sym = scopelookup(c.cur, nm);
|
||||
let s: *sym = scopelookupprefer(c.cur, c.curmod, nm);
|
||||
if (s == nil) {
|
||||
c.nunresolved += 1;
|
||||
if (c.verbose != 0) {
|
||||
@@ -4838,7 +4884,7 @@ fn resolvewalk(c: *checker, n: *node) void = {
|
||||
if (k == nkind.N_TNAME) {
|
||||
let nm: str = n.str;
|
||||
if (nm.len > 0) {
|
||||
let s: *sym = scopelookup(c.cur, nm);
|
||||
let s: *sym = scopelookupprefer(c.cur, c.curmod, nm);
|
||||
// `pkg.Type` — strip the last dot prefix and look up
|
||||
// the leaf with a mod filter so same-leaf-name types
|
||||
// from different imports (`bufio.stream` vs
|
||||
@@ -5637,6 +5683,8 @@ export fn checkinit(c: *checker, a: *arena, tc: *tctx) void = {
|
||||
c.errs = 0;
|
||||
c.verbose = 0;
|
||||
c.fnret = nil;
|
||||
let empty: str;
|
||||
c.curmod = empty;
|
||||
seedprimitives(c);
|
||||
};
|
||||
|
||||
@@ -5652,8 +5700,12 @@ export fn checkfile(c: *checker, file: *node) void = {
|
||||
};
|
||||
|
||||
// Pass 2: walk decl bodies/types and resolve identifiers.
|
||||
// Track the per-decl module bareword so bare-leaf lookups inside
|
||||
// the body prefer same-module entries over alphabetically-earlier
|
||||
// same-leaf imports.
|
||||
d = file.list;
|
||||
for (d != nil) {
|
||||
c.curmod = declmod(file, d);
|
||||
let k: nkind = d.kind;
|
||||
if (k == nkind.N_FNDECL) {
|
||||
if (d.lhs != nil) { resolvewalk(c, d.lhs); }; // return type
|
||||
@@ -5669,6 +5721,8 @@ export fn checkfile(c: *checker, file: *node) void = {
|
||||
};};};};
|
||||
d = d.next;
|
||||
};
|
||||
let empty: str;
|
||||
c.curmod = empty;
|
||||
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user