ww: add file-scoped import aliases
This commit is contained in:
@@ -67,38 +67,63 @@ parseinput(Arena *a, const char *file, char *buf, u64 len,
|
|||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Export data carries canonical owner and declared package name separately.
|
static int
|
||||||
* Search every direct interface's package-clause markers because its
|
canonical_pkgname(const char *path, const char *name)
|
||||||
* self-contained fact closure can also name a transitive owner. */
|
{
|
||||||
|
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 *
|
static const char *
|
||||||
import_pkgname(struct importin *imports, int nimports, const char *path,
|
import_pkgname(struct importin *imports, int nimports, const char *path,
|
||||||
Node *primary, int *conflict)
|
Node *primary, int *conflict)
|
||||||
{
|
{
|
||||||
const char *name = NULL;
|
const char *name = NULL;
|
||||||
|
const char *placeholder = NULL;
|
||||||
for (int i = 0; i < nimports; i++) {
|
for (int i = 0; i < nimports; i++) {
|
||||||
|
if (strcmp(imports[i].path, path) != 0) continue;
|
||||||
Node *file = imports[i].ast;
|
Node *file = imports[i].ast;
|
||||||
for (Node *p = file ? file->body : NULL; p; p = p->next) {
|
for (Node *p = file ? file->body : NULL; p; p = p->next) {
|
||||||
if (p->module == NULL || p->pkgname == NULL
|
if (p->module == NULL || p->pkgname == NULL
|
||||||
|| strcmp(p->module, path) != 0)
|
|| strcmp(p->module, path) != 0)
|
||||||
continue;
|
continue;
|
||||||
if (name != NULL && strcmp(name, p->pkgname) != 0) {
|
consider_pkgname(path, p->pkgname, &name, &placeholder,
|
||||||
*conflict = 1;
|
conflict);
|
||||||
return NULL;
|
if (*conflict) return NULL;
|
||||||
}
|
|
||||||
name = p->pkgname;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (Node *p = primary ? primary->body : NULL; p; p = p->next) {
|
for (Node *p = primary ? primary->body : NULL; p; p = p->next) {
|
||||||
if (p->module == NULL || p->pkgname == NULL
|
if (p->module == NULL || p->pkgname == NULL
|
||||||
|| strcmp(p->module, path) != 0)
|
|| strcmp(p->module, path) != 0)
|
||||||
continue;
|
continue;
|
||||||
if (name != NULL && strcmp(name, p->pkgname) != 0) {
|
consider_pkgname(path, p->pkgname, &name, &placeholder, conflict);
|
||||||
*conflict = 1;
|
if (*conflict) return NULL;
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
name = p->pkgname;
|
|
||||||
}
|
}
|
||||||
return name;
|
return name ? name : placeholder;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@@ -122,8 +147,9 @@ bind_import_names(Node *list, struct importin *imports, int nimports,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (name != NULL) {
|
if (name != NULL) {
|
||||||
u->str = name;
|
u->usepkgname = name;
|
||||||
u->strlen = strlen(name);
|
u->str = u->usealias ? u->usealias : name;
|
||||||
|
u->strlen = strlen(u->str);
|
||||||
} else if (!u->imported) {
|
} else if (!u->imported) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"w6c: import %s has no declared package name in direct export data\n",
|
"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
|
/* A closure-only import need not contribute declarations to this
|
||||||
* interface. Keep it canonical-path keyed without reinstalling
|
* interface. Keep it canonical-path keyed without reinstalling
|
||||||
* the historical path-leaf qualifier. */
|
* the historical path-leaf qualifier. */
|
||||||
u->str = u->usepath;
|
u->str = u->usealias ? u->usealias : u->usepath;
|
||||||
u->strlen = strlen(u->usepath);
|
u->strlen = strlen(u->str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -348,13 +374,13 @@ main(int argc, char **argv)
|
|||||||
Node *file = parseinput(a, src, buf, len, NULL, testsupport,
|
Node *file = parseinput(a, src, buf, len, NULL, testsupport,
|
||||||
commandpackage || entrymode, &bad);
|
commandpackage || entrymode, &bad);
|
||||||
if (bad) return 1;
|
if (bad) return 1;
|
||||||
/* Source keeps its effective spelling and position, while package
|
/* Vendor expansion changes only canonical identity. Source spelling and
|
||||||
* resolution supplies the expanded canonical owner. Rewrite only the
|
* an optional file-local alias remain independent facts. */
|
||||||
* primary import key before imported interface nodes are prepended. */
|
|
||||||
for (Node *u = file->list; u; u = u->next) {
|
for (Node *u = file->list; u; u = u->next) {
|
||||||
if (u->kind != N_USE || u->usepath == NULL) continue;
|
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++)
|
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;
|
u->usepath = maps[i].path;
|
||||||
maps[i].seen = 1;
|
maps[i].seen = 1;
|
||||||
break;
|
break;
|
||||||
|
|||||||
164
cmd/w6c/wwi.c
164
cmd/w6c/wwi.c
@@ -68,19 +68,41 @@ wwi_use_path(Checker *c, const char *owner, int source, const char *alias)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
/* Export spelling is derived only from canonical identity. The bytewise hex
|
||||||
wwi_direct_mod_visible(Checker *c, const char *owner, int source,
|
* encoding is injective, source-identifier-safe, and independent of a local
|
||||||
const char *mod)
|
* 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;
|
static const char hex[] = "0123456789abcdef";
|
||||||
for (Node *u = c->file->list; u; u = u->next) {
|
fputs("__wwi_", of);
|
||||||
if (u->kind != N_USE || u->sourceid != source) continue;
|
for (const unsigned char *p = (const unsigned char *)path; *p; p++) {
|
||||||
int same = owner == NULL ? u->imported == 0
|
fputc(hex[*p >> 4], of);
|
||||||
: u->imported != 0 && wwi_mod_eq(u->module, owner);
|
fputc(hex[*p & 15], of);
|
||||||
const char *path = u->usepath ? u->usepath : u->str;
|
|
||||||
if (same && path != NULL && strcmp(path, mod) == 0) return 1;
|
|
||||||
}
|
}
|
||||||
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 *
|
static Sym *
|
||||||
@@ -101,20 +123,6 @@ wwi_typesym(Checker *c, const char *owner, int source, const char *nm)
|
|||||||
free(alias);
|
free(alias);
|
||||||
} else {
|
} else {
|
||||||
s = scope_lookup_type(c->cur, owner, nm);
|
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)
|
if (s && s->kind == SK_TYPE)
|
||||||
return s;
|
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. */
|
/* type-expr unparse per rob §2.2; const-expr per rob §2.4. */
|
||||||
|
|
||||||
static void wwi_expr(FILE *of, Node *e);
|
static void wwi_expr(Checker*, FILE*, const char*, int, Node*);
|
||||||
static void wwi_type(FILE *of, Node *t);
|
static void wwi_type(Checker*, FILE*, const char*, int, Node*);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
wwi_quote(FILE *of, const char *s, u64 n)
|
wwi_quote(FILE *of, const char *s, u64 n)
|
||||||
@@ -248,7 +256,7 @@ wwi_rune(FILE *of, u64 cp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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 `...` */
|
if (p->str && strcmp(p->str, "...") == 0) { /* C-style FFI `...` */
|
||||||
fputs("...", of);
|
fputs("...", of);
|
||||||
@@ -258,13 +266,13 @@ wwi_param(FILE *of, Node *p)
|
|||||||
fputs(p->str, of);
|
fputs(p->str, of);
|
||||||
fputs(": ", of);
|
fputs(": ", of);
|
||||||
}
|
}
|
||||||
wwi_type(of, p->lhs);
|
wwi_type(c, of, owner, source, p->lhs);
|
||||||
if (p->op == TK_ELLIPSIS) /* Hare `T...` variadic */
|
if (p->op == TK_ELLIPSIS) /* Hare `T...` variadic */
|
||||||
fputs("...", of);
|
fputs("...", of);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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 */
|
if (t == NULL) { /* absent return type spells void */
|
||||||
fputs("void", of);
|
fputs("void", of);
|
||||||
@@ -272,39 +280,40 @@ wwi_type(FILE *of, Node *t)
|
|||||||
}
|
}
|
||||||
switch (t->kind) {
|
switch (t->kind) {
|
||||||
case N_TNAME:
|
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;
|
break;
|
||||||
case N_TPTR:
|
case N_TPTR:
|
||||||
fputc('*', of);
|
fputc('*', of);
|
||||||
wwi_type(of, t->lhs);
|
wwi_type(c, of, owner, source, t->lhs);
|
||||||
break;
|
break;
|
||||||
case N_TSLICE:
|
case N_TSLICE:
|
||||||
fputs("[]", of);
|
fputs("[]", of);
|
||||||
wwi_type(of, t->lhs);
|
wwi_type(c, of, owner, source, t->lhs);
|
||||||
break;
|
break;
|
||||||
case N_TARRAY:
|
case N_TARRAY:
|
||||||
fputc('[', of);
|
fputc('[', of);
|
||||||
if (t->rhs) wwi_expr(of, t->rhs);
|
if (t->rhs) wwi_expr(c, of, owner, source, t->rhs);
|
||||||
else fputc('_', of);
|
else fputc('_', of);
|
||||||
fputc(']', of);
|
fputc(']', of);
|
||||||
wwi_type(of, t->lhs);
|
wwi_type(c, of, owner, source, t->lhs);
|
||||||
break;
|
break;
|
||||||
case N_TBANG:
|
case N_TBANG:
|
||||||
fputc('!', of);
|
fputc('!', of);
|
||||||
wwi_type(of, t->lhs);
|
wwi_type(c, of, owner, source, t->lhs);
|
||||||
break;
|
break;
|
||||||
case N_TCHAN:
|
case N_TCHAN:
|
||||||
fputs("chan ", of);
|
fputs("chan ", of);
|
||||||
wwi_type(of, t->lhs);
|
wwi_type(c, of, owner, source, t->lhs);
|
||||||
break;
|
break;
|
||||||
case N_TFN:
|
case N_TFN:
|
||||||
fputs("fn(", of);
|
fputs("fn(", of);
|
||||||
for (Node *p = t->list; p; p = p->next) {
|
for (Node *p = t->list; p; p = p->next) {
|
||||||
if (p != t->list) fputs(", ", of);
|
if (p != t->list) fputs(", ", of);
|
||||||
wwi_param(of, p);
|
wwi_param(c, of, owner, source, p);
|
||||||
}
|
}
|
||||||
fputs(") ", of);
|
fputs(") ", of);
|
||||||
wwi_type(of, t->lhs);
|
wwi_type(c, of, owner, source, t->lhs);
|
||||||
break;
|
break;
|
||||||
case N_TSTRUCT:
|
case N_TSTRUCT:
|
||||||
/* re-emit the `@packed` attr so the flag round-trips
|
/* 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(f->str, of);
|
||||||
fputs(": ", of);
|
fputs(": ", of);
|
||||||
}
|
}
|
||||||
wwi_type(of, f->lhs);
|
wwi_type(c, of, owner, source, f->lhs);
|
||||||
}
|
}
|
||||||
fputs(" }", of);
|
fputs(" }", of);
|
||||||
break;
|
break;
|
||||||
@@ -324,7 +333,7 @@ wwi_type(FILE *of, Node *t)
|
|||||||
fputc('(', of);
|
fputc('(', of);
|
||||||
for (Node *e = t->list; e; e = e->next) {
|
for (Node *e = t->list; e; e = e->next) {
|
||||||
if (e != t->list) fputs(", ", of);
|
if (e != t->list) fputs(", ", of);
|
||||||
wwi_type(of, e);
|
wwi_type(c, of, owner, source, e);
|
||||||
}
|
}
|
||||||
fputc(')', of);
|
fputc(')', of);
|
||||||
break;
|
break;
|
||||||
@@ -337,14 +346,14 @@ wwi_type(FILE *of, Node *t)
|
|||||||
* unparse/type.ha:290-300). */
|
* unparse/type.ha:290-300). */
|
||||||
if (e->op == TK_ELLIPSIS)
|
if (e->op == TK_ELLIPSIS)
|
||||||
fputs("...", of);
|
fputs("...", of);
|
||||||
wwi_type(of, e);
|
wwi_type(c, of, owner, source, e);
|
||||||
}
|
}
|
||||||
fputc(')', of);
|
fputc(')', of);
|
||||||
break;
|
break;
|
||||||
case N_TENUM:
|
case N_TENUM:
|
||||||
fputs("enum ", of);
|
fputs("enum ", of);
|
||||||
if (t->lhs) {
|
if (t->lhs) {
|
||||||
wwi_type(of, t->lhs);
|
wwi_type(c, of, owner, source, t->lhs);
|
||||||
fputc(' ', of);
|
fputc(' ', of);
|
||||||
}
|
}
|
||||||
fputs("{ ", of);
|
fputs("{ ", of);
|
||||||
@@ -353,7 +362,7 @@ wwi_type(FILE *of, Node *t)
|
|||||||
fputs(m->str, of);
|
fputs(m->str, of);
|
||||||
if (m->lhs) {
|
if (m->lhs) {
|
||||||
fputs(" = ", of);
|
fputs(" = ", of);
|
||||||
wwi_expr(of, m->lhs);
|
wwi_expr(c, of, owner, source, m->lhs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fputs(" }", of);
|
fputs(" }", of);
|
||||||
@@ -364,7 +373,7 @@ wwi_type(FILE *of, Node *t)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
wwi_expr(FILE *of, Node *e)
|
wwi_expr(Checker *c, FILE *of, const char *owner, int source, Node *e)
|
||||||
{
|
{
|
||||||
if (e == NULL)
|
if (e == NULL)
|
||||||
return;
|
return;
|
||||||
@@ -375,10 +384,17 @@ wwi_expr(FILE *of, Node *e)
|
|||||||
break;
|
break;
|
||||||
case N_IDENT:
|
case N_IDENT:
|
||||||
case N_TNAME:
|
case N_TNAME:
|
||||||
fputs(e->str, of);
|
wwi_name(c, of, owner, source, e->str);
|
||||||
break;
|
break;
|
||||||
case N_DOT:
|
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);
|
fputc('.', of);
|
||||||
fputs(e->str, of);
|
fputs(e->str, of);
|
||||||
break;
|
break;
|
||||||
@@ -393,20 +409,20 @@ wwi_expr(FILE *of, Node *e)
|
|||||||
case N_RUNELIT: wwi_rune(of, e->uval); break;
|
case N_RUNELIT: wwi_rune(of, e->uval); break;
|
||||||
case N_BIN:
|
case N_BIN:
|
||||||
fputc('(', of);
|
fputc('(', of);
|
||||||
wwi_expr(of, e->lhs);
|
wwi_expr(c, of, owner, source, e->lhs);
|
||||||
fprintf(of, " %s ", tokname(e->op));
|
fprintf(of, " %s ", tokname(e->op));
|
||||||
wwi_expr(of, e->rhs);
|
wwi_expr(c, of, owner, source, e->rhs);
|
||||||
fputc(')', of);
|
fputc(')', of);
|
||||||
break;
|
break;
|
||||||
case N_UN:
|
case N_UN:
|
||||||
fputs(tokname(e->op), of);
|
fputs(tokname(e->op), of);
|
||||||
wwi_expr(of, e->lhs);
|
wwi_expr(c, of, owner, source, e->lhs);
|
||||||
break;
|
break;
|
||||||
case N_CAST:
|
case N_CAST:
|
||||||
fputc('(', of);
|
fputc('(', of);
|
||||||
wwi_expr(of, e->lhs);
|
wwi_expr(c, of, owner, source, e->lhs);
|
||||||
fputs(": ", of);
|
fputs(": ", of);
|
||||||
wwi_type(of, e->rhs);
|
wwi_type(c, of, owner, source, e->rhs);
|
||||||
fputc(')', of);
|
fputc(')', of);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -428,7 +444,7 @@ wwi_attr_relevant(const char *nm)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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) {
|
for (Node *a = d->attr; a; a = a->next) {
|
||||||
if (a->kind != N_ATTR || !wwi_attr_relevant(a->str))
|
if (a->kind != N_ATTR || !wwi_attr_relevant(a->str))
|
||||||
@@ -439,7 +455,7 @@ wwi_attrs(FILE *of, Node *d)
|
|||||||
fputc('(', of);
|
fputc('(', of);
|
||||||
for (Node *arg = a->list; arg; arg = arg->next) {
|
for (Node *arg = a->list; arg; arg = arg->next) {
|
||||||
if (arg != a->list) fputs(", ", of);
|
if (arg != a->list) fputs(", ", of);
|
||||||
wwi_expr(of, arg);
|
wwi_expr(c, of, owner, d->sourceid, arg);
|
||||||
}
|
}
|
||||||
fputc(')', of);
|
fputc(')', of);
|
||||||
}
|
}
|
||||||
@@ -448,34 +464,34 @@ wwi_attrs(FILE *of, Node *d)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
wwi_decl(FILE *of, Node *d)
|
wwi_decl(Checker *c, FILE *of, const char *owner, Node *d)
|
||||||
{
|
{
|
||||||
switch (d->kind) {
|
switch (d->kind) {
|
||||||
case N_FNDECL:
|
case N_FNDECL:
|
||||||
wwi_attrs(of, d);
|
wwi_attrs(c, of, owner, d);
|
||||||
fputs(d->export ? "export fn " : "fn ", of);
|
fputs(d->export ? "export fn " : "fn ", of);
|
||||||
fputs(d->str, of);
|
fputs(d->str, of);
|
||||||
fputc('(', of);
|
fputc('(', of);
|
||||||
for (Node *p = d->list; p; p = p->next) {
|
for (Node *p = d->list; p; p = p->next) {
|
||||||
if (p != d->list) fputs(", ", of);
|
if (p != d->list) fputs(", ", of);
|
||||||
wwi_param(of, p);
|
wwi_param(c, of, owner, d->sourceid, p);
|
||||||
}
|
}
|
||||||
fputs(") ", of);
|
fputs(") ", of);
|
||||||
wwi_type(of, d->lhs);
|
wwi_type(c, of, owner, d->sourceid, d->lhs);
|
||||||
fputs(";\n", of);
|
fputs(";\n", of);
|
||||||
break;
|
break;
|
||||||
case N_TYPEDECL:
|
case N_TYPEDECL:
|
||||||
fputs(d->export ? "export type " : "type ", of);
|
fputs(d->export ? "export type " : "type ", of);
|
||||||
fputs(d->str, of);
|
fputs(d->str, of);
|
||||||
fputs(" = ", of);
|
fputs(" = ", of);
|
||||||
wwi_type(of, d->lhs);
|
wwi_type(c, of, owner, d->sourceid, d->lhs);
|
||||||
fputs(";\n", of);
|
fputs(";\n", of);
|
||||||
break;
|
break;
|
||||||
case N_DEF:
|
case N_DEF:
|
||||||
fputs(d->export ? "export def " : "def ", of);
|
fputs(d->export ? "export def " : "def ", of);
|
||||||
fputs(d->str, of);
|
fputs(d->str, of);
|
||||||
fputs(": ", 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-
|
/* An aggregate initializer (N_STRUCTLIT/N_ARRLIT) is a DATA-
|
||||||
* global (#52): emit a value-LESS prototype `export def X: T;`.
|
* global (#52): emit a value-LESS prototype `export def X: T;`.
|
||||||
* The defining package's own .o emits the struct/array DATA; the
|
* 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);
|
fputs(";\n", of);
|
||||||
} else {
|
} else {
|
||||||
fputs(" = ", of);
|
fputs(" = ", of);
|
||||||
wwi_expr(of, d->rhs);
|
wwi_expr(c, of, owner, d->sourceid, d->rhs);
|
||||||
fputs(";\n", of);
|
fputs(";\n", of);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -502,7 +518,7 @@ wwi_decl(FILE *of, Node *d)
|
|||||||
if (d->lhs == NULL)
|
if (d->lhs == NULL)
|
||||||
fatal("wwi: exported let '%s' has no declared type",
|
fatal("wwi: exported let '%s' has no declared type",
|
||||||
d->str);
|
d->str);
|
||||||
wwi_type(of, d->lhs);
|
wwi_type(c, of, owner, d->sourceid, d->lhs);
|
||||||
fputs(";\n", of);
|
fputs(";\n", of);
|
||||||
break;
|
break;
|
||||||
default:
|
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
|
if (s->kind == SK_DEF && strcmp(s->name, name) == 0
|
||||||
&& wwi_mod_eq(s->mod, owner))
|
&& wwi_mod_eq(s->mod, owner))
|
||||||
return s;
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -812,7 +823,9 @@ wwi_emit_imports(FILE *of, Node *file, const char *owner, int source,
|
|||||||
const char *previous = NULL;
|
const char *previous = NULL;
|
||||||
for (int i = 0; i < nuse; i++) {
|
for (int i = 0; i < nuse; i++) {
|
||||||
if (previous && strcmp(previous, us[i].path) == 0) continue;
|
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;
|
previous = us[i].path;
|
||||||
}
|
}
|
||||||
free(us);
|
free(us);
|
||||||
@@ -865,16 +878,16 @@ wwi_emit_primary_section(Checker *c, FILE *of, Node *file,
|
|||||||
wwi_emit_imports(of, file, owner, source, 0);
|
wwi_emit_imports(of, file, owner, source, 0);
|
||||||
for (int i = 0; i < fs->nprivate; i++)
|
for (int i = 0; i < fs->nprivate; i++)
|
||||||
if (fs->privatefacts[i].d->sourceid == source)
|
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)
|
if (c->is_test_package)
|
||||||
for (Node *d = file->list; d; d = d->next)
|
for (Node *d = file->list; d; d = d->next)
|
||||||
if (wwi_primary(d) && d->sourceid == source
|
if (wwi_primary(d) && d->sourceid == source
|
||||||
&& d->kind == N_FNDECL && !d->export
|
&& d->kind == N_FNDECL && !d->export
|
||||||
&& wwi_has_attr(d, "test"))
|
&& wwi_has_attr(d, "test"))
|
||||||
wwi_decl(of, d);
|
wwi_decl(c, of, NULL, d);
|
||||||
for (int i = 0; i < nexports; i++)
|
for (int i = 0; i < nexports; i++)
|
||||||
if (exports[i].d->sourceid == source)
|
if (exports[i].d->sourceid == source)
|
||||||
wwi_decl(of, exports[i].d);
|
wwi_decl(c, of, NULL, exports[i].d);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
@@ -964,16 +977,15 @@ wwi_emit(Checker *c, FILE *of, Node *file)
|
|||||||
struct factent *f = &fs.facts[i];
|
struct factent *f = &fs.facts[i];
|
||||||
if (lastmod == NULL || strcmp(lastmod, f->mod) != 0
|
if (lastmod == NULL || strcmp(lastmod, f->mod) != 0
|
||||||
|| lastsource != f->d->sourceid) {
|
|| 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, "//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);
|
wwi_emit_imports(of, file, f->mod, f->d->sourceid, 1);
|
||||||
lastmod = f->mod;
|
lastmod = f->mod;
|
||||||
lastsource = f->d->sourceid;
|
lastsource = f->d->sourceid;
|
||||||
}
|
}
|
||||||
wwi_decl(of, f->d);
|
wwi_decl(c, of, f->mod, f->d);
|
||||||
}
|
}
|
||||||
free(fs.privatefacts);
|
free(fs.privatefacts);
|
||||||
free(fs.facts);
|
free(fs.facts);
|
||||||
|
|||||||
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 *decl_mod(Node *file, Node *d);
|
||||||
static const char *use_path(Node *file, const char *curmod, int source,
|
static const char *use_path(Node *file, const char *curmod, int source,
|
||||||
const char *alias);
|
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,
|
static int src_imports(Node *file, const char *modtag, int source,
|
||||||
const char *name);
|
const char *name);
|
||||||
static Sym *lookup_visible(Checker *c, 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`.
|
* Returns NULL if the referencing package has no such `use`.
|
||||||
*/
|
*/
|
||||||
static const char *
|
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;
|
if (file == NULL || alias == NULL) return NULL;
|
||||||
/* Legacy inline multi-package units may spell a package's own
|
/* 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)
|
int same = (um == NULL) ? (curmod == NULL)
|
||||||
: (curmod != NULL && strcmp(um, curmod) == 0);
|
: (curmod != NULL && strcmp(um, curmod) == 0);
|
||||||
if (same) {
|
if (same) {
|
||||||
u->used = 1;
|
if (mark) u->used = 1;
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
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
|
* resolve_typedecl — resolve d's body into its installed TY_NAMED
|
||||||
* placeholder. Reached from check_file's typedecl pass AND on demand
|
* 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;
|
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 *
|
static Sym *
|
||||||
lookup_visible(Checker *c, const char *name)
|
lookup_visible(Checker *c, const char *name)
|
||||||
{
|
{
|
||||||
@@ -3053,24 +3039,16 @@ lookup_visible(Checker *c, const char *name)
|
|||||||
if (s->decl != NULL) return s;
|
if (s->decl != NULL) return s;
|
||||||
builtin = s;
|
builtin = s;
|
||||||
}
|
}
|
||||||
/* N_USE entries are historically coalesced by leaf in the flat scope,
|
/* Flat scope installation may coalesce equal qualifiers from distinct
|
||||||
* so the retained symbol may carry another source package's owner. The
|
* files. The source-owned binding is authoritative, but a bare mention is
|
||||||
* source-owned alias map is authoritative: if this package directly
|
* not usage; only the enclosing qualified lookup may mark it. */
|
||||||
* imports NAME, return the coalesced module marker only as a marker; the
|
if (find_use_path(c->file, c->cur_mod, c->cur_source, name, 0) != NULL) {
|
||||||
* 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) {
|
|
||||||
for (Scope *p = c->cur; p; p = p->parent)
|
for (Scope *p = c->cur; p; p = p->parent)
|
||||||
for (Sym *b = p->first; b; b = b->next)
|
for (Sym *b = p->first; b; b = b->next)
|
||||||
if (strcmp(b->name, name) == 0
|
if (strcmp(b->name, name) == 0
|
||||||
&& (b->kind == SK_USE || b->use_alias))
|
&& (b->kind == SK_USE || b->use_alias))
|
||||||
return b;
|
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;
|
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);
|
Sym *s = scope_lookup_type(c->cur, c->cur_mod, name);
|
||||||
if (s != NULL) return s;
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3162,6 +3132,44 @@ top_decl_kind(Node *d)
|
|||||||
|| d->kind == N_FNDECL || d->kind == N_LET);
|
|| 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
|
static void
|
||||||
check_import_alt(Node *d, const char *name)
|
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) {
|
for (Node *u = file->list; u; u = u->next) {
|
||||||
if (u->kind != N_USE || u->imported || u->used || u->str == NULL)
|
if (u->kind != N_USE || u->imported || u->used || u->str == NULL)
|
||||||
continue;
|
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 *dot = strrchr(path, '.');
|
||||||
const char *leaf = dot ? dot + 1 : path;
|
const char *leaf = dot ? dot + 1 : path;
|
||||||
if (strcmp(u->str, leaf) == 0)
|
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
|
if (u->kind != N_USE || u->imported || u->str == NULL
|
||||||
|| strcmp(d->str, u->str) != 0)
|
|| strcmp(d->str, u->str) != 0)
|
||||||
continue;
|
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,
|
err(c, d->pos,
|
||||||
"%s already declared through import of package %s (\"%s\")",
|
"%s already declared through import of package %s (\"%s\")",
|
||||||
d->str, u->str, path);
|
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
|
/* The synthetic runner belongs to file->sourceid. An import in an
|
||||||
* earlier module-reset section neither supplies nor uses its binding. */
|
* earlier module-reset section neither supplies nor uses its binding. */
|
||||||
for (Node *u = file->list; u; u = u->next) {
|
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->imported && u->sourceid == file->sourceid
|
||||||
&& u->usepath
|
&& 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;
|
u->used = 1;
|
||||||
if (u->kind == N_USE && !u->imported
|
if (u->kind == N_USE && !u->imported
|
||||||
&& u->sourceid == file->sourceid
|
&& u->sourceid == file->sourceid
|
||||||
@@ -3264,15 +3275,18 @@ check_file(Checker *c, Node *file)
|
|||||||
Node *usenode = newnode(c->a, N_USE, file->pos);
|
Node *usenode = newnode(c->a, N_USE, file->pos);
|
||||||
usenode->str = c->test_module;
|
usenode->str = c->test_module;
|
||||||
usenode->strlen = strlen(c->test_module);
|
usenode->strlen = strlen(c->test_module);
|
||||||
|
usenode->usesource = c->test_module;
|
||||||
usenode->usepath = c->test_module;
|
usenode->usepath = c->test_module;
|
||||||
usenode->pkgname = file->pkgname;
|
usenode->pkgname = file->pkgname;
|
||||||
usenode->sourceid = file->sourceid;
|
usenode->sourceid = file->sourceid;
|
||||||
if (c->test_target != NULL) usenode->used = 1;
|
usenode->used = 1;
|
||||||
usenode->next = file->list;
|
usenode->next = file->list;
|
||||||
file->list = usenode;
|
file->list = usenode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mark_import_uses(c, file);
|
||||||
check_import_redeclarations(c, file);
|
check_import_redeclarations(c, file);
|
||||||
|
check_import_usage_and_collisions(c, file);
|
||||||
|
|
||||||
/* pass 1: install names (types first, then defs/fns).
|
/* pass 1: install names (types first, then defs/fns).
|
||||||
* For self-referential types we install the named-type placeholder
|
* 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_mod = NULL;
|
||||||
c->cur_source = 0;
|
c->cur_source = 0;
|
||||||
check_import_usage_and_collisions(c, file);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #6 harec-fidelity (ref/harec/src/check.c:3941): a @test fn is
|
* #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
|
* fully checked above — pass 2 walked its body like every fn — but
|
||||||
|
|||||||
@@ -1315,30 +1315,40 @@ parseblock(Parser *p)
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* `import encoding.utf8;` — the driver resolves the dotted path to a
|
/* The optional alias is source-local; the dotted path remains the dependency
|
||||||
* directory; the checker only needs the leaf (`utf8`) as the module
|
* identity supplied to the package driver. */
|
||||||
* 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). */
|
|
||||||
static Node *
|
static Node *
|
||||||
parseuse(Parser *p)
|
parseuse(Parser *p)
|
||||||
{
|
{
|
||||||
Pos pp = p->cur.pos;
|
Pos pp = p->cur.pos;
|
||||||
expect(p, TK_USE);
|
expect(p, TK_USE);
|
||||||
Node *n = newnode(p->a, N_USE, pp);
|
Node *n = newnode(p->a, N_USE, pp);
|
||||||
/* M1 #22: accumulate the full dotted import path in usepath. `str`
|
const char *alias = NULL;
|
||||||
* starts as its leaf; direct export metadata later installs the imported
|
const char *first;
|
||||||
* declaration's default name without changing canonical identity. */
|
if (p->cur.kind == TK_UNDER) {
|
||||||
const char *leaf = expectident(p);
|
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;
|
const char *path = leaf;
|
||||||
while (accept(p, TK_DOT)) {
|
while (accept(p, TK_DOT)) {
|
||||||
leaf = expectident(p);
|
leaf = expectident(p);
|
||||||
path = aprintf(p->a, "%s.%s", path, leaf);
|
path = aprintf(p->a, "%s.%s", path, leaf);
|
||||||
}
|
}
|
||||||
n->str = leaf;
|
n->str = alias ? alias : leaf;
|
||||||
n->strlen = strlen(leaf);
|
n->strlen = strlen(n->str);
|
||||||
|
n->usesource = path;
|
||||||
n->usepath = path;
|
n->usepath = path;
|
||||||
|
n->usealias = alias;
|
||||||
expect(p, TK_SEMI);
|
expect(p, TK_SEMI);
|
||||||
return n;
|
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
|
* let) carry it. On an N_USE node
|
||||||
* this is the importing (owning)
|
* this is the importing (owning)
|
||||||
* module. */
|
* module. */
|
||||||
const char *usepath; /* M1 #22: on an N_USE node, the full
|
const char *usesource; /* N_USE: immutable dotted source spelling. */
|
||||||
* canonical import path (`encoding.utf8`);
|
const char *usepath; /* N_USE: canonical, vendor-expanded identity;
|
||||||
* `str` is the declared default qualifier. Drives
|
* initially equal to `usesource`. */
|
||||||
* the path-keyed decl_mod match and the
|
const char *usealias; /* N_USE: explicit file-local alias, or NULL. */
|
||||||
* qualified-ref codegen hint. */
|
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
|
const char *pkgname; /* declared package name for this source/export
|
||||||
* section; independent of canonical `module`. */
|
* section; independent of canonical `module`. */
|
||||||
int sourceid; /* lexical source-file scope within the parsed
|
int sourceid; /* lexical source-file scope within the parsed
|
||||||
|
|||||||
@@ -2076,8 +2076,10 @@ use_node_cmp(const void *a, const void *b)
|
|||||||
{
|
{
|
||||||
const Node *x = *(Node *const *)a;
|
const Node *x = *(Node *const *)a;
|
||||||
const Node *y = *(Node *const *)b;
|
const Node *y = *(Node *const *)b;
|
||||||
const char *xp = x->usepath ? x->usepath : x->str;
|
const char *xp = x->usesource ? x->usesource
|
||||||
const char *yp = y->usepath ? y->usepath : y->str;
|
: x->usepath ? x->usepath : x->str;
|
||||||
|
const char *yp = y->usesource ? y->usesource
|
||||||
|
: y->usepath ? y->usepath : y->str;
|
||||||
int r = strcmp(xp, yp);
|
int r = strcmp(xp, yp);
|
||||||
if (r != 0) return r;
|
if (r != 0) return r;
|
||||||
r = strcmp(x->pos.file ? x->pos.file : "",
|
r = strcmp(x->pos.file ? x->pos.file : "",
|
||||||
@@ -2503,7 +2505,8 @@ sep_scan_file(struct sepgraph *g, int pi, const char *file,
|
|||||||
int rc = 0;
|
int rc = 0;
|
||||||
for (int i = 0; i < nuse && rc == 0; i++) {
|
for (int i = 0; i < nuse && rc == 0; i++) {
|
||||||
Node *u = uses[i];
|
Node *u = uses[i];
|
||||||
const char *name = u->usepath ? u->usepath : u->str;
|
const char *name = u->usesource ? u->usesource
|
||||||
|
: u->usepath ? u->usepath : u->str;
|
||||||
if (reserved_import_path(name)) {
|
if (reserved_import_path(name)) {
|
||||||
errorf(u->pos, "package path %s is reserved", name);
|
errorf(u->pos, "package path %s is reserved", name);
|
||||||
rc = -1;
|
rc = -1;
|
||||||
@@ -3663,7 +3666,7 @@ static void
|
|||||||
workdir_stamp_text(char *buf, size_t bufsz, int is_test, int emit_asm)
|
workdir_stamp_text(char *buf, size_t bufsz, int is_test, int emit_asm)
|
||||||
{
|
{
|
||||||
snprintf(buf, bufsz, "ww workdir fmt %d mode %s asm %d\n",
|
snprintf(buf, bufsz, "ww workdir fmt %d mode %s asm %d\n",
|
||||||
is_test ? 13 : 14, is_test ? "test" : "build", emit_asm);
|
is_test ? 14 : 15, is_test ? "test" : "build", emit_asm);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* A stale global builder identity invalidates every committed unit voucher in
|
/* A stale global builder identity invalidates every committed unit voucher in
|
||||||
|
|||||||
@@ -127,8 +127,10 @@ export type node = struct {
|
|||||||
type_: *void, // filled in by checker; type.ww treats it as *tinfo
|
type_: *void, // filled in by checker; type.ww treats it as *tinfo
|
||||||
tsuffix: str, // typed numeric literal suffix ("i32", "u64", ...)
|
tsuffix: str, // typed numeric literal suffix ("i32", "u64", ...)
|
||||||
nmod: str, // originating module from `// MODULE: foo`; "" if none
|
nmod: str, // originating module from `// MODULE: foo`; "" if none
|
||||||
usepath: str, // on N_USE: full canonical import path; `str` becomes
|
usesource: str, // N_USE: immutable dotted source spelling
|
||||||
// the file-local declared default qualifier; "" otherwise
|
usepath: str, // N_USE: canonical vendor-expanded identity
|
||||||
|
usealias: str, // N_USE: explicit file-local alias, or empty
|
||||||
|
usepkgname: str,// N_USE: imported declared package name
|
||||||
pkgname: str, // declared package name; independent of canonical nmod
|
pkgname: str, // declared package name; independent of canonical nmod
|
||||||
sourceid: i32, // lexical source-file scope in an owner/export unit
|
sourceid: i32, // lexical source-file scope in an owner/export unit
|
||||||
used: i32, // N_USE: checker observed this file-local binding
|
used: i32, // N_USE: checker observed this file-local binding
|
||||||
@@ -140,7 +142,7 @@ export fn newnode(k: nkind, file: str, line: i32, col: i32) *node = {
|
|||||||
// fval cast-init: 990's wwdump TK_FLOAT diff requires this file
|
// fval cast-init: 990's wwdump TK_FLOAT diff requires this file
|
||||||
// to tokenise identically through C and ww (lex.ww:382 has the
|
// to tokenise identically through C and ww (lex.ww:382 has the
|
||||||
// same workaround for the cstage %g-formats vs ww-skips divergence).
|
// same workaround for the cstage %g-formats vs ww-skips divergence).
|
||||||
let n: *node = alloc(node{kind=k, file=file, line=line, col=col, op=tkind.TK_NONE, str="", uval=0u64, fval=0: f64, lhs=nil, rhs=nil, cond=nil, body=nil, els=nil, list=nil, next=nil, attr=nil, exported=0, packed=0, type_=nil, tsuffix="", nmod="", usepath="", pkgname="", sourceid=0, used=0, imported=0})!;
|
let n: *node = alloc(node{kind=k, file=file, line=line, col=col, op=tkind.TK_NONE, str="", uval=0u64, fval=0: f64, lhs=nil, rhs=nil, cond=nil, body=nil, els=nil, list=nil, next=nil, attr=nil, exported=0, packed=0, type_=nil, tsuffix="", nmod="", usesource="", usepath="", usealias="", usepkgname="", pkgname="", sourceid=0, used=0, imported=0})!;
|
||||||
return n;
|
return n;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,8 @@ package syntax;
|
|||||||
|
|
||||||
import strings;
|
import strings;
|
||||||
|
|
||||||
// `import encoding.utf8;` — the driver resolves the dotted path to
|
// The optional alias is source-local; the dotted path remains the dependency
|
||||||
// a directory; only the leaf (`utf8`) is needed downstream as the
|
// identity supplied to the package driver.
|
||||||
// module bareword for n_use → decl disambiguation, mirroring Hare's
|
|
||||||
// `use encoding::utf8;` → `utf8::name` (ref/hare/hare/ast/import.ha:7
|
|
||||||
// stores `[]str` but identifier-resolution uses the last component).
|
|
||||||
fn parseuse(p: *parser) *node = {
|
fn parseuse(p: *parser) *node = {
|
||||||
let pf: str = p.curfile;
|
let pf: str = p.curfile;
|
||||||
let pl: i32 = p.curline;
|
let pl: i32 = p.curline;
|
||||||
@@ -16,19 +13,31 @@ fn parseuse(p: *parser) *node = {
|
|||||||
advance(p);
|
advance(p);
|
||||||
let n: *node = newnode(nkind.N_USE, pf, pl, pc);
|
let n: *node = newnode(nkind.N_USE, pf, pl, pc);
|
||||||
n.nmod = p.curmod;
|
n.nmod = p.curmod;
|
||||||
// M1 #22: accumulate the full dotted import path (n.usepath) for the
|
let alias: str;
|
||||||
// checker's path-keyed module match. n.str is the provisional path leaf;
|
let first: str;
|
||||||
// direct export metadata later replaces it with the declared default name.
|
if (p.curkind == tkind.TK_UNDER) {
|
||||||
let leaf: str;
|
errmsg(p, "blank import alias _ is not implemented");
|
||||||
expectident(p, &leaf);
|
alias = "_";
|
||||||
|
advance(p);
|
||||||
|
expectident(p, &first);
|
||||||
|
} else {
|
||||||
|
expectident(p, &first);
|
||||||
|
if (p.curkind == tkind.TK_IDENT) {
|
||||||
|
alias = first;
|
||||||
|
expectident(p, &first);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
let leaf: str = first;
|
||||||
let path: str = leaf;
|
let path: str = leaf;
|
||||||
for (p.curkind == tkind.TK_DOT) {
|
for (p.curkind == tkind.TK_DOT) {
|
||||||
advance(p);
|
advance(p);
|
||||||
expectident(p, &leaf);
|
expectident(p, &leaf);
|
||||||
path = strings.concat(path, ".", leaf);
|
path = strings.concat(path, ".", leaf);
|
||||||
};
|
};
|
||||||
n.str = leaf;
|
if (alias.len > 0) { n.str = alias; } else { n.str = leaf; };
|
||||||
|
n.usesource = path;
|
||||||
n.usepath = path;
|
n.usepath = path;
|
||||||
|
n.usealias = alias;
|
||||||
expecttok(p, tkind.TK_SEMI, "expected ';' after use");
|
expecttok(p, tkind.TK_SEMI, "expected ';' after use");
|
||||||
return n;
|
return n;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -99,26 +99,52 @@ fn allocnodeptrs(count: i32) ([]*syntax.node | nomem) = {
|
|||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Export data carries canonical owner and declared name independently. Each
|
fn canonicalpkgname(path: str, name: str) bool = {
|
||||||
// direct interface's package-clause markers also describe its reachable fact
|
let prefix: str = "__wwi_";
|
||||||
// closure, so search all parsed interfaces for a canonical owner.
|
if (name.len != prefix.len + path.len * 2) { return false; };
|
||||||
fn importpkgname(asts: []*syntax.node, nasts: i32, path: str,
|
let i: i32 = 0;
|
||||||
|
for (i < prefix.len) {
|
||||||
|
if (name[i] != prefix[i]) { return false; };
|
||||||
|
i += 1;
|
||||||
|
};
|
||||||
|
i = 0;
|
||||||
|
for (i < path.len) {
|
||||||
|
let hi: u8 = path[i] >> 4u8;
|
||||||
|
let lo: u8 = path[i] & 15u8;
|
||||||
|
let hib: u8 = hi + 48u8;
|
||||||
|
let lob: u8 = lo + 48u8;
|
||||||
|
if (hi >= 10u8) { hib = hi - 10u8 + 97u8; };
|
||||||
|
if (lo >= 10u8) { lob = lo - 10u8 + 97u8; };
|
||||||
|
if (name[prefix.len + i * 2] != hib
|
||||||
|
|| name[prefix.len + i * 2 + 1] != lob) { return false; };
|
||||||
|
i += 1;
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A paired interface owns PATH's declared name. Compiler-private names keep
|
||||||
|
// transitive fact sections semantic and are ignored when a real name exists.
|
||||||
|
fn importpkgname(asts: []*syntax.node, paths: []*u8, nasts: i32, path: str,
|
||||||
primary: *syntax.node, conflict: *bool) str = {
|
primary: *syntax.node, conflict: *bool) str = {
|
||||||
let name: str;
|
let name: str;
|
||||||
|
let placeholder: str;
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < nasts) {
|
for (i < nasts) {
|
||||||
|
if (!cstreq(paths[i], path)) { i += 1; continue; };
|
||||||
let f: *syntax.node = asts[i];
|
let f: *syntax.node = asts[i];
|
||||||
let p: *syntax.node = nil;
|
let p: *syntax.node = nil;
|
||||||
if (f != nil) { p = f.body; };
|
if (f != nil) { p = f.body; };
|
||||||
for (p != nil) {
|
for (p != nil) {
|
||||||
if (p.nmod.len > 0 && p.pkgname.len > 0
|
if (p.nmod.len > 0 && p.pkgname.len > 0
|
||||||
&& syntax.streq(p.nmod, path)) {
|
&& syntax.streq(p.nmod, path)) {
|
||||||
if (name.len > 0 && !syntax.streq(name, p.pkgname)) {
|
if (canonicalpkgname(path, p.pkgname)) {
|
||||||
|
placeholder = p.pkgname;
|
||||||
|
} else { if (name.len > 0
|
||||||
|
&& !syntax.streq(name, p.pkgname)) {
|
||||||
*conflict = true;
|
*conflict = true;
|
||||||
let empty: str;
|
let empty: str;
|
||||||
return empty;
|
return empty;
|
||||||
};
|
} else { name = p.pkgname; }; };
|
||||||
name = p.pkgname;
|
|
||||||
};
|
};
|
||||||
p = p.next;
|
p = p.next;
|
||||||
};
|
};
|
||||||
@@ -129,19 +155,22 @@ fn importpkgname(asts: []*syntax.node, nasts: i32, path: str,
|
|||||||
for (p != nil) {
|
for (p != nil) {
|
||||||
if (p.nmod.len > 0 && p.pkgname.len > 0
|
if (p.nmod.len > 0 && p.pkgname.len > 0
|
||||||
&& syntax.streq(p.nmod, path)) {
|
&& syntax.streq(p.nmod, path)) {
|
||||||
if (name.len > 0 && !syntax.streq(name, p.pkgname)) {
|
if (canonicalpkgname(path, p.pkgname)) {
|
||||||
|
placeholder = p.pkgname;
|
||||||
|
} else { if (name.len > 0 && !syntax.streq(name, p.pkgname)) {
|
||||||
*conflict = true;
|
*conflict = true;
|
||||||
let empty: str;
|
let empty: str;
|
||||||
return empty;
|
return empty;
|
||||||
};
|
} else { name = p.pkgname; }; };
|
||||||
name = p.pkgname;
|
|
||||||
};
|
};
|
||||||
p = p.next;
|
p = p.next;
|
||||||
};
|
};
|
||||||
|
if (name.len == 0) { name = placeholder; };
|
||||||
return name;
|
return name;
|
||||||
};
|
};
|
||||||
|
|
||||||
fn bindimportnames(list: *syntax.node, asts: []*syntax.node, nasts: i32,
|
fn bindimportnames(list: *syntax.node, asts: []*syntax.node, paths: []*u8,
|
||||||
|
nasts: i32,
|
||||||
primary: *syntax.node, testsupport: *u8) bool = {
|
primary: *syntax.node, testsupport: *u8) bool = {
|
||||||
let u: *syntax.node = list;
|
let u: *syntax.node = list;
|
||||||
for (u != nil) {
|
for (u != nil) {
|
||||||
@@ -151,7 +180,7 @@ fn bindimportnames(list: *syntax.node, asts: []*syntax.node, nasts: i32,
|
|||||||
&& syntax.streq(u.usepath, "__wwtest");
|
&& syntax.streq(u.usepath, "__wwtest");
|
||||||
if (!reserved) {
|
if (!reserved) {
|
||||||
let conflict: bool = false;
|
let conflict: bool = false;
|
||||||
let name: str = importpkgname(asts, nasts, u.usepath,
|
let name: str = importpkgname(asts, paths, nasts, u.usepath,
|
||||||
primary, &conflict);
|
primary, &conflict);
|
||||||
if (conflict) {
|
if (conflict) {
|
||||||
let pre: str = "w6c: package ";
|
let pre: str = "w6c: package ";
|
||||||
@@ -162,7 +191,9 @@ fn bindimportnames(list: *syntax.node, asts: []*syntax.node, nasts: i32,
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
if (name.len > 0) {
|
if (name.len > 0) {
|
||||||
u.str = name;
|
u.usepkgname = name;
|
||||||
|
if (u.usealias.len > 0) { u.str = u.usealias; }
|
||||||
|
else { u.str = name; };
|
||||||
} else { if (u.imported == 0) {
|
} else { if (u.imported == 0) {
|
||||||
let pre: str = "w6c: import ";
|
let pre: str = "w6c: import ";
|
||||||
let post: str = " has no declared package name in direct export data\n";
|
let post: str = " has no declared package name in direct export data\n";
|
||||||
@@ -174,7 +205,8 @@ fn bindimportnames(list: *syntax.node, asts: []*syntax.node, nasts: i32,
|
|||||||
// A closure-only import may have no declarations in this
|
// A closure-only import may have no declarations in this
|
||||||
// interface. Its canonical path must never become a leaf
|
// interface. Its canonical path must never become a leaf
|
||||||
// qualifier by fallback.
|
// qualifier by fallback.
|
||||||
u.str = u.usepath;
|
if (u.usealias.len > 0) { u.str = u.usealias; }
|
||||||
|
else { u.str = u.usepath; };
|
||||||
}; };
|
}; };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -425,8 +457,8 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
let importhead: *node = nil;
|
let importhead: *syntax.node = nil;
|
||||||
let importtail: *node = nil;
|
let importtail: *syntax.node = nil;
|
||||||
importi = 0;
|
importi = 0;
|
||||||
for (importi < nimports) {
|
for (importi < nimports) {
|
||||||
let ibuf: *u8;
|
let ibuf: *u8;
|
||||||
@@ -456,20 +488,20 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
|||||||
os.write(2, nl.ptr, nl.len: u64);
|
os.write(2, nl.ptr, nl.len: u64);
|
||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
let il: lex;
|
let il: syntax.lex;
|
||||||
lexinit(&il, strings.dup(pathstr(importfiles[importi])), ibuf, ilen);
|
syntax.lexinit(&il, strings.dup(pathstr(importfiles[importi])), ibuf, ilen);
|
||||||
let ips: parser;
|
let ips: syntax.parser;
|
||||||
parserinit(&ips, &il);
|
syntax.parserinit(&ips, &il);
|
||||||
let imod: str = pathstr(importpaths[importi]);
|
let imod: str = pathstr(importpaths[importi]);
|
||||||
ips.pathmod = imod;
|
ips.pathmod = imod;
|
||||||
ips.curmod = imod;
|
ips.curmod = imod;
|
||||||
if (testsupport != nil) { ips.testmodule = pathstr(testsupport); };
|
if (testsupport != nil) { ips.testmodule = pathstr(testsupport); };
|
||||||
let imported: *node = parsefile(&ips);
|
let imported: *syntax.node = syntax.parsefile(&ips);
|
||||||
if (il.errs > 0 || ips.errs > 0) { return 1; };
|
if (il.errs > 0 || ips.errs > 0) { return 1; };
|
||||||
importasts[importi] = imported;
|
importasts[importi] = imported;
|
||||||
if (!bindimportnames(imported.list, importasts, importi + 1,
|
if (!bindimportnames(imported.list, importasts, importpaths, importi + 1,
|
||||||
nil, testsupport)) { return 1; };
|
nil, testsupport)) { return 1; };
|
||||||
let d: *node = imported.list;
|
let d: *syntax.node = imported.list;
|
||||||
if (d != nil) {
|
if (d != nil) {
|
||||||
if (importhead == nil) { importhead = d; }
|
if (importhead == nil) { importhead = d; }
|
||||||
else { importtail.next = d; };
|
else { importtail.next = d; };
|
||||||
@@ -487,25 +519,27 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
|||||||
os.write(2, m.ptr, m.len: u64);
|
os.write(2, m.ptr, m.len: u64);
|
||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
let l: lex;
|
let l: syntax.lex;
|
||||||
lexinit(&l, strings.dup(pathstr(src)), buf, blen);
|
syntax.lexinit(&l, strings.dup(pathstr(src)), buf, blen);
|
||||||
let ps: parser;
|
let ps: syntax.parser;
|
||||||
parserinit(&ps, &l);
|
syntax.parserinit(&ps, &l);
|
||||||
if (testsupport != nil) { ps.testmodule = pathstr(testsupport); };
|
if (testsupport != nil) { ps.testmodule = pathstr(testsupport); };
|
||||||
// Entry compilation classifies an ordinary command package. The explicit
|
// Entry compilation classifies an ordinary command package. The explicit
|
||||||
// marker carries only that parser fact for non-entry command test variants.
|
// marker carries only that parser fact for non-entry command test variants.
|
||||||
ps.commandpackage = commandpackage != 0 || entrymode != 0;
|
ps.commandpackage = commandpackage != 0 || entrymode != 0;
|
||||||
let f: *node = parsefile(&ps);
|
let f: *syntax.node = syntax.parsefile(&ps);
|
||||||
// Gate cgen on parse-stage errors. Mirrors cmd/w6c/main.c's
|
// Gate cgen on parse-stage errors. Mirrors cmd/w6c/main.c's
|
||||||
// `if (l.errs || p.errs) return 1;` — broken AST otherwise reaches
|
// `if (l.errs || p.errs) return 1;` — broken AST otherwise reaches
|
||||||
// cgen and emits junk asm with a zero exit (silent miscompile).
|
// cgen and emits junk asm with a zero exit (silent miscompile).
|
||||||
if (l.errs > 0 || ps.errs > 0) { return 1; };
|
if (l.errs > 0 || ps.errs > 0) { return 1; };
|
||||||
let use: *node = f.list;
|
let use: *syntax.node = f.list;
|
||||||
for (use != nil) {
|
for (use != nil) {
|
||||||
if (use.kind == syntax.nkind.N_USE && use.usepath.len != 0) {
|
if (use.kind == syntax.nkind.N_USE && use.usepath.len != 0) {
|
||||||
|
let source: str = use.usesource;
|
||||||
|
if (source.len == 0) { source = use.usepath; };
|
||||||
mapi = 0;
|
mapi = 0;
|
||||||
for (mapi < nmaps) {
|
for (mapi < nmaps) {
|
||||||
if (syntax.streq(use.usepath,
|
if (syntax.streq(source,
|
||||||
pathstr(importmaps[mapi].source))) {
|
pathstr(importmaps[mapi].source))) {
|
||||||
use.usepath = pathstr(importmaps[mapi].path);
|
use.usepath = pathstr(importmaps[mapi].path);
|
||||||
importmaps[mapi].seen = true;
|
importmaps[mapi].seen = true;
|
||||||
@@ -529,11 +563,13 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
|||||||
// by an earlier interface, so bind once more against the complete set.
|
// by an earlier interface, so bind once more against the complete set.
|
||||||
importi = 0;
|
importi = 0;
|
||||||
for (importi < nimports) {
|
for (importi < nimports) {
|
||||||
if (!bindimportnames(importasts[importi].list, importasts, nimports,
|
if (!bindimportnames(importasts[importi].list, importasts, importpaths,
|
||||||
|
nimports,
|
||||||
nil, testsupport)) { return 1; };
|
nil, testsupport)) { return 1; };
|
||||||
importi += 1;
|
importi += 1;
|
||||||
};
|
};
|
||||||
if (!bindimportnames(f.list, importasts, nimports, f, testsupport)) {
|
if (!bindimportnames(f.list, importasts, importpaths, nimports, f,
|
||||||
|
testsupport)) {
|
||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
if (testtarget != nil) {
|
if (testtarget != nil) {
|
||||||
|
|||||||
@@ -586,8 +586,15 @@ fn pushargsrev(c: *cgen, arg: *syntax.node, param: *syntax.node, memphase: bool,
|
|||||||
};
|
};
|
||||||
if (!aistagged) {
|
if (!aistagged) {
|
||||||
widensz = slotsize(c, ptype);
|
widensz = slotsize(c, ptype);
|
||||||
let tagged: *syntax.node = resolvetagged(c, ptype);
|
// The parameter may come from an imported signature whose
|
||||||
let t: i32 = taggedvariantindex(c, tagged, arg);
|
// qualifier is owned by that interface source, not by the
|
||||||
|
// caller currently being emitted. The checker has already
|
||||||
|
// resolved and stamped its canonical type; use that identity
|
||||||
|
// directly instead of re-resolving source-local alias text in
|
||||||
|
// the caller's scope. Mirrors cstage's Type-keyed
|
||||||
|
// cg_tag_for_variant path.
|
||||||
|
let t: i32 = taggedvariantindext(c,
|
||||||
|
ptype.type_: *syntax.tinfo, arg);
|
||||||
// tagged→tagged subset sources synthesize
|
// tagged→tagged subset sources synthesize
|
||||||
// no scalar tag here (the widen store
|
// no scalar tag here (the widen store
|
||||||
// remaps); only a CONCRETE source with no
|
// remaps); only a CONCRETE source with no
|
||||||
|
|||||||
@@ -173,12 +173,11 @@ fn declmod(file: *syntax.node, d: *syntax.node) str = {
|
|||||||
return empty;
|
return empty;
|
||||||
};
|
};
|
||||||
|
|
||||||
// usepath — map a source-file default qualifier (the imported package's
|
// Map a source-file qualifier to canonical identity. Looking up the marker for
|
||||||
// declared name) to the full canonical import path it binds, for
|
// a possible DOT must not itself count as usage; only qualified resolution
|
||||||
// module-qualified resolution and codegen hint (M1 #22). Only an import owned
|
// marks the owning occurrence.
|
||||||
// by the referencing file is visible; a matching
|
fn findusepath(file: *syntax.node, modtag: str, source: i32, alias: str,
|
||||||
// alias carried by a transitive interface is deliberately ignored.
|
mark: bool) str = {
|
||||||
fn usepathfor(file: *syntax.node, modtag: str, source: i32, alias: str) str = {
|
|
||||||
let empty: str;
|
let empty: str;
|
||||||
if (file == nil) { return empty; };
|
if (file == nil) { return empty; };
|
||||||
if (alias.len == 0) { return empty; };
|
if (alias.len == 0) { return empty; };
|
||||||
@@ -198,7 +197,7 @@ fn usepathfor(file: *syntax.node, modtag: str, source: i32, alias: str) str = {
|
|||||||
if (um.len == 0) { same = true; };
|
if (um.len == 0) { same = true; };
|
||||||
} else { if (syntax.streq(um, modtag)) { same = true; }; };
|
} else { if (syntax.streq(um, modtag)) { same = true; }; };
|
||||||
if (same) {
|
if (same) {
|
||||||
u.used = 1;
|
if (mark) { u.used = 1; };
|
||||||
if (u.usepath.len != 0) { return u.usepath; };
|
if (u.usepath.len != 0) { return u.usepath; };
|
||||||
return u.str;
|
return u.str;
|
||||||
};
|
};
|
||||||
@@ -209,6 +208,10 @@ fn usepathfor(file: *syntax.node, modtag: str, source: i32, alias: str) str = {
|
|||||||
return empty;
|
return empty;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn usepathfor(file: *syntax.node, modtag: str, source: i32, alias: str) str = {
|
||||||
|
return findusepath(file, modtag, source, alias, true);
|
||||||
|
};
|
||||||
|
|
||||||
// modkeyfor — the module key for a directly imported alias. Empty means
|
// modkeyfor — the module key for a directly imported alias. Empty means
|
||||||
// the referencing package did not itself declare that import.
|
// the referencing package did not itself declare that import.
|
||||||
fn modkeyfor(c: *checker, alias: str) str = {
|
fn modkeyfor(c: *checker, alias: str) str = {
|
||||||
@@ -250,31 +253,6 @@ fn srcimports(file: *syntax.node, modtag: str, source: i32, name: str) bool = {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Bare imported declarations remain WW source syntax, but only across a
|
|
||||||
// direct edge. The syntax scope helpers resolve lexical locals, builtins and
|
|
||||||
// same-package declarations first; this pass admits a flattened interface
|
|
||||||
// symbol iff the referencing source package itself imported its module path.
|
|
||||||
fn directmodvisible(c: *checker, mod: str) bool = {
|
|
||||||
if (mod.len == 0) { return false; };
|
|
||||||
let u: *syntax.node = c.file.list;
|
|
||||||
for (u != nil) {
|
|
||||||
if (u.kind == syntax.nkind.N_USE && u.sourceid == c.cursource) {
|
|
||||||
let um: str = declmod(c.file, u);
|
|
||||||
let same: bool = false;
|
|
||||||
if (c.curmod.len == 0) { same = um.len == 0; }
|
|
||||||
else { same = syntax.streq(um, c.curmod); };
|
|
||||||
let path: str = u.usepath;
|
|
||||||
if (path.len == 0) { path = u.str; };
|
|
||||||
if (same && syntax.streq(path, mod)) {
|
|
||||||
u.used = 1;
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
u = u.next;
|
|
||||||
};
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
fn lookupvisible(c: *checker, name: str) *syntax.sym = {
|
fn lookupvisible(c: *checker, name: str) *syntax.sym = {
|
||||||
let found: *syntax.sym = syntax.scopelookupprefer(c.cur, c.curmod, name);
|
let found: *syntax.sym = syntax.scopelookupprefer(c.cur, c.curmod, name);
|
||||||
let builtin: *syntax.sym = nil;
|
let builtin: *syntax.sym = nil;
|
||||||
@@ -282,10 +260,10 @@ fn lookupvisible(c: *checker, name: str) *syntax.sym = {
|
|||||||
if (found.decl != nil) { return found; };
|
if (found.decl != nil) { return found; };
|
||||||
builtin = found;
|
builtin = found;
|
||||||
};
|
};
|
||||||
// Flat scope installation coalesces same-leaf N_USE entries. The
|
// Flat scope installation may coalesce equal qualifiers from distinct
|
||||||
// source-owned alias map, not the retained marker's mod field, decides
|
// files. A bare mention only locates the marker; the DOT resolution owns
|
||||||
// whether this package can use the qualifier.
|
// usage marking.
|
||||||
if (usepathfor(c.file, c.curmod, c.cursource, name).len != 0) {
|
if (findusepath(c.file, c.curmod, c.cursource, name, false).len != 0) {
|
||||||
let q: *syntax.scope = c.cur;
|
let q: *syntax.scope = c.cur;
|
||||||
for (q != nil) {
|
for (q != nil) {
|
||||||
let u: *syntax.sym = q.first;
|
let u: *syntax.sym = q.first;
|
||||||
@@ -298,17 +276,6 @@ fn lookupvisible(c: *checker, name: str) *syntax.sym = {
|
|||||||
q = q.parent;
|
q = q.parent;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
let p: *syntax.scope = c.cur;
|
|
||||||
for (p != nil) {
|
|
||||||
let b: *syntax.sym = p.first;
|
|
||||||
for (b != nil) {
|
|
||||||
if (syntax.streq(b.name, name) && directmodvisible(c, b.mod)) {
|
|
||||||
return b;
|
|
||||||
};
|
|
||||||
b = b.snext;
|
|
||||||
};
|
|
||||||
p = p.parent;
|
|
||||||
};
|
|
||||||
return builtin;
|
return builtin;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -323,19 +290,6 @@ fn lookupvisibletype(c: *checker, name: str) *syntax.sym = {
|
|||||||
};
|
};
|
||||||
let found: *syntax.sym = syntax.scopelookuptype(c.cur, c.curmod, name);
|
let found: *syntax.sym = syntax.scopelookuptype(c.cur, c.curmod, name);
|
||||||
if (found != nil) { return found; };
|
if (found != nil) { return found; };
|
||||||
let p: *syntax.scope = c.cur;
|
|
||||||
for (p != nil) {
|
|
||||||
let b: *syntax.sym = p.first;
|
|
||||||
for (b != nil) {
|
|
||||||
if (b.skind == syntax.skind.SK_TYPE
|
|
||||||
&& syntax.streq(b.name, name)
|
|
||||||
&& directmodvisible(c, b.mod)
|
|
||||||
&& (b.decl == nil || b.decl.imported == 0
|
|
||||||
|| b.decl.exported != 0)) { return b; };
|
|
||||||
b = b.snext;
|
|
||||||
};
|
|
||||||
p = p.parent;
|
|
||||||
};
|
|
||||||
return nil;
|
return nil;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -7332,6 +7286,22 @@ fn hascvariadic(params: *syntax.node) bool = {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Normalize a Hare-style variadic parameter before either signature stamping
|
||||||
|
// or parameter installation. Doing this in the signature pass means an inline
|
||||||
|
// aggregate element is walked and validated once, under its declaring package,
|
||||||
|
// rather than being revisited through a newly created slice in Pass 2.
|
||||||
|
fn normalizevariadicparam(p: *syntax.node) void = {
|
||||||
|
if (p == nil || p.kind != syntax.nkind.N_PARAM
|
||||||
|
|| p.op != syntax.tkind.TK_ELLIPSIS || p.lhs == nil
|
||||||
|
|| p.lhs.kind == syntax.nkind.N_TSLICE) { return; };
|
||||||
|
let sl: *syntax.node = syntax.newnode(syntax.nkind.N_TSLICE, "", 0, 0);
|
||||||
|
sl.lhs = p.lhs;
|
||||||
|
// op marks this compiler normalization so typeeqast and the interface
|
||||||
|
// writer can peel exactly this wrapper back to the surface `T...` form.
|
||||||
|
sl.op = syntax.tkind.TK_ELLIPSIS;
|
||||||
|
p.lhs = sl;
|
||||||
|
};
|
||||||
|
|
||||||
// TODO(#11): cstage check.c (post-#32) errors `param '%s' redeclared`
|
// TODO(#11): cstage check.c (post-#32) errors `param '%s' redeclared`
|
||||||
// when two params share a name. The fn body's scope IS fresh here
|
// when two params share a name. The fn body's scope IS fresh here
|
||||||
// (resolvefnbody opens it before calling us), so guarding scopedefine's
|
// (resolvefnbody opens it before calling us), so guarding scopedefine's
|
||||||
@@ -7350,21 +7320,7 @@ fn installparams(c: *checker, params: *syntax.node) void = {
|
|||||||
// `tp->type = type_slice(c->a, pt)` and harec
|
// `tp->type = type_slice(c->a, pt)` and harec
|
||||||
// check_func_type. Surface-fidelity preserved: wwdump
|
// check_func_type. Surface-fidelity preserved: wwdump
|
||||||
// -a runs parser only and never reaches this mutation.
|
// -a runs parser only and never reaches this mutation.
|
||||||
if (p.op == syntax.tkind.TK_ELLIPSIS) {
|
normalizevariadicparam(p);
|
||||||
if (p.lhs != nil && p.lhs.kind != syntax.nkind.N_TSLICE) {
|
|
||||||
let sl: *syntax.node = syntax.newnode(syntax.nkind.N_TSLICE, "", 0, 0);
|
|
||||||
sl.lhs = p.lhs;
|
|
||||||
// op marks the wrapper as THIS normalization, not
|
|
||||||
// surface syntax, so typeeqast can peel exactly it
|
|
||||||
// when comparing against an unnormalized fn TYPE
|
|
||||||
// expr (`let f: fn(args: i64...) void = sum` — the
|
|
||||||
// decl side reads []i64 here, the let side i64).
|
|
||||||
// Param-lhs position never carries a tagged spread
|
|
||||||
// marker, so the op reads stay disjoint.
|
|
||||||
sl.op = syntax.tkind.TK_ELLIPSIS;
|
|
||||||
p.lhs = sl;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
let nm: str = p.str;
|
let nm: str = p.str;
|
||||||
if (nm.len > 0) {
|
if (nm.len > 0) {
|
||||||
checkmoduleshadow(c, nm, "param");
|
checkmoduleshadow(c, nm, "param");
|
||||||
@@ -7390,7 +7346,9 @@ fn resolvefnbody(c: *checker, fnnode: *syntax.node) void = {
|
|||||||
let p: *syntax.node = fnnode.list;
|
let p: *syntax.node = fnnode.list;
|
||||||
for (p != nil) {
|
for (p != nil) {
|
||||||
if (p.kind == syntax.nkind.N_PARAM) {
|
if (p.kind == syntax.nkind.N_PARAM) {
|
||||||
if (p.lhs != nil) { resolvewalk(c, p.lhs); };
|
if (p.lhs != nil && p.lhs.type_ == nil) {
|
||||||
|
resolvewalk(c, p.lhs);
|
||||||
|
};
|
||||||
};
|
};
|
||||||
p = p.next;
|
p = p.next;
|
||||||
};
|
};
|
||||||
@@ -7550,6 +7508,42 @@ fn topdeclkind(d: *syntax.node) bool = {
|
|||||||
|| d.kind == syntax.nkind.N_LET);
|
|| d.kind == syntax.nkind.N_LET);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Record only qualified syntax in each owning source before name resolution.
|
||||||
|
// This preserves source-position import diagnostics and never lets a failed
|
||||||
|
// bare lookup consume an ordinary import.
|
||||||
|
fn markimportusesnode(c: *checker, n: *syntax.node, owner: str,
|
||||||
|
source: i32) void = {
|
||||||
|
if (n == nil) { return; };
|
||||||
|
if (n.kind == syntax.nkind.N_TNAME) {
|
||||||
|
let (head, leaf) = strings.rcut(n.str, ".");
|
||||||
|
if (head.len > 0) {
|
||||||
|
findusepath(c.file, owner, source, head, true);
|
||||||
|
};
|
||||||
|
} else { if (n.kind == syntax.nkind.N_DOT && n.lhs != nil
|
||||||
|
&& n.lhs.kind == syntax.nkind.N_IDENT) {
|
||||||
|
findusepath(c.file, owner, source, n.lhs.str, true);
|
||||||
|
}; };
|
||||||
|
let p: *syntax.node = n.attr;
|
||||||
|
for (p != nil) { markimportusesnode(c, p, owner, source); p = p.next; };
|
||||||
|
markimportusesnode(c, n.lhs, owner, source);
|
||||||
|
markimportusesnode(c, n.rhs, owner, source);
|
||||||
|
markimportusesnode(c, n.cond, owner, source);
|
||||||
|
markimportusesnode(c, n.body, owner, source);
|
||||||
|
markimportusesnode(c, n.els, owner, source);
|
||||||
|
p = n.list;
|
||||||
|
for (p != nil) { markimportusesnode(c, p, owner, source); p = p.next; };
|
||||||
|
};
|
||||||
|
|
||||||
|
fn markimportuses(c: *checker, file: *syntax.node) void = {
|
||||||
|
let d: *syntax.node = file.list;
|
||||||
|
for (d != nil) {
|
||||||
|
if (d.kind != syntax.nkind.N_USE) {
|
||||||
|
markimportusesnode(c, d, declmod(file, d), d.sourceid);
|
||||||
|
};
|
||||||
|
d = d.next;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
fn checkimportredeclarations(c: *checker, file: *syntax.node) void = {
|
fn checkimportredeclarations(c: *checker, file: *syntax.node) void = {
|
||||||
if (c.sepmode == 0) { return; };
|
if (c.sepmode == 0) { return; };
|
||||||
let u: *syntax.node = file.list;
|
let u: *syntax.node = file.list;
|
||||||
@@ -7577,7 +7571,8 @@ fn checkimportusageandcollisions(c: *checker, file: *syntax.node) void = {
|
|||||||
let u: *syntax.node = file.list;
|
let u: *syntax.node = file.list;
|
||||||
for (u != nil) {
|
for (u != nil) {
|
||||||
if (u.kind == syntax.nkind.N_USE && u.imported == 0 && u.used == 0) {
|
if (u.kind == syntax.nkind.N_USE && u.imported == 0 && u.used == 0) {
|
||||||
let path: str = u.usepath;
|
let path: str = u.usesource;
|
||||||
|
if (path.len == 0) { path = u.usepath; };
|
||||||
if (path.len == 0) { path = u.str; };
|
if (path.len == 0) { path = u.str; };
|
||||||
let (prefix, suffix) = strings.rcut(path, ".");
|
let (prefix, suffix) = strings.rcut(path, ".");
|
||||||
let leaf: str = suffix;
|
let leaf: str = suffix;
|
||||||
@@ -7600,7 +7595,8 @@ fn checkimportusageandcollisions(c: *checker, file: *syntax.node) void = {
|
|||||||
for (u != nil) {
|
for (u != nil) {
|
||||||
if (u.kind == syntax.nkind.N_USE && u.imported == 0
|
if (u.kind == syntax.nkind.N_USE && u.imported == 0
|
||||||
&& syntax.streq(d.str, u.str)) {
|
&& syntax.streq(d.str, u.str)) {
|
||||||
let path: str = u.usepath;
|
let path: str = u.usesource;
|
||||||
|
if (path.len == 0) { path = u.usepath; };
|
||||||
if (path.len == 0) { path = u.str; };
|
if (path.len == 0) { path = u.str; };
|
||||||
importdiagprefix(d); cerr(d.str);
|
importdiagprefix(d); cerr(d.str);
|
||||||
cerr(" already declared through import of package ");
|
cerr(" already declared through import of package ");
|
||||||
@@ -7659,10 +7655,11 @@ fn checkfile(c: *checker, file: *syntax.node) void = {
|
|||||||
// earlier module-reset section neither supplies nor uses its binding.
|
// earlier module-reset section neither supplies nor uses its binding.
|
||||||
let su: *syntax.node = file.list;
|
let su: *syntax.node = file.list;
|
||||||
for (su != nil) {
|
for (su != nil) {
|
||||||
if (c.testtarget.len > 0 && su.kind == syntax.nkind.N_USE
|
if (su.kind == syntax.nkind.N_USE
|
||||||
&& su.imported == 0 && su.sourceid == file.sourceid
|
&& su.imported == 0 && su.sourceid == file.sourceid
|
||||||
&& (syntax.streq(su.usepath, c.testtarget)
|
&& (syntax.streq(su.usepath, c.testmodule)
|
||||||
|| syntax.streq(su.usepath, c.testmodule))) {
|
|| (c.testtarget.len > 0
|
||||||
|
&& syntax.streq(su.usepath, c.testtarget)))) {
|
||||||
su.used = 1i32;
|
su.used = 1i32;
|
||||||
};
|
};
|
||||||
if (su.kind == syntax.nkind.N_USE && su.imported == 0
|
if (su.kind == syntax.nkind.N_USE && su.imported == 0
|
||||||
@@ -7675,15 +7672,18 @@ fn checkfile(c: *checker, file: *syntax.node) void = {
|
|||||||
if (!present) {
|
if (!present) {
|
||||||
let usenode: *syntax.node = syntax.newnode(syntax.nkind.N_USE, file.file, file.line, file.col);
|
let usenode: *syntax.node = syntax.newnode(syntax.nkind.N_USE, file.file, file.line, file.col);
|
||||||
usenode.str = c.testmodule;
|
usenode.str = c.testmodule;
|
||||||
|
usenode.usesource = c.testmodule;
|
||||||
usenode.usepath = c.testmodule;
|
usenode.usepath = c.testmodule;
|
||||||
usenode.pkgname = file.pkgname;
|
usenode.pkgname = file.pkgname;
|
||||||
usenode.sourceid = file.sourceid;
|
usenode.sourceid = file.sourceid;
|
||||||
if (c.testtarget.len > 0) { usenode.used = 1i32; };
|
usenode.used = 1i32;
|
||||||
usenode.next = file.list;
|
usenode.next = file.list;
|
||||||
file.list = usenode;
|
file.list = usenode;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
markimportuses(c, file);
|
||||||
checkimportredeclarations(c, file);
|
checkimportredeclarations(c, file);
|
||||||
|
checkimportusageandcollisions(c, file);
|
||||||
|
|
||||||
// Pass 1: install all top-level names.
|
// Pass 1: install all top-level names.
|
||||||
let d: *syntax.node = file.list;
|
let d: *syntax.node = file.list;
|
||||||
@@ -7956,6 +7956,39 @@ fn checkfile(c: *checker, file: *syntax.node) void = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Resolve every function signature in its declaring package before any
|
||||||
|
// body can consume it. Cstage's declaration pass builds all function
|
||||||
|
// types before its body-check pass. Without the same split here, a call
|
||||||
|
// from an earlier package section to a later declaration can first visit
|
||||||
|
// the later function's bare same-package return/parameter names while the
|
||||||
|
// caller's module is current. The historical implicit-import fallback
|
||||||
|
// happened to hide that owner error when the caller imported the callee;
|
||||||
|
// ordinary imports must not. The later body walk is idempotent over these
|
||||||
|
// checker-stamped type nodes. Variadics are normalized before stamping and
|
||||||
|
// Pass 2 skips those same roots, so inline enum/struct validators still run
|
||||||
|
// exactly once.
|
||||||
|
d = file.list;
|
||||||
|
for (d != nil) {
|
||||||
|
if (d.kind == syntax.nkind.N_FNDECL) {
|
||||||
|
c.curmod = declmod(file, d);
|
||||||
|
c.cursource = d.sourceid;
|
||||||
|
if (d.lhs != nil && d.lhs.type_ == nil) {
|
||||||
|
resolvewalk(c, d.lhs);
|
||||||
|
};
|
||||||
|
let fp: *syntax.node = d.list;
|
||||||
|
for (fp != nil) {
|
||||||
|
if (fp.kind == syntax.nkind.N_PARAM) {
|
||||||
|
normalizevariadicparam(fp);
|
||||||
|
if (fp.lhs != nil && fp.lhs.type_ == nil) {
|
||||||
|
resolvewalk(c, fp.lhs);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
fp = fp.next;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
d = d.next;
|
||||||
|
};
|
||||||
|
|
||||||
// Pass 2: walk decl bodies/types and resolve identifiers.
|
// Pass 2: walk decl bodies/types and resolve identifiers.
|
||||||
// Track the per-decl module bareword so bare-leaf lookups inside
|
// Track the per-decl module bareword so bare-leaf lookups inside
|
||||||
// the body prefer same-module entries over alphabetically-earlier
|
// the body prefer same-module entries over alphabetically-earlier
|
||||||
@@ -7984,7 +8017,9 @@ fn checkfile(c: *checker, file: *syntax.node) void = {
|
|||||||
cerr(": error: C-style variadic '...' requires a bodiless declaration\n");
|
cerr(": error: C-style variadic '...' requires a bodiless declaration\n");
|
||||||
c.errs += 1;
|
c.errs += 1;
|
||||||
};
|
};
|
||||||
if (d.lhs != nil) { resolvewalk(c, d.lhs); }; // return type
|
if (d.lhs != nil && d.lhs.type_ == nil) {
|
||||||
|
resolvewalk(c, d.lhs); // return type
|
||||||
|
};
|
||||||
resolvefnbody(c, d);
|
resolvefnbody(c, d);
|
||||||
case syntax.nkind.N_DEF:
|
case syntax.nkind.N_DEF:
|
||||||
// #11: a module-level `def xs: [_]T = arrlit;` must infer
|
// #11: a module-level `def xs: [_]T = arrlit;` must infer
|
||||||
@@ -8058,8 +8093,6 @@ fn checkfile(c: *checker, file: *syntax.node) void = {
|
|||||||
};
|
};
|
||||||
d = d.next;
|
d = d.next;
|
||||||
};
|
};
|
||||||
checkimportusageandcollisions(c, file);
|
|
||||||
|
|
||||||
// Pass 3 (#15, A.6.2.1e): post-checker invariant gate. Walks each
|
// Pass 3 (#15, A.6.2.1e): post-checker invariant gate. Walks each
|
||||||
// decl with its curmod set so asserttyped's gate lookups resolve
|
// decl with its curmod set so asserttyped's gate lookups resolve
|
||||||
// against the same module context exprtype saw during pass 2.
|
// against the same module context exprtype saw during pass 2.
|
||||||
|
|||||||
@@ -104,24 +104,41 @@ fn wwiusepath(c: *checker, owner: str, source: i32, alias: str) str = {
|
|||||||
return empty;
|
return empty;
|
||||||
};
|
};
|
||||||
|
|
||||||
fn wwidirectmodvisible(c: *checker, owner: str, source: i32, mod: str) bool = {
|
// Canonical export spelling depends only on path identity, never on the
|
||||||
if (mod.len == 0) { return false; };
|
// source alias or imported declared package name.
|
||||||
let u: *syntax.node = c.file.list;
|
fn wwicanonicalalias(fd: i32, path: str) void = {
|
||||||
for (u != nil) {
|
wputs(fd, "__wwi_");
|
||||||
if (u.kind == syntax.nkind.N_USE && u.sourceid == source) {
|
let i: i32 = 0;
|
||||||
let same: bool = false;
|
for (i < path.len) {
|
||||||
if (owner.len == 0) {
|
let hi: u8 = path[i] >> 4u8;
|
||||||
same = u.imported == 0;
|
let lo: u8 = path[i] & 15u8;
|
||||||
} else {
|
if (hi < 10u8) { wputb(fd, hi + 48u8); }
|
||||||
same = u.imported != 0 && wwimodeq(u.nmod, owner);
|
else { wputb(fd, hi - 10u8 + 97u8); };
|
||||||
};
|
if (lo < 10u8) { wputb(fd, lo + 48u8); }
|
||||||
let path: str = u.str;
|
else { wputb(fd, lo - 10u8 + 97u8); };
|
||||||
if (u.usepath.len > 0) { path = u.usepath; };
|
i += 1;
|
||||||
if (same && syntax.streq(path, mod)) { return true; };
|
|
||||||
};
|
|
||||||
u = u.next;
|
|
||||||
};
|
};
|
||||||
return false;
|
};
|
||||||
|
|
||||||
|
fn wwiname(c: *checker, fd: i32, owner: str, source: i32, name: str) void = {
|
||||||
|
let dotidx: i32 = -1;
|
||||||
|
let i: i32 = 0;
|
||||||
|
for (i < name.len) { if (name[i] == 46u8) { dotidx = i; }; i += 1; };
|
||||||
|
if (dotidx >= 0) {
|
||||||
|
let head: str;
|
||||||
|
head.ptr = name.ptr;
|
||||||
|
head.len = dotidx;
|
||||||
|
let path: str = wwiusepath(c, owner, source, head);
|
||||||
|
if (path.len > 0) {
|
||||||
|
wwicanonicalalias(fd, path);
|
||||||
|
let tail: str;
|
||||||
|
tail.ptr = name.ptr + (dotidx: u64);
|
||||||
|
tail.len = name.len - dotidx;
|
||||||
|
wputs(fd, tail);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
wputs(fd, name);
|
||||||
};
|
};
|
||||||
|
|
||||||
fn wwitypesym(c: *checker, owner: str, source: i32, nm: str) *syntax.sym = {
|
fn wwitypesym(c: *checker, owner: str, source: i32, nm: str) *syntax.sym = {
|
||||||
@@ -145,22 +162,6 @@ fn wwitypesym(c: *checker, owner: str, source: i32, nm: str) *syntax.sym = {
|
|||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
s = syntax.scopelookuptype(c.cur, owner, nm);
|
s = syntax.scopelookuptype(c.cur, owner, nm);
|
||||||
if (s == nil) {
|
|
||||||
let p: *syntax.scope = c.cur;
|
|
||||||
for (p != nil && s == nil) {
|
|
||||||
let b: *syntax.sym = p.first;
|
|
||||||
for (b != nil) {
|
|
||||||
if (b.skind == syntax.skind.SK_TYPE
|
|
||||||
&& syntax.streq(b.name, nm)
|
|
||||||
&& wwidirectmodvisible(c, owner, source, b.mod)) {
|
|
||||||
s = b;
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
b = b.snext;
|
|
||||||
};
|
|
||||||
p = p.parent;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
if (s == nil) { return nil; };
|
if (s == nil) { return nil; };
|
||||||
if (s.skind != syntax.skind.SK_TYPE) { return nil; };
|
if (s.skind != syntax.skind.SK_TYPE) { return nil; };
|
||||||
@@ -307,15 +308,20 @@ fn wwirune(fd: i32, cp: u64) void = {
|
|||||||
wputb(fd, '\'');
|
wputb(fd, '\'');
|
||||||
};
|
};
|
||||||
|
|
||||||
fn wwiexpr(fd: i32, e: *syntax.node) void = {
|
fn wwiexpr(c: *checker, fd: i32, owner: str, source: i32,
|
||||||
|
e: *syntax.node) void = {
|
||||||
if (e == nil) { return; };
|
if (e == nil) { return; };
|
||||||
if (e.kind == syntax.nkind.N_INTLIT) {
|
if (e.kind == syntax.nkind.N_INTLIT) {
|
||||||
wputs(fd, strconv.u64tos(e.uval, strconv.base.DEC));
|
wputs(fd, strconv.u64tos(e.uval, strconv.base.DEC));
|
||||||
if (e.tsuffix.len > 0) { wputs(fd, e.tsuffix); };
|
if (e.tsuffix.len > 0) { wputs(fd, e.tsuffix); };
|
||||||
} else { if (e.kind == syntax.nkind.N_IDENT || e.kind == syntax.nkind.N_TNAME) {
|
} else { if (e.kind == syntax.nkind.N_IDENT || e.kind == syntax.nkind.N_TNAME) {
|
||||||
wputs(fd, e.str);
|
wwiname(c, fd, owner, source, e.str);
|
||||||
} else { if (e.kind == syntax.nkind.N_DOT) {
|
} else { if (e.kind == syntax.nkind.N_DOT) {
|
||||||
wwiexpr(fd, e.lhs);
|
if (e.lhs != nil && e.lhs.kind == syntax.nkind.N_IDENT) {
|
||||||
|
let path: str = wwiusepath(c, owner, source, e.lhs.str);
|
||||||
|
if (path.len > 0) { wwicanonicalalias(fd, path); }
|
||||||
|
else { wwiexpr(c, fd, owner, source, e.lhs); };
|
||||||
|
} else { wwiexpr(c, fd, owner, source, e.lhs); };
|
||||||
wputb(fd, '.');
|
wputb(fd, '.');
|
||||||
wputs(fd, e.str);
|
wputs(fd, e.str);
|
||||||
} else { if (e.kind == syntax.nkind.N_TRUE) {
|
} else { if (e.kind == syntax.nkind.N_TRUE) {
|
||||||
@@ -334,20 +340,20 @@ fn wwiexpr(fd: i32, e: *syntax.node) void = {
|
|||||||
wwirune(fd, e.uval);
|
wwirune(fd, e.uval);
|
||||||
} else { if (e.kind == syntax.nkind.N_BIN) {
|
} else { if (e.kind == syntax.nkind.N_BIN) {
|
||||||
wputb(fd, '(');
|
wputb(fd, '(');
|
||||||
wwiexpr(fd, e.lhs);
|
wwiexpr(c, fd, owner, source, e.lhs);
|
||||||
wputb(fd, 32u8);
|
wputb(fd, 32u8);
|
||||||
wputs(fd, syntax.tokname(e.op));
|
wputs(fd, syntax.tokname(e.op));
|
||||||
wputb(fd, 32u8);
|
wputb(fd, 32u8);
|
||||||
wwiexpr(fd, e.rhs);
|
wwiexpr(c, fd, owner, source, e.rhs);
|
||||||
wputb(fd, ')');
|
wputb(fd, ')');
|
||||||
} else { if (e.kind == syntax.nkind.N_UN) {
|
} else { if (e.kind == syntax.nkind.N_UN) {
|
||||||
wputs(fd, syntax.tokname(e.op));
|
wputs(fd, syntax.tokname(e.op));
|
||||||
wwiexpr(fd, e.lhs);
|
wwiexpr(c, fd, owner, source, e.lhs);
|
||||||
} else { if (e.kind == syntax.nkind.N_CAST) {
|
} else { if (e.kind == syntax.nkind.N_CAST) {
|
||||||
wputb(fd, '(');
|
wputb(fd, '(');
|
||||||
wwiexpr(fd, e.lhs);
|
wwiexpr(c, fd, owner, source, e.lhs);
|
||||||
wputs(fd, ": ");
|
wputs(fd, ": ");
|
||||||
wwitype(fd, e.rhs);
|
wwitype(c, fd, owner, source, e.rhs);
|
||||||
wputb(fd, ')');
|
wputb(fd, ')');
|
||||||
} else {
|
} else {
|
||||||
wputs(2, "wwi: unhandled const-expr node kind\n");
|
wputs(2, "wwi: unhandled const-expr node kind\n");
|
||||||
@@ -355,7 +361,8 @@ fn wwiexpr(fd: i32, e: *syntax.node) void = {
|
|||||||
};};};};};};};};};};};
|
};};};};};};};};};};};
|
||||||
};
|
};
|
||||||
|
|
||||||
fn wwiparam(fd: i32, p: *syntax.node) void = {
|
fn wwiparam(c: *checker, fd: i32, owner: str, source: i32,
|
||||||
|
p: *syntax.node) void = {
|
||||||
if (syntax.streq(p.str, "...")) { // C-style FFI `...`
|
if (syntax.streq(p.str, "...")) { // C-style FFI `...`
|
||||||
wputs(fd, "...");
|
wputs(fd, "...");
|
||||||
return;
|
return;
|
||||||
@@ -368,52 +375,57 @@ fn wwiparam(fd: i32, p: *syntax.node) void = {
|
|||||||
// in place to `[]T` (lhs becomes N_TSLICE); cstage leaves lhs == T.
|
// in place to `[]T` (lhs becomes N_TSLICE); cstage leaves lhs == T.
|
||||||
// Peel the inserted slice so both stages emit the surface `T...`.
|
// Peel the inserted slice so both stages emit the surface `T...`.
|
||||||
if (p.op == syntax.tkind.TK_ELLIPSIS && p.lhs != nil && p.lhs.kind == syntax.nkind.N_TSLICE) {
|
if (p.op == syntax.tkind.TK_ELLIPSIS && p.lhs != nil && p.lhs.kind == syntax.nkind.N_TSLICE) {
|
||||||
wwitype(fd, p.lhs.lhs);
|
wwitype(c, fd, owner, source, p.lhs.lhs);
|
||||||
} else {
|
} else {
|
||||||
wwitype(fd, p.lhs);
|
wwitype(c, fd, owner, source, p.lhs);
|
||||||
};
|
};
|
||||||
if (p.op == syntax.tkind.TK_ELLIPSIS) { // Hare `T...` variadic
|
if (p.op == syntax.tkind.TK_ELLIPSIS) { // Hare `T...` variadic
|
||||||
wputs(fd, "...");
|
wputs(fd, "...");
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
fn wwitype(fd: i32, t: *syntax.node) void = {
|
fn wwitype(c: *checker, fd: i32, owner: str, source: i32,
|
||||||
|
t: *syntax.node) void = {
|
||||||
if (t == nil) { // absent return type spells void
|
if (t == nil) { // absent return type spells void
|
||||||
wputs(fd, "void");
|
wputs(fd, "void");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
// rule-10: unwrap the wwstage-only N_TPARAM tuple-element wrapper
|
// rule-10: unwrap the wwstage-only N_TPARAM tuple-element wrapper
|
||||||
// (ast.ww:101) so the unparse matches cstage's pristine type-AST.
|
// (ast.ww:101) so the unparse matches cstage's pristine type-AST.
|
||||||
if (t.kind == syntax.nkind.N_TPARAM) { wwitype(fd, t.lhs); return; };
|
if (t.kind == syntax.nkind.N_TPARAM) {
|
||||||
|
wwitype(c, fd, owner, source, t.lhs); return;
|
||||||
|
};
|
||||||
if (t.kind == syntax.nkind.N_TNAME) {
|
if (t.kind == syntax.nkind.N_TNAME) {
|
||||||
if (t.str.len > 0) { wputs(fd, t.str); } else { wputs(fd, "void"); };
|
if (t.str.len > 0) { wwiname(c, fd, owner, source, t.str); }
|
||||||
|
else { wputs(fd, "void"); };
|
||||||
} else { if (t.kind == syntax.nkind.N_TPTR) {
|
} else { if (t.kind == syntax.nkind.N_TPTR) {
|
||||||
wputb(fd, '*');
|
wputb(fd, '*');
|
||||||
wwitype(fd, t.lhs);
|
wwitype(c, fd, owner, source, t.lhs);
|
||||||
} else { if (t.kind == syntax.nkind.N_TSLICE) {
|
} else { if (t.kind == syntax.nkind.N_TSLICE) {
|
||||||
wputs(fd, "[]");
|
wputs(fd, "[]");
|
||||||
wwitype(fd, t.lhs);
|
wwitype(c, fd, owner, source, t.lhs);
|
||||||
} else { if (t.kind == syntax.nkind.N_TARRAY) {
|
} else { if (t.kind == syntax.nkind.N_TARRAY) {
|
||||||
wputb(fd, '[');
|
wputb(fd, '[');
|
||||||
if (t.rhs != nil) { wwiexpr(fd, t.rhs); } else { wputb(fd, '_'); };
|
if (t.rhs != nil) { wwiexpr(c, fd, owner, source, t.rhs); }
|
||||||
|
else { wputb(fd, '_'); };
|
||||||
wputb(fd, ']');
|
wputb(fd, ']');
|
||||||
wwitype(fd, t.lhs);
|
wwitype(c, fd, owner, source, t.lhs);
|
||||||
} else { if (t.kind == syntax.nkind.N_TBANG) {
|
} else { if (t.kind == syntax.nkind.N_TBANG) {
|
||||||
wputb(fd, '!');
|
wputb(fd, '!');
|
||||||
wwitype(fd, t.lhs);
|
wwitype(c, fd, owner, source, t.lhs);
|
||||||
} else { if (t.kind == syntax.nkind.N_TCHAN) {
|
} else { if (t.kind == syntax.nkind.N_TCHAN) {
|
||||||
wputs(fd, "chan ");
|
wputs(fd, "chan ");
|
||||||
wwitype(fd, t.lhs);
|
wwitype(c, fd, owner, source, t.lhs);
|
||||||
} else { if (t.kind == syntax.nkind.N_TFN) {
|
} else { if (t.kind == syntax.nkind.N_TFN) {
|
||||||
wputs(fd, "fn(");
|
wputs(fd, "fn(");
|
||||||
let p: *syntax.node = t.list;
|
let p: *syntax.node = t.list;
|
||||||
for (p != nil) {
|
for (p != nil) {
|
||||||
if (p != t.list) { wputs(fd, ", "); };
|
if (p != t.list) { wputs(fd, ", "); };
|
||||||
wwiparam(fd, p);
|
wwiparam(c, fd, owner, source, p);
|
||||||
p = p.next;
|
p = p.next;
|
||||||
};
|
};
|
||||||
wputs(fd, ") ");
|
wputs(fd, ") ");
|
||||||
wwitype(fd, t.lhs);
|
wwitype(c, fd, owner, source, t.lhs);
|
||||||
} else { if (t.kind == syntax.nkind.N_TSTRUCT) {
|
} else { if (t.kind == syntax.nkind.N_TSTRUCT) {
|
||||||
// re-emit `@packed` so the flag round-trips through
|
// re-emit `@packed` so the flag round-trips through
|
||||||
// sep-compile (harec unparse/type.ha:122-126).
|
// sep-compile (harec unparse/type.ha:122-126).
|
||||||
@@ -429,7 +441,7 @@ fn wwitype(fd: i32, t: *syntax.node) void = {
|
|||||||
wputs(fd, f.str);
|
wputs(fd, f.str);
|
||||||
wputs(fd, ": ");
|
wputs(fd, ": ");
|
||||||
};
|
};
|
||||||
wwitype(fd, f.lhs);
|
wwitype(c, fd, owner, source, f.lhs);
|
||||||
f = f.next;
|
f = f.next;
|
||||||
};
|
};
|
||||||
wputs(fd, " }");
|
wputs(fd, " }");
|
||||||
@@ -438,7 +450,7 @@ fn wwitype(fd: i32, t: *syntax.node) void = {
|
|||||||
let e: *syntax.node = t.list;
|
let e: *syntax.node = t.list;
|
||||||
for (e != nil) {
|
for (e != nil) {
|
||||||
if (e != t.list) { wputs(fd, ", "); };
|
if (e != t.list) { wputs(fd, ", "); };
|
||||||
wwitype(fd, e);
|
wwitype(c, fd, owner, source, e);
|
||||||
e = e.next;
|
e = e.next;
|
||||||
};
|
};
|
||||||
wputb(fd, ')');
|
wputb(fd, ')');
|
||||||
@@ -451,14 +463,14 @@ fn wwitype(fd: i32, t: *syntax.node) void = {
|
|||||||
// the consumer's type-store flattens
|
// the consumer's type-store flattens
|
||||||
// (ref/hare/hare/unparse/type.ha:290-300).
|
// (ref/hare/hare/unparse/type.ha:290-300).
|
||||||
if (e.op == syntax.tkind.TK_ELLIPSIS) { wputs(fd, "..."); };
|
if (e.op == syntax.tkind.TK_ELLIPSIS) { wputs(fd, "..."); };
|
||||||
wwitype(fd, e);
|
wwitype(c, fd, owner, source, e);
|
||||||
e = e.next;
|
e = e.next;
|
||||||
};
|
};
|
||||||
wputb(fd, ')');
|
wputb(fd, ')');
|
||||||
} else { if (t.kind == syntax.nkind.N_TENUM) {
|
} else { if (t.kind == syntax.nkind.N_TENUM) {
|
||||||
wputs(fd, "enum ");
|
wputs(fd, "enum ");
|
||||||
if (t.lhs != nil) {
|
if (t.lhs != nil) {
|
||||||
wwitype(fd, t.lhs);
|
wwitype(c, fd, owner, source, t.lhs);
|
||||||
wputb(fd, 32u8);
|
wputb(fd, 32u8);
|
||||||
};
|
};
|
||||||
wputs(fd, "{ ");
|
wputs(fd, "{ ");
|
||||||
@@ -468,7 +480,7 @@ fn wwitype(fd: i32, t: *syntax.node) void = {
|
|||||||
wputs(fd, m.str);
|
wputs(fd, m.str);
|
||||||
if (m.lhs != nil) {
|
if (m.lhs != nil) {
|
||||||
wputs(fd, " = ");
|
wputs(fd, " = ");
|
||||||
wwiexpr(fd, m.lhs);
|
wwiexpr(c, fd, owner, source, m.lhs);
|
||||||
};
|
};
|
||||||
m = m.next;
|
m = m.next;
|
||||||
};
|
};
|
||||||
@@ -489,7 +501,7 @@ fn wwiattrrelevant(nm: str) bool = {
|
|||||||
return syntax.streq(nm, "symbol") || syntax.streq(nm, "test");
|
return syntax.streq(nm, "symbol") || syntax.streq(nm, "test");
|
||||||
};
|
};
|
||||||
|
|
||||||
fn wwiattrs(fd: i32, d: *syntax.node) void = {
|
fn wwiattrs(c: *checker, fd: i32, owner: str, d: *syntax.node) void = {
|
||||||
let a: *syntax.node = d.attr;
|
let a: *syntax.node = d.attr;
|
||||||
for (a != nil) {
|
for (a != nil) {
|
||||||
if (a.kind == syntax.nkind.N_ATTR && wwiattrrelevant(a.str)) {
|
if (a.kind == syntax.nkind.N_ATTR && wwiattrrelevant(a.str)) {
|
||||||
@@ -500,7 +512,7 @@ fn wwiattrs(fd: i32, d: *syntax.node) void = {
|
|||||||
let arg: *syntax.node = a.list;
|
let arg: *syntax.node = a.list;
|
||||||
for (arg != nil) {
|
for (arg != nil) {
|
||||||
if (arg != a.list) { wputs(fd, ", "); };
|
if (arg != a.list) { wputs(fd, ", "); };
|
||||||
wwiexpr(fd, arg);
|
wwiexpr(c, fd, owner, d.sourceid, arg);
|
||||||
arg = arg.next;
|
arg = arg.next;
|
||||||
};
|
};
|
||||||
wputb(fd, ')');
|
wputb(fd, ')');
|
||||||
@@ -511,9 +523,9 @@ fn wwiattrs(fd: i32, d: *syntax.node) void = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
fn wwidecl(fd: i32, d: *syntax.node) void = {
|
fn wwidecl(c: *checker, fd: i32, owner: str, d: *syntax.node) void = {
|
||||||
if (d.kind == syntax.nkind.N_FNDECL) {
|
if (d.kind == syntax.nkind.N_FNDECL) {
|
||||||
wwiattrs(fd, d);
|
wwiattrs(c, fd, owner, d);
|
||||||
if (d.exported != 0) { wputs(fd, "export fn "); }
|
if (d.exported != 0) { wputs(fd, "export fn "); }
|
||||||
else { wputs(fd, "fn "); };
|
else { wputs(fd, "fn "); };
|
||||||
wputs(fd, d.str);
|
wputs(fd, d.str);
|
||||||
@@ -521,25 +533,25 @@ fn wwidecl(fd: i32, d: *syntax.node) void = {
|
|||||||
let p: *syntax.node = d.list;
|
let p: *syntax.node = d.list;
|
||||||
for (p != nil) {
|
for (p != nil) {
|
||||||
if (p != d.list) { wputs(fd, ", "); };
|
if (p != d.list) { wputs(fd, ", "); };
|
||||||
wwiparam(fd, p);
|
wwiparam(c, fd, owner, d.sourceid, p);
|
||||||
p = p.next;
|
p = p.next;
|
||||||
};
|
};
|
||||||
wputs(fd, ") ");
|
wputs(fd, ") ");
|
||||||
wwitype(fd, d.lhs);
|
wwitype(c, fd, owner, d.sourceid, d.lhs);
|
||||||
wputs(fd, ";\n");
|
wputs(fd, ";\n");
|
||||||
} else { if (d.kind == syntax.nkind.N_TYPEDECL) {
|
} else { if (d.kind == syntax.nkind.N_TYPEDECL) {
|
||||||
if (d.exported != 0) { wputs(fd, "export type "); }
|
if (d.exported != 0) { wputs(fd, "export type "); }
|
||||||
else { wputs(fd, "type "); };
|
else { wputs(fd, "type "); };
|
||||||
wputs(fd, d.str);
|
wputs(fd, d.str);
|
||||||
wputs(fd, " = ");
|
wputs(fd, " = ");
|
||||||
wwitype(fd, d.lhs);
|
wwitype(c, fd, owner, d.sourceid, d.lhs);
|
||||||
wputs(fd, ";\n");
|
wputs(fd, ";\n");
|
||||||
} else { if (d.kind == syntax.nkind.N_DEF) {
|
} else { if (d.kind == syntax.nkind.N_DEF) {
|
||||||
if (d.exported != 0) { wputs(fd, "export def "); }
|
if (d.exported != 0) { wputs(fd, "export def "); }
|
||||||
else { wputs(fd, "def "); };
|
else { wputs(fd, "def "); };
|
||||||
wputs(fd, d.str);
|
wputs(fd, d.str);
|
||||||
wputs(fd, ": ");
|
wputs(fd, ": ");
|
||||||
wwitype(fd, d.lhs);
|
wwitype(c, fd, owner, d.sourceid, d.lhs);
|
||||||
// An aggregate initializer (N_STRUCTLIT/N_ARRLIT) is a DATA-global
|
// An aggregate initializer (N_STRUCTLIT/N_ARRLIT) is a DATA-global
|
||||||
// (#52): emit a value-LESS prototype `export def X: T;`. The
|
// (#52): emit a value-LESS prototype `export def X: T;`. The
|
||||||
// defining package's own .o emits the struct/array DATA; the
|
// defining package's own .o emits the struct/array DATA; the
|
||||||
@@ -554,7 +566,7 @@ fn wwidecl(fd: i32, d: *syntax.node) void = {
|
|||||||
wputs(fd, ";\n");
|
wputs(fd, ";\n");
|
||||||
} else {
|
} else {
|
||||||
wputs(fd, " = ");
|
wputs(fd, " = ");
|
||||||
wwiexpr(fd, d.rhs);
|
wwiexpr(c, fd, owner, d.sourceid, d.rhs);
|
||||||
wputs(fd, ";\n");
|
wputs(fd, ";\n");
|
||||||
};
|
};
|
||||||
} else { if (d.kind == syntax.nkind.N_LET) {
|
} else { if (d.kind == syntax.nkind.N_LET) {
|
||||||
@@ -566,7 +578,7 @@ fn wwidecl(fd: i32, d: *syntax.node) void = {
|
|||||||
wputs(2, "wwi: exported let has no declared type\n");
|
wputs(2, "wwi: exported let has no declared type\n");
|
||||||
os.exit(1);
|
os.exit(1);
|
||||||
};
|
};
|
||||||
wwitype(fd, d.lhs);
|
wwitype(c, fd, owner, d.sourceid, d.lhs);
|
||||||
wputs(fd, ";\n");
|
wputs(fd, ";\n");
|
||||||
};};};};
|
};};};};
|
||||||
};
|
};
|
||||||
@@ -671,16 +683,6 @@ fn wwifactvaluesym(c: *checker, owner: str, source: i32,
|
|||||||
};
|
};
|
||||||
p = p.parent;
|
p = p.parent;
|
||||||
};
|
};
|
||||||
p = c.top;
|
|
||||||
for (p != nil) {
|
|
||||||
let s: *syntax.sym = p.first;
|
|
||||||
for (s != nil) {
|
|
||||||
if (s.skind == syntax.skind.SK_DEF && syntax.streq(s.name, name)
|
|
||||||
&& wwidirectmodvisible(c, owner, source, s.mod)) { return s; };
|
|
||||||
s = s.snext;
|
|
||||||
};
|
|
||||||
p = p.parent;
|
|
||||||
};
|
|
||||||
return nil;
|
return nil;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -929,7 +931,8 @@ fn wwiemitimports(fd: i32, file: *syntax.node, owner: str, source: i32,
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < nuse) {
|
for (i < nuse) {
|
||||||
if (previous.len == 0 || !syntax.streq(previous, paths[i])) {
|
if (previous.len == 0 || !syntax.streq(previous, paths[i])) {
|
||||||
wputs(fd, "import "); wputs(fd, paths[i]); wputs(fd, ";\n");
|
wputs(fd, "import "); wwicanonicalalias(fd, paths[i]);
|
||||||
|
wputb(fd, 32u8); wputs(fd, paths[i]); wputs(fd, ";\n");
|
||||||
previous = paths[i];
|
previous = paths[i];
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -995,7 +998,8 @@ fn wwiemitprimarysection(c: *checker, fd: i32, file: *syntax.node,
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < fs.nprivate) {
|
for (i < fs.nprivate) {
|
||||||
if (fs.privatenodes[i].sourceid == source) {
|
if (fs.privatenodes[i].sourceid == source) {
|
||||||
wwidecl(fd, fs.privatenodes[i]);
|
let primaryowner: str;
|
||||||
|
wwidecl(c, fd, primaryowner, fs.privatenodes[i]);
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
};
|
};
|
||||||
@@ -1004,13 +1008,19 @@ fn wwiemitprimarysection(c: *checker, fd: i32, file: *syntax.node,
|
|||||||
for (d != nil) {
|
for (d != nil) {
|
||||||
if (wwiprimary(d) && d.sourceid == source
|
if (wwiprimary(d) && d.sourceid == source
|
||||||
&& d.kind == syntax.nkind.N_FNDECL && d.exported == 0
|
&& d.kind == syntax.nkind.N_FNDECL && d.exported == 0
|
||||||
&& wwihasattr(d, "test")) { wwidecl(fd, d); };
|
&& wwihasattr(d, "test")) {
|
||||||
|
let primaryowner: str;
|
||||||
|
wwidecl(c, fd, primaryowner, d);
|
||||||
|
};
|
||||||
d = d.next;
|
d = d.next;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
i = 0;
|
i = 0;
|
||||||
for (i < nexports) {
|
for (i < nexports) {
|
||||||
if (exports[i].sourceid == source) { wwidecl(fd, exports[i]); };
|
if (exports[i].sourceid == source) {
|
||||||
|
let primaryowner: str;
|
||||||
|
wwidecl(c, fd, primaryowner, exports[i]);
|
||||||
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -1134,23 +1144,13 @@ fn wwiemit(c: *checker, file: *syntax.node, path: str) i32 = {
|
|||||||
if (lastmod.len == 0 || !syntax.streq(lastmod, mod)
|
if (lastmod.len == 0 || !syntax.streq(lastmod, mod)
|
||||||
|| lastsource != source) {
|
|| lastsource != source) {
|
||||||
wputs(fd, "//ww:module "); wputs(fd, mod); wputs(fd, "\n");
|
wputs(fd, "//ww:module "); wputs(fd, mod); wputs(fd, "\n");
|
||||||
let factpkg: str = fs.factnodes[fi].pkgname;
|
wputs(fd, "package "); wwicanonicalalias(fd, mod);
|
||||||
if (factpkg.len == 0) {
|
wputs(fd, ";\n");
|
||||||
let dotidx: i32 = -1;
|
|
||||||
let mi: i32 = 0;
|
|
||||||
for (mi < mod.len) { if (mod[mi] == 46u8) { dotidx = mi; }; mi += 1; };
|
|
||||||
factpkg = mod;
|
|
||||||
if (dotidx >= 0) {
|
|
||||||
factpkg.ptr = mod.ptr + ((dotidx + 1): u64);
|
|
||||||
factpkg.len = mod.len - dotidx - 1;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
wputs(fd, "package "); wputs(fd, factpkg); wputs(fd, ";\n");
|
|
||||||
wwiemitimports(fd, file, mod, source, true);
|
wwiemitimports(fd, file, mod, source, true);
|
||||||
lastmod = mod;
|
lastmod = mod;
|
||||||
lastsource = source;
|
lastsource = source;
|
||||||
};
|
};
|
||||||
wwidecl(fd, fs.factnodes[fi]);
|
wwidecl(c, fd, mod, fs.factnodes[fi]);
|
||||||
fi += 1;
|
fi += 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2853,7 +2853,11 @@ fn sepresolvesourceimport(g: *sepgraph, context: i32, name: *u8,
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn sepusecmp(a: *syntax.node, b: *syntax.node) i32 = {
|
fn sepusecmp(a: *syntax.node, b: *syntax.node) i32 = {
|
||||||
let r: i32 = strings.compare(a.usepath, b.usepath): i32;
|
let apath: str = a.usesource;
|
||||||
|
if (apath.len == 0) { apath = a.usepath; };
|
||||||
|
let bpath: str = b.usesource;
|
||||||
|
if (bpath.len == 0) { bpath = b.usepath; };
|
||||||
|
let r: i32 = strings.compare(apath, bpath): i32;
|
||||||
if (r != 0) { return r; };
|
if (r != 0) { return r; };
|
||||||
r = strings.compare(a.file, b.file): i32;
|
r = strings.compare(a.file, b.file): i32;
|
||||||
if (r != 0) { return r; };
|
if (r != 0) { return r; };
|
||||||
@@ -2977,12 +2981,14 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, context: i32,
|
|||||||
ui = 0;
|
ui = 0;
|
||||||
for (ui < nuse) {
|
for (ui < nuse) {
|
||||||
u = uses[ui];
|
u = uses[ui];
|
||||||
let idp: *u8 = u.usepath.ptr;
|
let sourcepath: str = u.usesource;
|
||||||
let idn: u64 = u.usepath.len: u64;
|
if (sourcepath.len == 0) { sourcepath = u.usepath; };
|
||||||
if (reservedimport(u.usepath)) {
|
let idp: *u8 = sourcepath.ptr;
|
||||||
|
let idn: u64 = sourcepath.len: u64;
|
||||||
|
if (reservedimport(sourcepath)) {
|
||||||
cerrpos(u.file, u.line, u.col);
|
cerrpos(u.file, u.line, u.col);
|
||||||
cerr(": error: package path ");
|
cerr(": error: package path ");
|
||||||
cerr(u.usepath); cerr(" is reserved\n");
|
cerr(sourcepath); cerr(" is reserved\n");
|
||||||
return -1;
|
return -1;
|
||||||
};
|
};
|
||||||
let nm: []u8;
|
let nm: []u8;
|
||||||
@@ -3085,7 +3091,7 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, context: i32,
|
|||||||
let childcontext: i32 = sepchildcontextfor(g, context,
|
let childcontext: i32 = sepchildcontextfor(g, context,
|
||||||
resolved.entry, resolved.sourceroot);
|
resolved.entry, resolved.sourceroot);
|
||||||
if (childcontext < 0
|
if (childcontext < 0
|
||||||
|| !sepbindadd(bindings, 'D': u8, u.usepath, di,
|
|| !sepbindadd(bindings, 'D': u8, sourcepath, di,
|
||||||
u.file, u.line, u.col)
|
u.file, u.line, u.col)
|
||||||
|| !sepadddep(g, pi, di)
|
|| !sepadddep(g, pi, di)
|
||||||
|| !sepchildrenadd(children, di, childcontext)) {
|
|| !sepchildrenadd(children, di, childcontext)) {
|
||||||
@@ -3108,7 +3114,7 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, context: i32,
|
|||||||
cerr("\n");
|
cerr("\n");
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
if (!sepbindadd(bindings, 'I': u8, u.usepath, -1,
|
if (!sepbindadd(bindings, 'I': u8, sourcepath, -1,
|
||||||
u.file, u.line, u.col)) {
|
u.file, u.line, u.col)) {
|
||||||
return -1;
|
return -1;
|
||||||
};
|
};
|
||||||
@@ -4360,14 +4366,14 @@ fn validatecommandoutputpath(out: *u8) i32 = {
|
|||||||
fn workdirstamptext(istest: i32, emitasm: i32) str = {
|
fn workdirstamptext(istest: i32, emitasm: i32) str = {
|
||||||
if (istest != 0) {
|
if (istest != 0) {
|
||||||
if (emitasm != 0) {
|
if (emitasm != 0) {
|
||||||
return "ww workdir fmt 13 mode test asm 1\n";
|
return "ww workdir fmt 14 mode test asm 1\n";
|
||||||
};
|
};
|
||||||
return "ww workdir fmt 13 mode test asm 0\n";
|
return "ww workdir fmt 14 mode test asm 0\n";
|
||||||
};
|
};
|
||||||
if (emitasm != 0) {
|
if (emitasm != 0) {
|
||||||
return "ww workdir fmt 14 mode build asm 1\n";
|
return "ww workdir fmt 15 mode build asm 1\n";
|
||||||
};
|
};
|
||||||
return "ww workdir fmt 14 mode build asm 0\n";
|
return "ww workdir fmt 15 mode build asm 0\n";
|
||||||
};
|
};
|
||||||
|
|
||||||
fn stampmatches(path: *u8, want: str) bool = {
|
fn stampmatches(path: *u8, want: str) bool = {
|
||||||
|
|||||||
@@ -97,27 +97,27 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
|||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
let l: lex;
|
let l: syntax.lex;
|
||||||
lexinit(&l, argstr(path), buf.ptr, sz: u64);
|
syntax.lexinit(&l, argstr(path), buf.ptr, sz: u64);
|
||||||
|
|
||||||
if (mode == 116) { // '-t'
|
if (mode == 116) { // '-t'
|
||||||
for (true) {
|
for (true) {
|
||||||
let t: tok;
|
let t: syntax.tok;
|
||||||
lexnext(&l, &t);
|
syntax.lexnext(&l, &t);
|
||||||
tokprint(1i32, &t);
|
syntax.tokprint(1i32, &t);
|
||||||
if (t.kind == tkind.TK_EOF) { break; };
|
if (t.kind == syntax.tkind.TK_EOF) { break; };
|
||||||
if (t.kind == tkind.TK_ERR) { break; };
|
if (t.kind == syntax.tkind.TK_ERR) { break; };
|
||||||
};
|
};
|
||||||
} else { if (mode == 97) { // '-a'
|
} else { if (mode == 97) { // '-a'
|
||||||
let ps: parser;
|
let ps: syntax.parser;
|
||||||
parserinit(&ps, &l);
|
syntax.parserinit(&ps, &l);
|
||||||
let f: *node = parsefile(&ps);
|
let f: *syntax.node = syntax.parsefile(&ps);
|
||||||
astprint(1i32, f);
|
syntax.astprint(1i32, f);
|
||||||
if (ps.errs > 0) { return 1; };
|
if (ps.errs > 0) { return 1; };
|
||||||
} else { if (mode == 114) { // '-r' — name resolve report
|
} else { if (mode == 114) { // '-r' — name resolve report
|
||||||
let ps: parser;
|
let ps: syntax.parser;
|
||||||
parserinit(&ps, &l);
|
syntax.parserinit(&ps, &l);
|
||||||
let f: *node = parsefile(&ps);
|
let f: *syntax.node = syntax.parsefile(&ps);
|
||||||
// #52: gate the resolve report on parse-stage errors. Without
|
// #52: gate the resolve report on parse-stage errors. Without
|
||||||
// this a parse-errored decl is silently dropped from the AST
|
// this a parse-errored decl is silently dropped from the AST
|
||||||
// and the report is printed with rc=0. Mirrors w6c main.ww:162
|
// and the report is printed with rc=0. Mirrors w6c main.ww:162
|
||||||
@@ -138,9 +138,9 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
|||||||
os.write(1, " resolved\n".ptr, 10u64);
|
os.write(1, " resolved\n".ptr, 10u64);
|
||||||
if (checkrc != 0) { return 1; };
|
if (checkrc != 0) { return 1; };
|
||||||
} else { if (mode == 99) { // '-c' — codegen / emit asm
|
} else { if (mode == 99) { // '-c' — codegen / emit asm
|
||||||
let ps: parser;
|
let ps: syntax.parser;
|
||||||
parserinit(&ps, &l);
|
syntax.parserinit(&ps, &l);
|
||||||
let f: *node = parsefile(&ps);
|
let f: *syntax.node = syntax.parsefile(&ps);
|
||||||
// #52: gate cgen on parse-stage errors BEFORE check/cgen.
|
// #52: gate cgen on parse-stage errors BEFORE check/cgen.
|
||||||
// A parse-errored decl is silently dropped from the AST; the
|
// A parse-errored decl is silently dropped from the AST; the
|
||||||
// remaining file would otherwise emit asm with rc=0 (silent
|
// remaining file would otherwise emit asm with rc=0 (silent
|
||||||
|
|||||||
Reference in New Issue
Block a user