ww: add file-scoped import aliases

This commit is contained in:
2026-08-14 10:56:34 +09:00
parent 351f4a25bc
commit 792b6ecbe5
14 changed files with 587 additions and 430 deletions

View File

@@ -67,38 +67,63 @@ parseinput(Arena *a, const char *file, char *buf, u64 len,
return f;
}
/* Export data carries canonical owner and declared package name separately.
* Search every direct interface's package-clause markers because its
* self-contained fact closure can also name a transitive owner. */
static int
canonical_pkgname(const char *path, const char *name)
{
static const char hex[] = "0123456789abcdef";
const char *prefix = "__wwi_";
for (int i = 0; prefix[i]; i++)
if (*name++ != prefix[i]) return 0;
for (const unsigned char *p = (const unsigned char *)path; *p; p++) {
if (*name++ != hex[*p >> 4] || *name++ != hex[*p & 15])
return 0;
}
return *name == '\0';
}
static void
consider_pkgname(const char *path, const char *candidate,
const char **name, const char **placeholder, int *conflict)
{
if (canonical_pkgname(path, candidate)) {
*placeholder = candidate;
return;
}
if (*name != NULL && strcmp(*name, candidate) != 0) {
*conflict = 1;
return;
}
*name = candidate;
}
/* A paired interface owns PATH's declared name. Compiler-private names keep
* transitive fact sections semantic and are ignored when a real name exists. */
static const char *
import_pkgname(struct importin *imports, int nimports, const char *path,
Node *primary, int *conflict)
{
const char *name = NULL;
const char *placeholder = NULL;
for (int i = 0; i < nimports; i++) {
if (strcmp(imports[i].path, path) != 0) continue;
Node *file = imports[i].ast;
for (Node *p = file ? file->body : NULL; p; p = p->next) {
if (p->module == NULL || p->pkgname == NULL
|| strcmp(p->module, path) != 0)
continue;
if (name != NULL && strcmp(name, p->pkgname) != 0) {
*conflict = 1;
return NULL;
}
name = p->pkgname;
consider_pkgname(path, p->pkgname, &name, &placeholder,
conflict);
if (*conflict) return NULL;
}
}
for (Node *p = primary ? primary->body : NULL; p; p = p->next) {
if (p->module == NULL || p->pkgname == NULL
|| strcmp(p->module, path) != 0)
continue;
if (name != NULL && strcmp(name, p->pkgname) != 0) {
*conflict = 1;
return NULL;
}
name = p->pkgname;
consider_pkgname(path, p->pkgname, &name, &placeholder, conflict);
if (*conflict) return NULL;
}
return name;
return name ? name : placeholder;
}
static int
@@ -122,8 +147,9 @@ bind_import_names(Node *list, struct importin *imports, int nimports,
return -1;
}
if (name != NULL) {
u->str = name;
u->strlen = strlen(name);
u->usepkgname = name;
u->str = u->usealias ? u->usealias : name;
u->strlen = strlen(u->str);
} else if (!u->imported) {
fprintf(stderr,
"w6c: import %s has no declared package name in direct export data\n",
@@ -133,8 +159,8 @@ bind_import_names(Node *list, struct importin *imports, int nimports,
/* A closure-only import need not contribute declarations to this
* interface. Keep it canonical-path keyed without reinstalling
* the historical path-leaf qualifier. */
u->str = u->usepath;
u->strlen = strlen(u->usepath);
u->str = u->usealias ? u->usealias : u->usepath;
u->strlen = strlen(u->str);
}
}
return 0;
@@ -348,13 +374,13 @@ main(int argc, char **argv)
Node *file = parseinput(a, src, buf, len, NULL, testsupport,
commandpackage || entrymode, &bad);
if (bad) return 1;
/* Source keeps its effective spelling and position, while package
* resolution supplies the expanded canonical owner. Rewrite only the
* primary import key before imported interface nodes are prepended. */
/* Vendor expansion changes only canonical identity. Source spelling and
* an optional file-local alias remain independent facts. */
for (Node *u = file->list; u; u = u->next) {
if (u->kind != N_USE || u->usepath == NULL) continue;
const char *source = u->usesource ? u->usesource : u->usepath;
for (int i = 0; i < nmaps; i++)
if (strcmp(u->usepath, maps[i].source) == 0) {
if (strcmp(source, maps[i].source) == 0) {
u->usepath = maps[i].path;
maps[i].seen = 1;
break;