w6c+selfhost: same-module preference for bare-leaf lookup
This commit is contained in:
@@ -60,7 +60,7 @@ resolve_typename(Checker *c, Node *n)
|
||||
const char *nm = n->str;
|
||||
Type *bi = lookup_builtin(nm);
|
||||
if (bi) return bi;
|
||||
Sym *s = scope_lookup(c->cur, nm);
|
||||
Sym *s = scope_lookup_prefer(c->cur, c->cur_mod, nm);
|
||||
if (s == NULL && nm) {
|
||||
/* module-qualified: io.stream → strip the last dot prefix
|
||||
* and look up the leaf, filtering on the importing module's
|
||||
@@ -679,7 +679,7 @@ cexpr(Checker *c, Node *n)
|
||||
if (n->str && n->str[0] == '\0')
|
||||
return n->type = err(c, n->pos,
|
||||
"`_` is only valid as a binding or discard lvalue");
|
||||
Sym *s = scope_lookup(c->cur, n->str);
|
||||
Sym *s = scope_lookup_prefer(c->cur, c->cur_mod, n->str);
|
||||
if (s == NULL)
|
||||
return n->type = err(c, n->pos, "undefined: %s", n->str);
|
||||
/* SK_USE has no concrete value type; the only legal use is
|
||||
@@ -942,7 +942,7 @@ cexpr(Checker *c, Node *n)
|
||||
* (e.g. lib/os/os.ww) keeps working unchanged. */
|
||||
if (n->lhs && n->lhs->kind == N_IDENT && n->lhs->str &&
|
||||
strcmp(n->lhs->str, "abort") == 0 &&
|
||||
scope_lookup(c->cur, "abort") == NULL) {
|
||||
scope_lookup_prefer(c->cur, c->cur_mod, "abort") == NULL) {
|
||||
if (n->list) {
|
||||
Type *mt = cexpr(c, n->list);
|
||||
if (mt != ty_err && !type_assignable(ty_str, mt))
|
||||
@@ -957,7 +957,7 @@ cexpr(Checker *c, Node *n)
|
||||
if (n->lhs && n->lhs->kind == N_IDENT && n->lhs->str &&
|
||||
strcmp(n->lhs->str, "assert") == 0 &&
|
||||
n->list != NULL &&
|
||||
scope_lookup(c->cur, "assert") == NULL) {
|
||||
scope_lookup_prefer(c->cur, c->cur_mod, "assert") == NULL) {
|
||||
Type *ct = cexpr(c, n->list);
|
||||
if (ct != ty_err && ct != ty_bool && ct != ty_untyped_bool)
|
||||
err(c, n->pos, "assert: cond must be bool");
|
||||
@@ -1051,7 +1051,8 @@ cexpr(Checker *c, Node *n)
|
||||
}
|
||||
/* Reject assignment to a const-bound name. */
|
||||
if (n->lhs && n->lhs->kind == N_IDENT && n->lhs->str) {
|
||||
Sym *s = scope_lookup(c->cur, n->lhs->str);
|
||||
Sym *s = scope_lookup_prefer(c->cur, c->cur_mod,
|
||||
n->lhs->str);
|
||||
if (s && s->is_const)
|
||||
err(c, n->pos, "cannot assign to const `%s`",
|
||||
n->lhs->str);
|
||||
@@ -1069,7 +1070,8 @@ cexpr(Checker *c, Node *n)
|
||||
* resolve_type for the synthetic-type-expr case. */
|
||||
Type *t = NULL;
|
||||
if (n->lhs && n->lhs->kind == N_IDENT) {
|
||||
Sym *s = scope_lookup(c->cur, n->lhs->str);
|
||||
Sym *s = scope_lookup_prefer(c->cur, c->cur_mod,
|
||||
n->lhs->str);
|
||||
if (s == NULL || s->kind != SK_TYPE)
|
||||
t = err(c, n->pos, "unknown struct type '%s'",
|
||||
n->lhs->str);
|
||||
@@ -1682,6 +1684,7 @@ check_file(Checker *c, Node *file)
|
||||
}
|
||||
for (Node *d = file->list; d; d = d->next) {
|
||||
if (d->kind != N_TYPEDECL) continue;
|
||||
c->cur_mod = decl_mod(file, d);
|
||||
Type *under = resolve_type(c, d->lhs);
|
||||
d->type->under = under;
|
||||
if (under) {
|
||||
@@ -1690,7 +1693,9 @@ check_file(Checker *c, Node *file)
|
||||
d->type->iserror = under->iserror;
|
||||
}
|
||||
}
|
||||
c->cur_mod = NULL;
|
||||
for (Node *d = file->list; d; d = d->next) {
|
||||
c->cur_mod = decl_mod(file, d);
|
||||
switch (d->kind) {
|
||||
case N_USE:
|
||||
/* already installed in pass 1; no-op here so the
|
||||
@@ -1741,9 +1746,11 @@ check_file(Checker *c, Node *file)
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
c->cur_mod = NULL;
|
||||
|
||||
/* pass 2: check def initialisers and fn bodies */
|
||||
for (Node *d = file->list; d; d = d->next) {
|
||||
c->cur_mod = decl_mod(file, d);
|
||||
switch (d->kind) {
|
||||
case N_DEF: {
|
||||
if (d->rhs) {
|
||||
@@ -1786,4 +1793,5 @@ check_file(Checker *c, Node *file)
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
c->cur_mod = NULL;
|
||||
}
|
||||
|
||||
@@ -79,6 +79,43 @@ scope_lookup_in_module(Scope *s, const char *mod, const char *name)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* scope_lookup_prefer — bare-leaf lookup with same-module preference.
|
||||
*
|
||||
* Walks the same FNV bucket + hashnext chain + parent walk scope_lookup
|
||||
* uses. Within each scope's bucket: Pass 1 prefers entries whose
|
||||
* `sym.mod` matches the caller's `mod`; Pass 2 falls back to the first
|
||||
* match regardless of mod (the existing scope_lookup semantics). 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 NULL we just call scope_lookup — 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.
|
||||
* Qualified-lookup paths (`mod.name`) stay on scope_lookup_in_module.
|
||||
*/
|
||||
Sym *
|
||||
scope_lookup_prefer(Scope *s, const char *mod, const char *name)
|
||||
{
|
||||
if (mod == NULL) return scope_lookup(s, name);
|
||||
for (Scope *p = s; p; p = p->parent) {
|
||||
u64 h = hashstr(name) % p->nbuckets;
|
||||
Sym *fallback = NULL;
|
||||
for (Sym *b = p->buckets[h]; b; b = b->hashnext) {
|
||||
if (strcmp(b->name, name) != 0) continue;
|
||||
if (b->mod && strcmp(b->mod, mod) == 0) return b;
|
||||
if (fallback == NULL) fallback = b;
|
||||
}
|
||||
if (fallback) return fallback;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Sym *
|
||||
scope_define(Scope *s, const char *name, Skind k, Type *t, Node *decl)
|
||||
{
|
||||
|
||||
@@ -509,6 +509,7 @@ Sym *scope_define_in_module(Scope*, const char *name, const char *mod,
|
||||
Sym *scope_lookup(Scope*, const char *name); /* walk up parents */
|
||||
Sym *scope_lookup_local(Scope*, const char *name);
|
||||
Sym *scope_lookup_in_module(Scope*, const char *mod, const char *name);
|
||||
Sym *scope_lookup_prefer(Scope*, const char *mod, const char *name);
|
||||
|
||||
/* ---- checker (check.c) -------------------------------------------- */
|
||||
typedef struct Checker Checker;
|
||||
@@ -517,6 +518,12 @@ struct Checker {
|
||||
Scope *top; /* file scope */
|
||||
Scope *cur; /* current scope */
|
||||
Type *ret; /* expected return type of current fn (or NULL) */
|
||||
const char *cur_mod; /* importing-module bareword for the decl
|
||||
* currently being checked; NULL for primary
|
||||
* compilation unit. Drives same-module
|
||||
* preference in bare-leaf lookups so a bare
|
||||
* `read` inside lib/os resolves to os.read
|
||||
* rather than colliding io.read. */
|
||||
int loops; /* nesting count for break/continue */
|
||||
int errs;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user