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

@@ -68,19 +68,41 @@ wwi_use_path(Checker *c, const char *owner, int source, const char *alias)
return NULL;
}
static int
wwi_direct_mod_visible(Checker *c, const char *owner, int source,
const char *mod)
/* Export spelling is derived only from canonical identity. The bytewise hex
* encoding is injective, source-identifier-safe, and independent of a local
* alias or the dependency's declared package name. */
static void
wwi_canonical_alias(FILE *of, const char *path)
{
if (mod == NULL || mod[0] == '\0') return 0;
for (Node *u = c->file->list; u; u = u->next) {
if (u->kind != N_USE || u->sourceid != source) continue;
int same = owner == NULL ? u->imported == 0
: u->imported != 0 && wwi_mod_eq(u->module, owner);
const char *path = u->usepath ? u->usepath : u->str;
if (same && path != NULL && strcmp(path, mod) == 0) return 1;
static const char hex[] = "0123456789abcdef";
fputs("__wwi_", of);
for (const unsigned char *p = (const unsigned char *)path; *p; p++) {
fputc(hex[*p >> 4], of);
fputc(hex[*p & 15], of);
}
return 0;
}
static void
wwi_name(Checker *c, FILE *of, const char *owner, int source,
const char *name)
{
if (name == NULL) return;
const char *dot = strrchr(name, '.');
if (dot != NULL) {
size_t n = (size_t)(dot - name);
char *alias = malloc(n + 1);
if (alias == NULL) fatal("wwi: out of memory");
memcpy(alias, name, n);
alias[n] = '\0';
const char *path = wwi_use_path(c, owner, source, alias);
free(alias);
if (path != NULL) {
wwi_canonical_alias(of, path);
fputs(dot, of);
return;
}
}
fputs(name, of);
}
static Sym *
@@ -101,20 +123,6 @@ wwi_typesym(Checker *c, const char *owner, int source, const char *nm)
free(alias);
} else {
s = scope_lookup_type(c->cur, owner, nm);
if (s == NULL) {
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, nm) == 0
&& wwi_direct_mod_visible(c, owner, source,
b->mod)) {
s = b;
break;
}
}
if (s != NULL) break;
}
}
}
if (s && s->kind == SK_TYPE)
return s;
@@ -197,8 +205,8 @@ wwi_check_decl(Checker *c, Node *d)
/* type-expr unparse per rob §2.2; const-expr per rob §2.4. */
static void wwi_expr(FILE *of, Node *e);
static void wwi_type(FILE *of, Node *t);
static void wwi_expr(Checker*, FILE*, const char*, int, Node*);
static void wwi_type(Checker*, FILE*, const char*, int, Node*);
static void
wwi_quote(FILE *of, const char *s, u64 n)
@@ -248,7 +256,7 @@ wwi_rune(FILE *of, u64 cp)
}
static void
wwi_param(FILE *of, Node *p)
wwi_param(Checker *c, FILE *of, const char *owner, int source, Node *p)
{
if (p->str && strcmp(p->str, "...") == 0) { /* C-style FFI `...` */
fputs("...", of);
@@ -258,13 +266,13 @@ wwi_param(FILE *of, Node *p)
fputs(p->str, of);
fputs(": ", of);
}
wwi_type(of, p->lhs);
wwi_type(c, of, owner, source, p->lhs);
if (p->op == TK_ELLIPSIS) /* Hare `T...` variadic */
fputs("...", of);
}
static void
wwi_type(FILE *of, Node *t)
wwi_type(Checker *c, FILE *of, const char *owner, int source, Node *t)
{
if (t == NULL) { /* absent return type spells void */
fputs("void", of);
@@ -272,39 +280,40 @@ wwi_type(FILE *of, Node *t)
}
switch (t->kind) {
case N_TNAME:
fputs(t->str ? t->str : "void", of);
if (t->str) wwi_name(c, of, owner, source, t->str);
else fputs("void", of);
break;
case N_TPTR:
fputc('*', of);
wwi_type(of, t->lhs);
wwi_type(c, of, owner, source, t->lhs);
break;
case N_TSLICE:
fputs("[]", of);
wwi_type(of, t->lhs);
wwi_type(c, of, owner, source, t->lhs);
break;
case N_TARRAY:
fputc('[', of);
if (t->rhs) wwi_expr(of, t->rhs);
if (t->rhs) wwi_expr(c, of, owner, source, t->rhs);
else fputc('_', of);
fputc(']', of);
wwi_type(of, t->lhs);
wwi_type(c, of, owner, source, t->lhs);
break;
case N_TBANG:
fputc('!', of);
wwi_type(of, t->lhs);
wwi_type(c, of, owner, source, t->lhs);
break;
case N_TCHAN:
fputs("chan ", of);
wwi_type(of, t->lhs);
wwi_type(c, of, owner, source, t->lhs);
break;
case N_TFN:
fputs("fn(", of);
for (Node *p = t->list; p; p = p->next) {
if (p != t->list) fputs(", ", of);
wwi_param(of, p);
wwi_param(c, of, owner, source, p);
}
fputs(") ", of);
wwi_type(of, t->lhs);
wwi_type(c, of, owner, source, t->lhs);
break;
case N_TSTRUCT:
/* re-emit the `@packed` attr so the flag round-trips
@@ -316,7 +325,7 @@ wwi_type(FILE *of, Node *t)
fputs(f->str, of);
fputs(": ", of);
}
wwi_type(of, f->lhs);
wwi_type(c, of, owner, source, f->lhs);
}
fputs(" }", of);
break;
@@ -324,7 +333,7 @@ wwi_type(FILE *of, Node *t)
fputc('(', of);
for (Node *e = t->list; e; e = e->next) {
if (e != t->list) fputs(", ", of);
wwi_type(of, e);
wwi_type(c, of, owner, source, e);
}
fputc(')', of);
break;
@@ -337,14 +346,14 @@ wwi_type(FILE *of, Node *t)
* unparse/type.ha:290-300). */
if (e->op == TK_ELLIPSIS)
fputs("...", of);
wwi_type(of, e);
wwi_type(c, of, owner, source, e);
}
fputc(')', of);
break;
case N_TENUM:
fputs("enum ", of);
if (t->lhs) {
wwi_type(of, t->lhs);
wwi_type(c, of, owner, source, t->lhs);
fputc(' ', of);
}
fputs("{ ", of);
@@ -353,7 +362,7 @@ wwi_type(FILE *of, Node *t)
fputs(m->str, of);
if (m->lhs) {
fputs(" = ", of);
wwi_expr(of, m->lhs);
wwi_expr(c, of, owner, source, m->lhs);
}
}
fputs(" }", of);
@@ -364,7 +373,7 @@ wwi_type(FILE *of, Node *t)
}
static void
wwi_expr(FILE *of, Node *e)
wwi_expr(Checker *c, FILE *of, const char *owner, int source, Node *e)
{
if (e == NULL)
return;
@@ -375,10 +384,17 @@ wwi_expr(FILE *of, Node *e)
break;
case N_IDENT:
case N_TNAME:
fputs(e->str, of);
wwi_name(c, of, owner, source, e->str);
break;
case N_DOT:
wwi_expr(of, e->lhs);
if (e->lhs && e->lhs->kind == N_IDENT) {
const char *path = wwi_use_path(c, owner, source,
e->lhs->str);
if (path != NULL) wwi_canonical_alias(of, path);
else wwi_expr(c, of, owner, source, e->lhs);
} else {
wwi_expr(c, of, owner, source, e->lhs);
}
fputc('.', of);
fputs(e->str, of);
break;
@@ -393,20 +409,20 @@ wwi_expr(FILE *of, Node *e)
case N_RUNELIT: wwi_rune(of, e->uval); break;
case N_BIN:
fputc('(', of);
wwi_expr(of, e->lhs);
wwi_expr(c, of, owner, source, e->lhs);
fprintf(of, " %s ", tokname(e->op));
wwi_expr(of, e->rhs);
wwi_expr(c, of, owner, source, e->rhs);
fputc(')', of);
break;
case N_UN:
fputs(tokname(e->op), of);
wwi_expr(of, e->lhs);
wwi_expr(c, of, owner, source, e->lhs);
break;
case N_CAST:
fputc('(', of);
wwi_expr(of, e->lhs);
wwi_expr(c, of, owner, source, e->lhs);
fputs(": ", of);
wwi_type(of, e->rhs);
wwi_type(c, of, owner, source, e->rhs);
fputc(')', of);
break;
default:
@@ -428,7 +444,7 @@ wwi_attr_relevant(const char *nm)
}
static void
wwi_attrs(FILE *of, Node *d)
wwi_attrs(Checker *c, FILE *of, const char *owner, Node *d)
{
for (Node *a = d->attr; a; a = a->next) {
if (a->kind != N_ATTR || !wwi_attr_relevant(a->str))
@@ -439,7 +455,7 @@ wwi_attrs(FILE *of, Node *d)
fputc('(', of);
for (Node *arg = a->list; arg; arg = arg->next) {
if (arg != a->list) fputs(", ", of);
wwi_expr(of, arg);
wwi_expr(c, of, owner, d->sourceid, arg);
}
fputc(')', of);
}
@@ -448,34 +464,34 @@ wwi_attrs(FILE *of, Node *d)
}
static void
wwi_decl(FILE *of, Node *d)
wwi_decl(Checker *c, FILE *of, const char *owner, Node *d)
{
switch (d->kind) {
case N_FNDECL:
wwi_attrs(of, d);
wwi_attrs(c, of, owner, d);
fputs(d->export ? "export fn " : "fn ", of);
fputs(d->str, of);
fputc('(', of);
for (Node *p = d->list; p; p = p->next) {
if (p != d->list) fputs(", ", of);
wwi_param(of, p);
wwi_param(c, of, owner, d->sourceid, p);
}
fputs(") ", of);
wwi_type(of, d->lhs);
wwi_type(c, of, owner, d->sourceid, d->lhs);
fputs(";\n", of);
break;
case N_TYPEDECL:
fputs(d->export ? "export type " : "type ", of);
fputs(d->str, of);
fputs(" = ", of);
wwi_type(of, d->lhs);
wwi_type(c, of, owner, d->sourceid, d->lhs);
fputs(";\n", of);
break;
case N_DEF:
fputs(d->export ? "export def " : "def ", of);
fputs(d->str, of);
fputs(": ", of);
wwi_type(of, d->lhs);
wwi_type(c, of, owner, d->sourceid, d->lhs);
/* An aggregate initializer (N_STRUCTLIT/N_ARRLIT) is a DATA-
* global (#52): emit a value-LESS prototype `export def X: T;`.
* The defining package's own .o emits the struct/array DATA; the
@@ -491,7 +507,7 @@ wwi_decl(FILE *of, Node *d)
fputs(";\n", of);
} else {
fputs(" = ", of);
wwi_expr(of, d->rhs);
wwi_expr(c, of, owner, d->sourceid, d->rhs);
fputs(";\n", of);
}
break;
@@ -502,7 +518,7 @@ wwi_decl(FILE *of, Node *d)
if (d->lhs == NULL)
fatal("wwi: exported let '%s' has no declared type",
d->str);
wwi_type(of, d->lhs);
wwi_type(c, of, owner, d->sourceid, d->lhs);
fputs(";\n", of);
break;
default:
@@ -616,11 +632,6 @@ wwi_valuesym(Checker *c, const char *owner, int source, const char *name)
if (s->kind == SK_DEF && strcmp(s->name, name) == 0
&& wwi_mod_eq(s->mod, owner))
return s;
for (Scope *p = c->top; p; p = p->parent)
for (Sym *s = p->first; s; s = s->next)
if (s->kind == SK_DEF && strcmp(s->name, name) == 0
&& wwi_direct_mod_visible(c, owner, source, s->mod))
return s;
return NULL;
}
@@ -812,7 +823,9 @@ wwi_emit_imports(FILE *of, Node *file, const char *owner, int source,
const char *previous = NULL;
for (int i = 0; i < nuse; i++) {
if (previous && strcmp(previous, us[i].path) == 0) continue;
fprintf(of, "import %s;\n", us[i].path);
fputs("import ", of);
wwi_canonical_alias(of, us[i].path);
fprintf(of, " %s;\n", us[i].path);
previous = us[i].path;
}
free(us);
@@ -865,16 +878,16 @@ wwi_emit_primary_section(Checker *c, FILE *of, Node *file,
wwi_emit_imports(of, file, owner, source, 0);
for (int i = 0; i < fs->nprivate; i++)
if (fs->privatefacts[i].d->sourceid == source)
wwi_decl(of, fs->privatefacts[i].d);
wwi_decl(c, of, NULL, fs->privatefacts[i].d);
if (c->is_test_package)
for (Node *d = file->list; d; d = d->next)
if (wwi_primary(d) && d->sourceid == source
&& d->kind == N_FNDECL && !d->export
&& wwi_has_attr(d, "test"))
wwi_decl(of, d);
wwi_decl(c, of, NULL, d);
for (int i = 0; i < nexports; i++)
if (exports[i].d->sourceid == source)
wwi_decl(of, exports[i].d);
wwi_decl(c, of, NULL, exports[i].d);
}
int
@@ -964,16 +977,15 @@ wwi_emit(Checker *c, FILE *of, Node *file)
struct factent *f = &fs.facts[i];
if (lastmod == NULL || strcmp(lastmod, f->mod) != 0
|| lastsource != f->d->sourceid) {
const char *dot = strrchr(f->mod, '.');
const char *factpkg = f->d->pkgname && f->d->pkgname[0]
? f->d->pkgname : (dot ? dot + 1 : f->mod);
fprintf(of, "//ww:module %s\n", f->mod);
fprintf(of, "package %s;\n", factpkg);
fputs("package ", of);
wwi_canonical_alias(of, f->mod);
fputs(";\n", of);
wwi_emit_imports(of, file, f->mod, f->d->sourceid, 1);
lastmod = f->mod;
lastsource = f->d->sourceid;
}
wwi_decl(of, f->d);
wwi_decl(c, of, f->mod, f->d);
}
free(fs.privatefacts);
free(fs.facts);