ww: add file-scoped import aliases
This commit is contained in:
118
cmd/wcc/check.c
118
cmd/wcc/check.c
@@ -60,6 +60,8 @@ lookup_builtin(const char *name)
|
||||
static const char *decl_mod(Node *file, Node *d);
|
||||
static const char *use_path(Node *file, const char *curmod, int source,
|
||||
const char *alias);
|
||||
static const char *find_use_path(Node *file, const char *curmod, int source,
|
||||
const char *alias, int mark);
|
||||
static int src_imports(Node *file, const char *modtag, int source,
|
||||
const char *name);
|
||||
static Sym *lookup_visible(Checker *c, const char *name);
|
||||
@@ -2911,7 +2913,8 @@ decl_mod(Node *file, Node *d)
|
||||
* Returns NULL if the referencing package has no such `use`.
|
||||
*/
|
||||
static const char *
|
||||
use_path(Node *file, const char *curmod, int source, const char *alias)
|
||||
find_use_path(Node *file, const char *curmod, int source, const char *alias,
|
||||
int mark)
|
||||
{
|
||||
if (file == NULL || alias == NULL) return NULL;
|
||||
/* Legacy inline multi-package units may spell a package's own
|
||||
@@ -2931,13 +2934,19 @@ use_path(Node *file, const char *curmod, int source, const char *alias)
|
||||
int same = (um == NULL) ? (curmod == NULL)
|
||||
: (curmod != NULL && strcmp(um, curmod) == 0);
|
||||
if (same) {
|
||||
u->used = 1;
|
||||
if (mark) u->used = 1;
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *
|
||||
use_path(Node *file, const char *curmod, int source, const char *alias)
|
||||
{
|
||||
return find_use_path(file, curmod, source, alias, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* resolve_typedecl — resolve d's body into its installed TY_NAMED
|
||||
* placeholder. Reached from check_file's typedecl pass AND on demand
|
||||
@@ -3019,29 +3028,6 @@ src_imports(Node *file, const char *modtag, int source, const char *name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* A flattened interface symbol is source-visible as a bare name only when
|
||||
* the referencing package directly imports the symbol's defining path. The
|
||||
* strict scope helpers above handle lexical locals, builtins and same-package
|
||||
* declarations first; this second pass restores WW's existing direct-import
|
||||
* convenience without admitting a transitive interface by accident. */
|
||||
static int
|
||||
direct_module_visible(Checker *c, const char *mod)
|
||||
{
|
||||
if (c == NULL || mod == NULL || mod[0] == '\0') return 0;
|
||||
for (Node *u = c->file->list; u; u = u->next) {
|
||||
if (u->kind != N_USE || u->sourceid != c->cur_source) continue;
|
||||
const char *um = decl_mod(c->file, u);
|
||||
int same = c->cur_mod == NULL ? um == NULL
|
||||
: um != NULL && strcmp(um, c->cur_mod) == 0;
|
||||
const char *path = u->usepath ? u->usepath : u->str;
|
||||
if (same && path != NULL && strcmp(path, mod) == 0) {
|
||||
u->used = 1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Sym *
|
||||
lookup_visible(Checker *c, const char *name)
|
||||
{
|
||||
@@ -3053,24 +3039,16 @@ lookup_visible(Checker *c, const char *name)
|
||||
if (s->decl != NULL) return s;
|
||||
builtin = s;
|
||||
}
|
||||
/* N_USE entries are historically coalesced by leaf in the flat scope,
|
||||
* so the retained symbol may carry another source package's owner. The
|
||||
* source-owned alias map is authoritative: if this package directly
|
||||
* imports NAME, return the coalesced module marker only as a marker; the
|
||||
* N_DOT path maps the alias to the correct full path again. */
|
||||
if (use_path(c->file, c->cur_mod, c->cur_source, name) != NULL) {
|
||||
/* Flat scope installation may coalesce equal qualifiers from distinct
|
||||
* files. The source-owned binding is authoritative, but a bare mention is
|
||||
* not usage; only the enclosing qualified lookup may mark it. */
|
||||
if (find_use_path(c->file, c->cur_mod, c->cur_source, name, 0) != NULL) {
|
||||
for (Scope *p = c->cur; p; p = p->parent)
|
||||
for (Sym *b = p->first; b; b = b->next)
|
||||
if (strcmp(b->name, name) == 0
|
||||
&& (b->kind == SK_USE || b->use_alias))
|
||||
return b;
|
||||
}
|
||||
for (Scope *p = c->cur; p; p = p->parent) {
|
||||
for (Sym *b = p->first; b; b = b->next)
|
||||
if (strcmp(b->name, name) == 0
|
||||
&& direct_module_visible(c, b->mod))
|
||||
return b;
|
||||
}
|
||||
return builtin;
|
||||
}
|
||||
|
||||
@@ -3079,14 +3057,6 @@ lookup_visible_type(Checker *c, const char *name)
|
||||
{
|
||||
Sym *s = scope_lookup_type(c->cur, c->cur_mod, name);
|
||||
if (s != NULL) return s;
|
||||
for (Scope *p = c->cur; p; p = p->parent) {
|
||||
for (Sym *b = p->first; b; b = b->next)
|
||||
if (b->kind == SK_TYPE && strcmp(b->name, name) == 0
|
||||
&& direct_module_visible(c, b->mod)
|
||||
&& (b->decl == NULL || !b->decl->imported
|
||||
|| b->decl->export))
|
||||
return b;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -3162,6 +3132,44 @@ top_decl_kind(Node *d)
|
||||
|| d->kind == N_FNDECL || d->kind == N_LET);
|
||||
}
|
||||
|
||||
/* Import usage is a property of the file-local qualifier occurrence. Record
|
||||
* qualified syntax before resolving declaration bodies so import diagnostics
|
||||
* retain production Go's source order without making a failed bare lookup a
|
||||
* use. WW already rejects lexical bindings that shadow an import qualifier. */
|
||||
static void
|
||||
mark_import_uses_node(Checker *c, Node *n, const char *owner, int source)
|
||||
{
|
||||
if (n == NULL) return;
|
||||
if (n->kind == N_TNAME && n->str != NULL) {
|
||||
const char *dot = strrchr(n->str, '.');
|
||||
if (dot != NULL) {
|
||||
char *head = astrndup(c->a, n->str, (size_t)(dot - n->str));
|
||||
(void)find_use_path(c->file, owner, source, head, 1);
|
||||
}
|
||||
} else if (n->kind == N_DOT && n->lhs != NULL
|
||||
&& n->lhs->kind == N_IDENT && n->lhs->str != NULL) {
|
||||
(void)find_use_path(c->file, owner, source, n->lhs->str, 1);
|
||||
}
|
||||
for (Node *p = n->attr; p; p = p->next)
|
||||
mark_import_uses_node(c, p, owner, source);
|
||||
mark_import_uses_node(c, n->lhs, owner, source);
|
||||
mark_import_uses_node(c, n->rhs, owner, source);
|
||||
mark_import_uses_node(c, n->cond, owner, source);
|
||||
mark_import_uses_node(c, n->body, owner, source);
|
||||
mark_import_uses_node(c, n->els, owner, source);
|
||||
for (Node *p = n->list; p; p = p->next)
|
||||
mark_import_uses_node(c, p, owner, source);
|
||||
}
|
||||
|
||||
static void
|
||||
mark_import_uses(Checker *c, Node *file)
|
||||
{
|
||||
for (Node *d = file->list; d; d = d->next) {
|
||||
if (d->kind == N_USE) continue;
|
||||
mark_import_uses_node(c, d, decl_mod(file, d), d->sourceid);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
check_import_alt(Node *d, const char *name)
|
||||
{
|
||||
@@ -3204,7 +3212,8 @@ check_import_usage_and_collisions(Checker *c, Node *file)
|
||||
for (Node *u = file->list; u; u = u->next) {
|
||||
if (u->kind != N_USE || u->imported || u->used || u->str == NULL)
|
||||
continue;
|
||||
const char *path = u->usepath ? u->usepath : u->str;
|
||||
const char *path = u->usesource ? u->usesource
|
||||
: u->usepath ? u->usepath : u->str;
|
||||
const char *dot = strrchr(path, '.');
|
||||
const char *leaf = dot ? dot + 1 : path;
|
||||
if (strcmp(u->str, leaf) == 0)
|
||||
@@ -3220,7 +3229,8 @@ check_import_usage_and_collisions(Checker *c, Node *file)
|
||||
if (u->kind != N_USE || u->imported || u->str == NULL
|
||||
|| strcmp(d->str, u->str) != 0)
|
||||
continue;
|
||||
const char *path = u->usepath ? u->usepath : u->str;
|
||||
const char *path = u->usesource ? u->usesource
|
||||
: u->usepath ? u->usepath : u->str;
|
||||
err(c, d->pos,
|
||||
"%s already declared through import of package %s (\"%s\")",
|
||||
d->str, u->str, path);
|
||||
@@ -3248,11 +3258,12 @@ check_file(Checker *c, Node *file)
|
||||
/* The synthetic runner belongs to file->sourceid. An import in an
|
||||
* earlier module-reset section neither supplies nor uses its binding. */
|
||||
for (Node *u = file->list; u; u = u->next) {
|
||||
if (c->test_target != NULL && u->kind == N_USE
|
||||
if (u->kind == N_USE
|
||||
&& !u->imported && u->sourceid == file->sourceid
|
||||
&& u->usepath
|
||||
&& (strcmp(u->usepath, c->test_target) == 0
|
||||
|| strcmp(u->usepath, c->test_module) == 0))
|
||||
&& (strcmp(u->usepath, c->test_module) == 0
|
||||
|| (c->test_target != NULL
|
||||
&& strcmp(u->usepath, c->test_target) == 0)))
|
||||
u->used = 1;
|
||||
if (u->kind == N_USE && !u->imported
|
||||
&& u->sourceid == file->sourceid
|
||||
@@ -3264,15 +3275,18 @@ check_file(Checker *c, Node *file)
|
||||
Node *usenode = newnode(c->a, N_USE, file->pos);
|
||||
usenode->str = c->test_module;
|
||||
usenode->strlen = strlen(c->test_module);
|
||||
usenode->usesource = c->test_module;
|
||||
usenode->usepath = c->test_module;
|
||||
usenode->pkgname = file->pkgname;
|
||||
usenode->sourceid = file->sourceid;
|
||||
if (c->test_target != NULL) usenode->used = 1;
|
||||
usenode->used = 1;
|
||||
usenode->next = file->list;
|
||||
file->list = usenode;
|
||||
}
|
||||
}
|
||||
mark_import_uses(c, file);
|
||||
check_import_redeclarations(c, file);
|
||||
check_import_usage_and_collisions(c, file);
|
||||
|
||||
/* pass 1: install names (types first, then defs/fns).
|
||||
* For self-referential types we install the named-type placeholder
|
||||
@@ -3915,8 +3929,6 @@ check_file(Checker *c, Node *file)
|
||||
}
|
||||
c->cur_mod = NULL;
|
||||
c->cur_source = 0;
|
||||
check_import_usage_and_collisions(c, file);
|
||||
|
||||
/*
|
||||
* #6 harec-fidelity (ref/harec/src/check.c:3941): a @test fn is
|
||||
* fully checked above — pass 2 walked its body like every fn — but
|
||||
|
||||
@@ -1315,30 +1315,40 @@ parseblock(Parser *p)
|
||||
return n;
|
||||
}
|
||||
|
||||
/* `import encoding.utf8;` — the driver resolves the dotted path to a
|
||||
* directory; the checker only needs the leaf (`utf8`) as the module
|
||||
* bareword for n_use→decl disambiguation, mirroring Hare's
|
||||
* `use encoding::utf8;` → `utf8::name` (ref/hare/hare/ast/import.ha:7
|
||||
* stores `ident: []str` but identifier-resolution uses the last
|
||||
* component). */
|
||||
/* The optional alias is source-local; the dotted path remains the dependency
|
||||
* identity supplied to the package driver. */
|
||||
static Node *
|
||||
parseuse(Parser *p)
|
||||
{
|
||||
Pos pp = p->cur.pos;
|
||||
expect(p, TK_USE);
|
||||
Node *n = newnode(p->a, N_USE, pp);
|
||||
/* M1 #22: accumulate the full dotted import path in usepath. `str`
|
||||
* starts as its leaf; direct export metadata later installs the imported
|
||||
* declaration's default name without changing canonical identity. */
|
||||
const char *leaf = expectident(p);
|
||||
const char *alias = NULL;
|
||||
const char *first;
|
||||
if (p->cur.kind == TK_UNDER) {
|
||||
errorf(p->cur.pos, "blank import alias _ is not implemented");
|
||||
p->errs++;
|
||||
alias = "_";
|
||||
advance(p);
|
||||
first = expectident(p);
|
||||
} else {
|
||||
first = expectident(p);
|
||||
if (p->cur.kind == TK_IDENT) {
|
||||
alias = first;
|
||||
first = expectident(p);
|
||||
}
|
||||
}
|
||||
const char *leaf = first;
|
||||
const char *path = leaf;
|
||||
while (accept(p, TK_DOT)) {
|
||||
leaf = expectident(p);
|
||||
path = aprintf(p->a, "%s.%s", path, leaf);
|
||||
}
|
||||
n->str = leaf;
|
||||
n->strlen = strlen(leaf);
|
||||
n->str = alias ? alias : leaf;
|
||||
n->strlen = strlen(n->str);
|
||||
n->usesource = path;
|
||||
n->usepath = path;
|
||||
n->usealias = alias;
|
||||
expect(p, TK_SEMI);
|
||||
return n;
|
||||
}
|
||||
|
||||
11
cmd/wcc/ww.h
11
cmd/wcc/ww.h
@@ -343,11 +343,12 @@ struct Node {
|
||||
* let) carry it. On an N_USE node
|
||||
* this is the importing (owning)
|
||||
* module. */
|
||||
const char *usepath; /* M1 #22: on an N_USE node, the full
|
||||
* canonical import path (`encoding.utf8`);
|
||||
* `str` is the declared default qualifier. Drives
|
||||
* the path-keyed decl_mod match and the
|
||||
* qualified-ref codegen hint. */
|
||||
const char *usesource; /* N_USE: immutable dotted source spelling. */
|
||||
const char *usepath; /* N_USE: canonical, vendor-expanded identity;
|
||||
* initially equal to `usesource`. */
|
||||
const char *usealias; /* N_USE: explicit file-local alias, or NULL. */
|
||||
const char *usepkgname; /* N_USE: imported declared package name,
|
||||
* independent of the visible binding in `str`. */
|
||||
const char *pkgname; /* declared package name for this source/export
|
||||
* section; independent of canonical `module`. */
|
||||
int sourceid; /* lexical source-file scope within the parsed
|
||||
|
||||
Reference in New Issue
Block a user