diff --git a/cmd/w6c/main.c b/cmd/w6c/main.c index d659983e..c549d31d 100644 --- a/cmd/w6c/main.c +++ b/cmd/w6c/main.c @@ -67,38 +67,63 @@ parseinput(Arena *a, const char *file, char *buf, u64 len, return f; } -/* Export data carries canonical owner and declared package name separately. - * Search every direct interface's package-clause markers because its - * self-contained fact closure can also name a transitive owner. */ +static int +canonical_pkgname(const char *path, const char *name) +{ + static const char hex[] = "0123456789abcdef"; + const char *prefix = "__wwi_"; + for (int i = 0; prefix[i]; i++) + if (*name++ != prefix[i]) return 0; + for (const unsigned char *p = (const unsigned char *)path; *p; p++) { + if (*name++ != hex[*p >> 4] || *name++ != hex[*p & 15]) + return 0; + } + return *name == '\0'; +} + +static void +consider_pkgname(const char *path, const char *candidate, + const char **name, const char **placeholder, int *conflict) +{ + if (canonical_pkgname(path, candidate)) { + *placeholder = candidate; + return; + } + if (*name != NULL && strcmp(*name, candidate) != 0) { + *conflict = 1; + return; + } + *name = candidate; +} + +/* A paired interface owns PATH's declared name. Compiler-private names keep + * transitive fact sections semantic and are ignored when a real name exists. */ static const char * import_pkgname(struct importin *imports, int nimports, const char *path, Node *primary, int *conflict) { const char *name = NULL; + const char *placeholder = NULL; for (int i = 0; i < nimports; i++) { + if (strcmp(imports[i].path, path) != 0) continue; Node *file = imports[i].ast; for (Node *p = file ? file->body : NULL; p; p = p->next) { if (p->module == NULL || p->pkgname == NULL || strcmp(p->module, path) != 0) continue; - if (name != NULL && strcmp(name, p->pkgname) != 0) { - *conflict = 1; - return NULL; - } - name = p->pkgname; + consider_pkgname(path, p->pkgname, &name, &placeholder, + conflict); + if (*conflict) return NULL; } } for (Node *p = primary ? primary->body : NULL; p; p = p->next) { if (p->module == NULL || p->pkgname == NULL || strcmp(p->module, path) != 0) continue; - if (name != NULL && strcmp(name, p->pkgname) != 0) { - *conflict = 1; - return NULL; - } - name = p->pkgname; + consider_pkgname(path, p->pkgname, &name, &placeholder, conflict); + if (*conflict) return NULL; } - return name; + return name ? name : placeholder; } static int @@ -122,8 +147,9 @@ bind_import_names(Node *list, struct importin *imports, int nimports, return -1; } if (name != NULL) { - u->str = name; - u->strlen = strlen(name); + u->usepkgname = name; + u->str = u->usealias ? u->usealias : name; + u->strlen = strlen(u->str); } else if (!u->imported) { fprintf(stderr, "w6c: import %s has no declared package name in direct export data\n", @@ -133,8 +159,8 @@ bind_import_names(Node *list, struct importin *imports, int nimports, /* A closure-only import need not contribute declarations to this * interface. Keep it canonical-path keyed without reinstalling * the historical path-leaf qualifier. */ - u->str = u->usepath; - u->strlen = strlen(u->usepath); + u->str = u->usealias ? u->usealias : u->usepath; + u->strlen = strlen(u->str); } } return 0; @@ -348,13 +374,13 @@ main(int argc, char **argv) Node *file = parseinput(a, src, buf, len, NULL, testsupport, commandpackage || entrymode, &bad); if (bad) return 1; - /* Source keeps its effective spelling and position, while package - * resolution supplies the expanded canonical owner. Rewrite only the - * primary import key before imported interface nodes are prepended. */ + /* Vendor expansion changes only canonical identity. Source spelling and + * an optional file-local alias remain independent facts. */ for (Node *u = file->list; u; u = u->next) { if (u->kind != N_USE || u->usepath == NULL) continue; + const char *source = u->usesource ? u->usesource : u->usepath; for (int i = 0; i < nmaps; i++) - if (strcmp(u->usepath, maps[i].source) == 0) { + if (strcmp(source, maps[i].source) == 0) { u->usepath = maps[i].path; maps[i].seen = 1; break; diff --git a/cmd/w6c/wwi.c b/cmd/w6c/wwi.c index 48921970..4db11dcd 100644 --- a/cmd/w6c/wwi.c +++ b/cmd/w6c/wwi.c @@ -68,19 +68,41 @@ wwi_use_path(Checker *c, const char *owner, int source, const char *alias) return NULL; } -static int -wwi_direct_mod_visible(Checker *c, const char *owner, int source, - const char *mod) +/* Export spelling is derived only from canonical identity. The bytewise hex + * encoding is injective, source-identifier-safe, and independent of a local + * alias or the dependency's declared package name. */ +static void +wwi_canonical_alias(FILE *of, const char *path) { - if (mod == NULL || mod[0] == '\0') return 0; - for (Node *u = c->file->list; u; u = u->next) { - if (u->kind != N_USE || u->sourceid != source) continue; - int same = owner == NULL ? u->imported == 0 - : u->imported != 0 && wwi_mod_eq(u->module, owner); - const char *path = u->usepath ? u->usepath : u->str; - if (same && path != NULL && strcmp(path, mod) == 0) return 1; + static const char hex[] = "0123456789abcdef"; + fputs("__wwi_", of); + for (const unsigned char *p = (const unsigned char *)path; *p; p++) { + fputc(hex[*p >> 4], of); + fputc(hex[*p & 15], of); } - return 0; +} + +static void +wwi_name(Checker *c, FILE *of, const char *owner, int source, + const char *name) +{ + if (name == NULL) return; + const char *dot = strrchr(name, '.'); + if (dot != NULL) { + size_t n = (size_t)(dot - name); + char *alias = malloc(n + 1); + if (alias == NULL) fatal("wwi: out of memory"); + memcpy(alias, name, n); + alias[n] = '\0'; + const char *path = wwi_use_path(c, owner, source, alias); + free(alias); + if (path != NULL) { + wwi_canonical_alias(of, path); + fputs(dot, of); + return; + } + } + fputs(name, of); } static Sym * @@ -101,20 +123,6 @@ wwi_typesym(Checker *c, const char *owner, int source, const char *nm) free(alias); } else { s = scope_lookup_type(c->cur, owner, nm); - if (s == NULL) { - for (Scope *p = c->cur; p; p = p->parent) { - for (Sym *b = p->first; b; b = b->next) { - if (b->kind == SK_TYPE - && strcmp(b->name, nm) == 0 - && wwi_direct_mod_visible(c, owner, source, - b->mod)) { - s = b; - break; - } - } - if (s != NULL) break; - } - } } if (s && s->kind == SK_TYPE) return s; @@ -197,8 +205,8 @@ wwi_check_decl(Checker *c, Node *d) /* type-expr unparse per rob §2.2; const-expr per rob §2.4. */ -static void wwi_expr(FILE *of, Node *e); -static void wwi_type(FILE *of, Node *t); +static void wwi_expr(Checker*, FILE*, const char*, int, Node*); +static void wwi_type(Checker*, FILE*, const char*, int, Node*); static void wwi_quote(FILE *of, const char *s, u64 n) @@ -248,7 +256,7 @@ wwi_rune(FILE *of, u64 cp) } static void -wwi_param(FILE *of, Node *p) +wwi_param(Checker *c, FILE *of, const char *owner, int source, Node *p) { if (p->str && strcmp(p->str, "...") == 0) { /* C-style FFI `...` */ fputs("...", of); @@ -258,13 +266,13 @@ wwi_param(FILE *of, Node *p) fputs(p->str, of); fputs(": ", of); } - wwi_type(of, p->lhs); + wwi_type(c, of, owner, source, p->lhs); if (p->op == TK_ELLIPSIS) /* Hare `T...` variadic */ fputs("...", of); } static void -wwi_type(FILE *of, Node *t) +wwi_type(Checker *c, FILE *of, const char *owner, int source, Node *t) { if (t == NULL) { /* absent return type spells void */ fputs("void", of); @@ -272,39 +280,40 @@ wwi_type(FILE *of, Node *t) } switch (t->kind) { case N_TNAME: - fputs(t->str ? t->str : "void", of); + if (t->str) wwi_name(c, of, owner, source, t->str); + else fputs("void", of); break; case N_TPTR: fputc('*', of); - wwi_type(of, t->lhs); + wwi_type(c, of, owner, source, t->lhs); break; case N_TSLICE: fputs("[]", of); - wwi_type(of, t->lhs); + wwi_type(c, of, owner, source, t->lhs); break; case N_TARRAY: fputc('[', of); - if (t->rhs) wwi_expr(of, t->rhs); + if (t->rhs) wwi_expr(c, of, owner, source, t->rhs); else fputc('_', of); fputc(']', of); - wwi_type(of, t->lhs); + wwi_type(c, of, owner, source, t->lhs); break; case N_TBANG: fputc('!', of); - wwi_type(of, t->lhs); + wwi_type(c, of, owner, source, t->lhs); break; case N_TCHAN: fputs("chan ", of); - wwi_type(of, t->lhs); + wwi_type(c, of, owner, source, t->lhs); break; case N_TFN: fputs("fn(", of); for (Node *p = t->list; p; p = p->next) { if (p != t->list) fputs(", ", of); - wwi_param(of, p); + wwi_param(c, of, owner, source, p); } fputs(") ", of); - wwi_type(of, t->lhs); + wwi_type(c, of, owner, source, t->lhs); break; case N_TSTRUCT: /* re-emit the `@packed` attr so the flag round-trips @@ -316,7 +325,7 @@ wwi_type(FILE *of, Node *t) fputs(f->str, of); fputs(": ", of); } - wwi_type(of, f->lhs); + wwi_type(c, of, owner, source, f->lhs); } fputs(" }", of); break; @@ -324,7 +333,7 @@ wwi_type(FILE *of, Node *t) fputc('(', of); for (Node *e = t->list; e; e = e->next) { if (e != t->list) fputs(", ", of); - wwi_type(of, e); + wwi_type(c, of, owner, source, e); } fputc(')', of); break; @@ -337,14 +346,14 @@ wwi_type(FILE *of, Node *t) * unparse/type.ha:290-300). */ if (e->op == TK_ELLIPSIS) fputs("...", of); - wwi_type(of, e); + wwi_type(c, of, owner, source, e); } fputc(')', of); break; case N_TENUM: fputs("enum ", of); if (t->lhs) { - wwi_type(of, t->lhs); + wwi_type(c, of, owner, source, t->lhs); fputc(' ', of); } fputs("{ ", of); @@ -353,7 +362,7 @@ wwi_type(FILE *of, Node *t) fputs(m->str, of); if (m->lhs) { fputs(" = ", of); - wwi_expr(of, m->lhs); + wwi_expr(c, of, owner, source, m->lhs); } } fputs(" }", of); @@ -364,7 +373,7 @@ wwi_type(FILE *of, Node *t) } static void -wwi_expr(FILE *of, Node *e) +wwi_expr(Checker *c, FILE *of, const char *owner, int source, Node *e) { if (e == NULL) return; @@ -375,10 +384,17 @@ wwi_expr(FILE *of, Node *e) break; case N_IDENT: case N_TNAME: - fputs(e->str, of); + wwi_name(c, of, owner, source, e->str); break; case N_DOT: - wwi_expr(of, e->lhs); + if (e->lhs && e->lhs->kind == N_IDENT) { + const char *path = wwi_use_path(c, owner, source, + e->lhs->str); + if (path != NULL) wwi_canonical_alias(of, path); + else wwi_expr(c, of, owner, source, e->lhs); + } else { + wwi_expr(c, of, owner, source, e->lhs); + } fputc('.', of); fputs(e->str, of); break; @@ -393,20 +409,20 @@ wwi_expr(FILE *of, Node *e) case N_RUNELIT: wwi_rune(of, e->uval); break; case N_BIN: fputc('(', of); - wwi_expr(of, e->lhs); + wwi_expr(c, of, owner, source, e->lhs); fprintf(of, " %s ", tokname(e->op)); - wwi_expr(of, e->rhs); + wwi_expr(c, of, owner, source, e->rhs); fputc(')', of); break; case N_UN: fputs(tokname(e->op), of); - wwi_expr(of, e->lhs); + wwi_expr(c, of, owner, source, e->lhs); break; case N_CAST: fputc('(', of); - wwi_expr(of, e->lhs); + wwi_expr(c, of, owner, source, e->lhs); fputs(": ", of); - wwi_type(of, e->rhs); + wwi_type(c, of, owner, source, e->rhs); fputc(')', of); break; default: @@ -428,7 +444,7 @@ wwi_attr_relevant(const char *nm) } static void -wwi_attrs(FILE *of, Node *d) +wwi_attrs(Checker *c, FILE *of, const char *owner, Node *d) { for (Node *a = d->attr; a; a = a->next) { if (a->kind != N_ATTR || !wwi_attr_relevant(a->str)) @@ -439,7 +455,7 @@ wwi_attrs(FILE *of, Node *d) fputc('(', of); for (Node *arg = a->list; arg; arg = arg->next) { if (arg != a->list) fputs(", ", of); - wwi_expr(of, arg); + wwi_expr(c, of, owner, d->sourceid, arg); } fputc(')', of); } @@ -448,34 +464,34 @@ wwi_attrs(FILE *of, Node *d) } static void -wwi_decl(FILE *of, Node *d) +wwi_decl(Checker *c, FILE *of, const char *owner, Node *d) { switch (d->kind) { case N_FNDECL: - wwi_attrs(of, d); + wwi_attrs(c, of, owner, d); fputs(d->export ? "export fn " : "fn ", of); fputs(d->str, of); fputc('(', of); for (Node *p = d->list; p; p = p->next) { if (p != d->list) fputs(", ", of); - wwi_param(of, p); + wwi_param(c, of, owner, d->sourceid, p); } fputs(") ", of); - wwi_type(of, d->lhs); + wwi_type(c, of, owner, d->sourceid, d->lhs); fputs(";\n", of); break; case N_TYPEDECL: fputs(d->export ? "export type " : "type ", of); fputs(d->str, of); fputs(" = ", of); - wwi_type(of, d->lhs); + wwi_type(c, of, owner, d->sourceid, d->lhs); fputs(";\n", of); break; case N_DEF: fputs(d->export ? "export def " : "def ", of); fputs(d->str, of); fputs(": ", of); - wwi_type(of, d->lhs); + wwi_type(c, of, owner, d->sourceid, d->lhs); /* An aggregate initializer (N_STRUCTLIT/N_ARRLIT) is a DATA- * global (#52): emit a value-LESS prototype `export def X: T;`. * The defining package's own .o emits the struct/array DATA; the @@ -491,7 +507,7 @@ wwi_decl(FILE *of, Node *d) fputs(";\n", of); } else { fputs(" = ", of); - wwi_expr(of, d->rhs); + wwi_expr(c, of, owner, d->sourceid, d->rhs); fputs(";\n", of); } break; @@ -502,7 +518,7 @@ wwi_decl(FILE *of, Node *d) if (d->lhs == NULL) fatal("wwi: exported let '%s' has no declared type", d->str); - wwi_type(of, d->lhs); + wwi_type(c, of, owner, d->sourceid, d->lhs); fputs(";\n", of); break; default: @@ -616,11 +632,6 @@ wwi_valuesym(Checker *c, const char *owner, int source, const char *name) if (s->kind == SK_DEF && strcmp(s->name, name) == 0 && wwi_mod_eq(s->mod, owner)) return s; - for (Scope *p = c->top; p; p = p->parent) - for (Sym *s = p->first; s; s = s->next) - if (s->kind == SK_DEF && strcmp(s->name, name) == 0 - && wwi_direct_mod_visible(c, owner, source, s->mod)) - return s; return NULL; } @@ -812,7 +823,9 @@ wwi_emit_imports(FILE *of, Node *file, const char *owner, int source, const char *previous = NULL; for (int i = 0; i < nuse; i++) { if (previous && strcmp(previous, us[i].path) == 0) continue; - fprintf(of, "import %s;\n", us[i].path); + fputs("import ", of); + wwi_canonical_alias(of, us[i].path); + fprintf(of, " %s;\n", us[i].path); previous = us[i].path; } free(us); @@ -865,16 +878,16 @@ wwi_emit_primary_section(Checker *c, FILE *of, Node *file, wwi_emit_imports(of, file, owner, source, 0); for (int i = 0; i < fs->nprivate; i++) if (fs->privatefacts[i].d->sourceid == source) - wwi_decl(of, fs->privatefacts[i].d); + wwi_decl(c, of, NULL, fs->privatefacts[i].d); if (c->is_test_package) for (Node *d = file->list; d; d = d->next) if (wwi_primary(d) && d->sourceid == source && d->kind == N_FNDECL && !d->export && wwi_has_attr(d, "test")) - wwi_decl(of, d); + wwi_decl(c, of, NULL, d); for (int i = 0; i < nexports; i++) if (exports[i].d->sourceid == source) - wwi_decl(of, exports[i].d); + wwi_decl(c, of, NULL, exports[i].d); } int @@ -964,16 +977,15 @@ wwi_emit(Checker *c, FILE *of, Node *file) struct factent *f = &fs.facts[i]; if (lastmod == NULL || strcmp(lastmod, f->mod) != 0 || lastsource != f->d->sourceid) { - const char *dot = strrchr(f->mod, '.'); - const char *factpkg = f->d->pkgname && f->d->pkgname[0] - ? f->d->pkgname : (dot ? dot + 1 : f->mod); fprintf(of, "//ww:module %s\n", f->mod); - fprintf(of, "package %s;\n", factpkg); + fputs("package ", of); + wwi_canonical_alias(of, f->mod); + fputs(";\n", of); wwi_emit_imports(of, file, f->mod, f->d->sourceid, 1); lastmod = f->mod; lastsource = f->d->sourceid; } - wwi_decl(of, f->d); + wwi_decl(c, of, f->mod, f->d); } free(fs.privatefacts); free(fs.facts); diff --git a/cmd/wcc/check.c b/cmd/wcc/check.c index 5c08d6a1..601cbc53 100644 --- a/cmd/wcc/check.c +++ b/cmd/wcc/check.c @@ -60,6 +60,8 @@ lookup_builtin(const char *name) static const char *decl_mod(Node *file, Node *d); static const char *use_path(Node *file, const char *curmod, int source, const char *alias); +static const char *find_use_path(Node *file, const char *curmod, int source, + const char *alias, int mark); static int src_imports(Node *file, const char *modtag, int source, const char *name); static Sym *lookup_visible(Checker *c, const char *name); @@ -2911,7 +2913,8 @@ decl_mod(Node *file, Node *d) * Returns NULL if the referencing package has no such `use`. */ static const char * -use_path(Node *file, const char *curmod, int source, const char *alias) +find_use_path(Node *file, const char *curmod, int source, const char *alias, + int mark) { if (file == NULL || alias == NULL) return NULL; /* Legacy inline multi-package units may spell a package's own @@ -2931,13 +2934,19 @@ use_path(Node *file, const char *curmod, int source, const char *alias) int same = (um == NULL) ? (curmod == NULL) : (curmod != NULL && strcmp(um, curmod) == 0); if (same) { - u->used = 1; + if (mark) u->used = 1; return p; } } return NULL; } +static const char * +use_path(Node *file, const char *curmod, int source, const char *alias) +{ + return find_use_path(file, curmod, source, alias, 1); +} + /* * resolve_typedecl — resolve d's body into its installed TY_NAMED * placeholder. Reached from check_file's typedecl pass AND on demand @@ -3019,29 +3028,6 @@ src_imports(Node *file, const char *modtag, int source, const char *name) return 0; } -/* A flattened interface symbol is source-visible as a bare name only when - * the referencing package directly imports the symbol's defining path. The - * strict scope helpers above handle lexical locals, builtins and same-package - * declarations first; this second pass restores WW's existing direct-import - * convenience without admitting a transitive interface by accident. */ -static int -direct_module_visible(Checker *c, const char *mod) -{ - if (c == NULL || mod == NULL || mod[0] == '\0') return 0; - for (Node *u = c->file->list; u; u = u->next) { - if (u->kind != N_USE || u->sourceid != c->cur_source) continue; - const char *um = decl_mod(c->file, u); - int same = c->cur_mod == NULL ? um == NULL - : um != NULL && strcmp(um, c->cur_mod) == 0; - const char *path = u->usepath ? u->usepath : u->str; - if (same && path != NULL && strcmp(path, mod) == 0) { - u->used = 1; - return 1; - } - } - return 0; -} - static Sym * lookup_visible(Checker *c, const char *name) { @@ -3053,24 +3039,16 @@ lookup_visible(Checker *c, const char *name) if (s->decl != NULL) return s; builtin = s; } - /* N_USE entries are historically coalesced by leaf in the flat scope, - * so the retained symbol may carry another source package's owner. The - * source-owned alias map is authoritative: if this package directly - * imports NAME, return the coalesced module marker only as a marker; the - * N_DOT path maps the alias to the correct full path again. */ - if (use_path(c->file, c->cur_mod, c->cur_source, name) != NULL) { + /* Flat scope installation may coalesce equal qualifiers from distinct + * files. The source-owned binding is authoritative, but a bare mention is + * not usage; only the enclosing qualified lookup may mark it. */ + if (find_use_path(c->file, c->cur_mod, c->cur_source, name, 0) != NULL) { for (Scope *p = c->cur; p; p = p->parent) for (Sym *b = p->first; b; b = b->next) if (strcmp(b->name, name) == 0 && (b->kind == SK_USE || b->use_alias)) return b; } - for (Scope *p = c->cur; p; p = p->parent) { - for (Sym *b = p->first; b; b = b->next) - if (strcmp(b->name, name) == 0 - && direct_module_visible(c, b->mod)) - return b; - } return builtin; } @@ -3079,14 +3057,6 @@ lookup_visible_type(Checker *c, const char *name) { Sym *s = scope_lookup_type(c->cur, c->cur_mod, name); if (s != NULL) return s; - for (Scope *p = c->cur; p; p = p->parent) { - for (Sym *b = p->first; b; b = b->next) - if (b->kind == SK_TYPE && strcmp(b->name, name) == 0 - && direct_module_visible(c, b->mod) - && (b->decl == NULL || !b->decl->imported - || b->decl->export)) - return b; - } return NULL; } @@ -3162,6 +3132,44 @@ top_decl_kind(Node *d) || d->kind == N_FNDECL || d->kind == N_LET); } +/* Import usage is a property of the file-local qualifier occurrence. Record + * qualified syntax before resolving declaration bodies so import diagnostics + * retain production Go's source order without making a failed bare lookup a + * use. WW already rejects lexical bindings that shadow an import qualifier. */ +static void +mark_import_uses_node(Checker *c, Node *n, const char *owner, int source) +{ + if (n == NULL) return; + if (n->kind == N_TNAME && n->str != NULL) { + const char *dot = strrchr(n->str, '.'); + if (dot != NULL) { + char *head = astrndup(c->a, n->str, (size_t)(dot - n->str)); + (void)find_use_path(c->file, owner, source, head, 1); + } + } else if (n->kind == N_DOT && n->lhs != NULL + && n->lhs->kind == N_IDENT && n->lhs->str != NULL) { + (void)find_use_path(c->file, owner, source, n->lhs->str, 1); + } + for (Node *p = n->attr; p; p = p->next) + mark_import_uses_node(c, p, owner, source); + mark_import_uses_node(c, n->lhs, owner, source); + mark_import_uses_node(c, n->rhs, owner, source); + mark_import_uses_node(c, n->cond, owner, source); + mark_import_uses_node(c, n->body, owner, source); + mark_import_uses_node(c, n->els, owner, source); + for (Node *p = n->list; p; p = p->next) + mark_import_uses_node(c, p, owner, source); +} + +static void +mark_import_uses(Checker *c, Node *file) +{ + for (Node *d = file->list; d; d = d->next) { + if (d->kind == N_USE) continue; + mark_import_uses_node(c, d, decl_mod(file, d), d->sourceid); + } +} + static void check_import_alt(Node *d, const char *name) { @@ -3204,7 +3212,8 @@ check_import_usage_and_collisions(Checker *c, Node *file) for (Node *u = file->list; u; u = u->next) { if (u->kind != N_USE || u->imported || u->used || u->str == NULL) continue; - const char *path = u->usepath ? u->usepath : u->str; + const char *path = u->usesource ? u->usesource + : u->usepath ? u->usepath : u->str; const char *dot = strrchr(path, '.'); const char *leaf = dot ? dot + 1 : path; if (strcmp(u->str, leaf) == 0) @@ -3220,7 +3229,8 @@ check_import_usage_and_collisions(Checker *c, Node *file) if (u->kind != N_USE || u->imported || u->str == NULL || strcmp(d->str, u->str) != 0) continue; - const char *path = u->usepath ? u->usepath : u->str; + const char *path = u->usesource ? u->usesource + : u->usepath ? u->usepath : u->str; err(c, d->pos, "%s already declared through import of package %s (\"%s\")", d->str, u->str, path); @@ -3248,11 +3258,12 @@ check_file(Checker *c, Node *file) /* The synthetic runner belongs to file->sourceid. An import in an * earlier module-reset section neither supplies nor uses its binding. */ for (Node *u = file->list; u; u = u->next) { - if (c->test_target != NULL && u->kind == N_USE + if (u->kind == N_USE && !u->imported && u->sourceid == file->sourceid && u->usepath - && (strcmp(u->usepath, c->test_target) == 0 - || strcmp(u->usepath, c->test_module) == 0)) + && (strcmp(u->usepath, c->test_module) == 0 + || (c->test_target != NULL + && strcmp(u->usepath, c->test_target) == 0))) u->used = 1; if (u->kind == N_USE && !u->imported && u->sourceid == file->sourceid @@ -3264,15 +3275,18 @@ check_file(Checker *c, Node *file) Node *usenode = newnode(c->a, N_USE, file->pos); usenode->str = c->test_module; usenode->strlen = strlen(c->test_module); + usenode->usesource = c->test_module; usenode->usepath = c->test_module; usenode->pkgname = file->pkgname; usenode->sourceid = file->sourceid; - if (c->test_target != NULL) usenode->used = 1; + usenode->used = 1; usenode->next = file->list; file->list = usenode; } } + mark_import_uses(c, file); check_import_redeclarations(c, file); + check_import_usage_and_collisions(c, file); /* pass 1: install names (types first, then defs/fns). * For self-referential types we install the named-type placeholder @@ -3915,8 +3929,6 @@ check_file(Checker *c, Node *file) } c->cur_mod = NULL; c->cur_source = 0; - check_import_usage_and_collisions(c, file); - /* * #6 harec-fidelity (ref/harec/src/check.c:3941): a @test fn is * fully checked above — pass 2 walked its body like every fn — but diff --git a/cmd/wcc/parse.c b/cmd/wcc/parse.c index f21757c7..fc853c88 100644 --- a/cmd/wcc/parse.c +++ b/cmd/wcc/parse.c @@ -1315,30 +1315,40 @@ parseblock(Parser *p) return n; } -/* `import encoding.utf8;` — the driver resolves the dotted path to a - * directory; the checker only needs the leaf (`utf8`) as the module - * bareword for n_use→decl disambiguation, mirroring Hare's - * `use encoding::utf8;` → `utf8::name` (ref/hare/hare/ast/import.ha:7 - * stores `ident: []str` but identifier-resolution uses the last - * component). */ +/* The optional alias is source-local; the dotted path remains the dependency + * identity supplied to the package driver. */ static Node * parseuse(Parser *p) { Pos pp = p->cur.pos; expect(p, TK_USE); Node *n = newnode(p->a, N_USE, pp); - /* M1 #22: accumulate the full dotted import path in usepath. `str` - * starts as its leaf; direct export metadata later installs the imported - * declaration's default name without changing canonical identity. */ - const char *leaf = expectident(p); + const char *alias = NULL; + const char *first; + if (p->cur.kind == TK_UNDER) { + errorf(p->cur.pos, "blank import alias _ is not implemented"); + p->errs++; + alias = "_"; + advance(p); + first = expectident(p); + } else { + first = expectident(p); + if (p->cur.kind == TK_IDENT) { + alias = first; + first = expectident(p); + } + } + const char *leaf = first; const char *path = leaf; while (accept(p, TK_DOT)) { leaf = expectident(p); path = aprintf(p->a, "%s.%s", path, leaf); } - n->str = leaf; - n->strlen = strlen(leaf); + n->str = alias ? alias : leaf; + n->strlen = strlen(n->str); + n->usesource = path; n->usepath = path; + n->usealias = alias; expect(p, TK_SEMI); return n; } diff --git a/cmd/wcc/ww.h b/cmd/wcc/ww.h index 9f3b36c5..f4de16d0 100644 --- a/cmd/wcc/ww.h +++ b/cmd/wcc/ww.h @@ -343,11 +343,12 @@ struct Node { * let) carry it. On an N_USE node * this is the importing (owning) * module. */ - const char *usepath; /* M1 #22: on an N_USE node, the full - * canonical import path (`encoding.utf8`); - * `str` is the declared default qualifier. Drives - * the path-keyed decl_mod match and the - * qualified-ref codegen hint. */ + const char *usesource; /* N_USE: immutable dotted source spelling. */ + const char *usepath; /* N_USE: canonical, vendor-expanded identity; + * initially equal to `usesource`. */ + const char *usealias; /* N_USE: explicit file-local alias, or NULL. */ + const char *usepkgname; /* N_USE: imported declared package name, + * independent of the visible binding in `str`. */ const char *pkgname; /* declared package name for this source/export * section; independent of canonical `module`. */ int sourceid; /* lexical source-file scope within the parsed diff --git a/cmd/ww/main.c b/cmd/ww/main.c index 95405dc0..48735106 100644 --- a/cmd/ww/main.c +++ b/cmd/ww/main.c @@ -2076,8 +2076,10 @@ use_node_cmp(const void *a, const void *b) { const Node *x = *(Node *const *)a; const Node *y = *(Node *const *)b; - const char *xp = x->usepath ? x->usepath : x->str; - const char *yp = y->usepath ? y->usepath : y->str; + const char *xp = x->usesource ? x->usesource + : x->usepath ? x->usepath : x->str; + const char *yp = y->usesource ? y->usesource + : y->usepath ? y->usepath : y->str; int r = strcmp(xp, yp); if (r != 0) return r; 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; for (int i = 0; i < nuse && rc == 0; 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)) { errorf(u->pos, "package path %s is reserved", name); rc = -1; @@ -3663,7 +3666,7 @@ static void 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", - 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 diff --git a/lib/ww/syntax/ast.ww b/lib/ww/syntax/ast.ww index 643fdd2d..3981a8c0 100644 --- a/lib/ww/syntax/ast.ww +++ b/lib/ww/syntax/ast.ww @@ -127,8 +127,10 @@ export type node = struct { type_: *void, // filled in by checker; type.ww treats it as *tinfo tsuffix: str, // typed numeric literal suffix ("i32", "u64", ...) nmod: str, // originating module from `// MODULE: foo`; "" if none - usepath: str, // on N_USE: full canonical import path; `str` becomes - // the file-local declared default qualifier; "" otherwise + usesource: str, // N_USE: immutable dotted source spelling + 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 sourceid: i32, // lexical source-file scope in an owner/export unit 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 // to tokenise identically through C and ww (lex.ww:382 has the // 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; }; diff --git a/lib/ww/syntax/decl.ww b/lib/ww/syntax/decl.ww index 54b200dd..f07a1bd0 100644 --- a/lib/ww/syntax/decl.ww +++ b/lib/ww/syntax/decl.ww @@ -4,11 +4,8 @@ package syntax; import strings; -// `import encoding.utf8;` — the driver resolves the dotted path to -// a directory; only the leaf (`utf8`) is needed downstream as the -// module bareword for n_use → decl disambiguation, mirroring Hare's -// `use encoding::utf8;` → `utf8::name` (ref/hare/hare/ast/import.ha:7 -// stores `[]str` but identifier-resolution uses the last component). +// The optional alias is source-local; the dotted path remains the dependency +// identity supplied to the package driver. fn parseuse(p: *parser) *node = { let pf: str = p.curfile; let pl: i32 = p.curline; @@ -16,19 +13,31 @@ fn parseuse(p: *parser) *node = { advance(p); let n: *node = newnode(nkind.N_USE, pf, pl, pc); n.nmod = p.curmod; - // M1 #22: accumulate the full dotted import path (n.usepath) for the - // checker's path-keyed module match. n.str is the provisional path leaf; - // direct export metadata later replaces it with the declared default name. - let leaf: str; - expectident(p, &leaf); + let alias: str; + let first: str; + if (p.curkind == tkind.TK_UNDER) { + errmsg(p, "blank import alias _ is not implemented"); + 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; for (p.curkind == tkind.TK_DOT) { advance(p); expectident(p, &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.usealias = alias; expecttok(p, tkind.TK_SEMI, "expected ';' after use"); return n; }; diff --git a/selfhost/cmd/w6c/main.ww b/selfhost/cmd/w6c/main.ww index ffd88b5a..02bc9382 100644 --- a/selfhost/cmd/w6c/main.ww +++ b/selfhost/cmd/w6c/main.ww @@ -99,26 +99,52 @@ fn allocnodeptrs(count: i32) ([]*syntax.node | nomem) = { return value; }; -// Export data carries canonical owner and declared name independently. Each -// direct interface's package-clause markers also describe its reachable fact -// closure, so search all parsed interfaces for a canonical owner. -fn importpkgname(asts: []*syntax.node, nasts: i32, path: str, +fn canonicalpkgname(path: str, name: str) bool = { + let prefix: str = "__wwi_"; + if (name.len != prefix.len + path.len * 2) { return false; }; + 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 = { let name: str; + let placeholder: str; let i: i32 = 0; for (i < nasts) { + if (!cstreq(paths[i], path)) { i += 1; continue; }; let f: *syntax.node = asts[i]; let p: *syntax.node = nil; if (f != nil) { p = f.body; }; for (p != nil) { if (p.nmod.len > 0 && p.pkgname.len > 0 && 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; let empty: str; return empty; - }; - name = p.pkgname; + } else { name = p.pkgname; }; }; }; p = p.next; }; @@ -129,19 +155,22 @@ fn importpkgname(asts: []*syntax.node, nasts: i32, path: str, for (p != nil) { if (p.nmod.len > 0 && p.pkgname.len > 0 && 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; let empty: str; return empty; - }; - name = p.pkgname; + } else { name = p.pkgname; }; }; }; p = p.next; }; + if (name.len == 0) { name = placeholder; }; 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 = { let u: *syntax.node = list; for (u != nil) { @@ -151,7 +180,7 @@ fn bindimportnames(list: *syntax.node, asts: []*syntax.node, nasts: i32, && syntax.streq(u.usepath, "__wwtest"); if (!reserved) { let conflict: bool = false; - let name: str = importpkgname(asts, nasts, u.usepath, + let name: str = importpkgname(asts, paths, nasts, u.usepath, primary, &conflict); if (conflict) { let pre: str = "w6c: package "; @@ -162,7 +191,9 @@ fn bindimportnames(list: *syntax.node, asts: []*syntax.node, nasts: i32, return false; }; 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) { let pre: str = "w6c: import "; 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 // interface. Its canonical path must never become a leaf // 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 importtail: *node = nil; + let importhead: *syntax.node = nil; + let importtail: *syntax.node = nil; importi = 0; for (importi < nimports) { let ibuf: *u8; @@ -456,20 +488,20 @@ export fn main(argc: i32, argv: **u8) i32 = { os.write(2, nl.ptr, nl.len: u64); return 1; }; - let il: lex; - lexinit(&il, strings.dup(pathstr(importfiles[importi])), ibuf, ilen); - let ips: parser; - parserinit(&ips, &il); + let il: syntax.lex; + syntax.lexinit(&il, strings.dup(pathstr(importfiles[importi])), ibuf, ilen); + let ips: syntax.parser; + syntax.parserinit(&ips, &il); let imod: str = pathstr(importpaths[importi]); ips.pathmod = imod; ips.curmod = imod; 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; }; importasts[importi] = imported; - if (!bindimportnames(imported.list, importasts, importi + 1, + if (!bindimportnames(imported.list, importasts, importpaths, importi + 1, nil, testsupport)) { return 1; }; - let d: *node = imported.list; + let d: *syntax.node = imported.list; if (d != nil) { if (importhead == nil) { importhead = 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); return 1; }; - let l: lex; - lexinit(&l, strings.dup(pathstr(src)), buf, blen); - let ps: parser; - parserinit(&ps, &l); + let l: syntax.lex; + syntax.lexinit(&l, strings.dup(pathstr(src)), buf, blen); + let ps: syntax.parser; + syntax.parserinit(&ps, &l); if (testsupport != nil) { ps.testmodule = pathstr(testsupport); }; // Entry compilation classifies an ordinary command package. The explicit // marker carries only that parser fact for non-entry command test variants. 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 // `if (l.errs || p.errs) return 1;` — broken AST otherwise reaches // cgen and emits junk asm with a zero exit (silent miscompile). if (l.errs > 0 || ps.errs > 0) { return 1; }; - let use: *node = f.list; + let use: *syntax.node = f.list; for (use != nil) { 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; for (mapi < nmaps) { - if (syntax.streq(use.usepath, + if (syntax.streq(source, pathstr(importmaps[mapi].source))) { use.usepath = pathstr(importmaps[mapi].path); 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. importi = 0; for (importi < nimports) { - if (!bindimportnames(importasts[importi].list, importasts, nimports, + if (!bindimportnames(importasts[importi].list, importasts, importpaths, + nimports, nil, testsupport)) { return 1; }; importi += 1; }; - if (!bindimportnames(f.list, importasts, nimports, f, testsupport)) { + if (!bindimportnames(f.list, importasts, importpaths, nimports, f, + testsupport)) { return 1; }; if (testtarget != nil) { diff --git a/selfhost/cmd/wcc/cgenutil.ww b/selfhost/cmd/wcc/cgenutil.ww index 11b58c89..d71d1465 100644 --- a/selfhost/cmd/wcc/cgenutil.ww +++ b/selfhost/cmd/wcc/cgenutil.ww @@ -586,8 +586,15 @@ fn pushargsrev(c: *cgen, arg: *syntax.node, param: *syntax.node, memphase: bool, }; if (!aistagged) { widensz = slotsize(c, ptype); - let tagged: *syntax.node = resolvetagged(c, ptype); - let t: i32 = taggedvariantindex(c, tagged, arg); + // The parameter may come from an imported signature whose + // 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 // no scalar tag here (the widen store // remaps); only a CONCRETE source with no diff --git a/selfhost/cmd/wcc/check.ww b/selfhost/cmd/wcc/check.ww index c94c2529..dc2bd351 100644 --- a/selfhost/cmd/wcc/check.ww +++ b/selfhost/cmd/wcc/check.ww @@ -173,12 +173,11 @@ fn declmod(file: *syntax.node, d: *syntax.node) str = { return empty; }; -// usepath — map a source-file default qualifier (the imported package's -// declared name) to the full canonical import path it binds, for -// module-qualified resolution and codegen hint (M1 #22). Only an import owned -// by the referencing file is visible; a matching -// alias carried by a transitive interface is deliberately ignored. -fn usepathfor(file: *syntax.node, modtag: str, source: i32, alias: str) str = { +// Map a source-file qualifier to canonical identity. Looking up the marker for +// a possible DOT must not itself count as usage; only qualified resolution +// marks the owning occurrence. +fn findusepath(file: *syntax.node, modtag: str, source: i32, alias: str, + mark: bool) str = { let empty: str; if (file == nil) { 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; }; } else { if (syntax.streq(um, modtag)) { same = true; }; }; if (same) { - u.used = 1; + if (mark) { u.used = 1; }; if (u.usepath.len != 0) { return u.usepath; }; return u.str; }; @@ -209,6 +208,10 @@ fn usepathfor(file: *syntax.node, modtag: str, source: i32, alias: str) str = { 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 // the referencing package did not itself declare that import. 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; }; -// 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 = { let found: *syntax.sym = syntax.scopelookupprefer(c.cur, c.curmod, name); let builtin: *syntax.sym = nil; @@ -282,10 +260,10 @@ fn lookupvisible(c: *checker, name: str) *syntax.sym = { if (found.decl != nil) { return found; }; builtin = found; }; - // Flat scope installation coalesces same-leaf N_USE entries. The - // source-owned alias map, not the retained marker's mod field, decides - // whether this package can use the qualifier. - if (usepathfor(c.file, c.curmod, c.cursource, name).len != 0) { + // Flat scope installation may coalesce equal qualifiers from distinct + // files. A bare mention only locates the marker; the DOT resolution owns + // usage marking. + if (findusepath(c.file, c.curmod, c.cursource, name, false).len != 0) { let q: *syntax.scope = c.cur; for (q != nil) { let u: *syntax.sym = q.first; @@ -298,17 +276,6 @@ fn lookupvisible(c: *checker, name: str) *syntax.sym = { 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; }; @@ -323,19 +290,6 @@ fn lookupvisibletype(c: *checker, name: str) *syntax.sym = { }; let found: *syntax.sym = syntax.scopelookuptype(c.cur, c.curmod, name); 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; }; @@ -7332,6 +7286,22 @@ fn hascvariadic(params: *syntax.node) bool = { 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` // when two params share a name. The fn body's scope IS fresh here // (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 // check_func_type. Surface-fidelity preserved: wwdump // -a runs parser only and never reaches this mutation. - if (p.op == syntax.tkind.TK_ELLIPSIS) { - 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; - }; - }; + normalizevariadicparam(p); let nm: str = p.str; if (nm.len > 0) { checkmoduleshadow(c, nm, "param"); @@ -7390,7 +7346,9 @@ fn resolvefnbody(c: *checker, fnnode: *syntax.node) void = { let p: *syntax.node = fnnode.list; for (p != nil) { 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; }; @@ -7550,6 +7508,42 @@ fn topdeclkind(d: *syntax.node) bool = { || 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 = { if (c.sepmode == 0) { return; }; let u: *syntax.node = file.list; @@ -7577,7 +7571,8 @@ fn checkimportusageandcollisions(c: *checker, file: *syntax.node) void = { let u: *syntax.node = file.list; for (u != nil) { 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; }; let (prefix, suffix) = strings.rcut(path, "."); let leaf: str = suffix; @@ -7600,7 +7595,8 @@ fn checkimportusageandcollisions(c: *checker, file: *syntax.node) void = { for (u != nil) { if (u.kind == syntax.nkind.N_USE && u.imported == 0 && 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; }; importdiagprefix(d); cerr(d.str); 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. let su: *syntax.node = file.list; 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 - && (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; }; if (su.kind == syntax.nkind.N_USE && su.imported == 0 @@ -7675,15 +7672,18 @@ fn checkfile(c: *checker, file: *syntax.node) void = { if (!present) { let usenode: *syntax.node = syntax.newnode(syntax.nkind.N_USE, file.file, file.line, file.col); usenode.str = c.testmodule; + usenode.usesource = c.testmodule; usenode.usepath = c.testmodule; usenode.pkgname = file.pkgname; usenode.sourceid = file.sourceid; - if (c.testtarget.len > 0) { usenode.used = 1i32; }; + usenode.used = 1i32; usenode.next = file.list; file.list = usenode; }; }; + markimportuses(c, file); checkimportredeclarations(c, file); + checkimportusageandcollisions(c, file); // Pass 1: install all top-level names. 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. // Track the per-decl module bareword so bare-leaf lookups inside // 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"); 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); case syntax.nkind.N_DEF: // #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; }; - checkimportusageandcollisions(c, file); - // Pass 3 (#15, A.6.2.1e): post-checker invariant gate. Walks each // decl with its curmod set so asserttyped's gate lookups resolve // against the same module context exprtype saw during pass 2. diff --git a/selfhost/cmd/wcc/wwi.ww b/selfhost/cmd/wcc/wwi.ww index b3ea59dd..5da6cc70 100644 --- a/selfhost/cmd/wcc/wwi.ww +++ b/selfhost/cmd/wcc/wwi.ww @@ -104,24 +104,41 @@ fn wwiusepath(c: *checker, owner: str, source: i32, alias: str) str = { return empty; }; -fn wwidirectmodvisible(c: *checker, owner: str, source: i32, 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 == source) { - let same: bool = false; - if (owner.len == 0) { - same = u.imported == 0; - } else { - same = u.imported != 0 && wwimodeq(u.nmod, owner); - }; - let path: str = u.str; - if (u.usepath.len > 0) { path = u.usepath; }; - if (same && syntax.streq(path, mod)) { return true; }; - }; - u = u.next; +// Canonical export spelling depends only on path identity, never on the +// source alias or imported declared package name. +fn wwicanonicalalias(fd: i32, path: str) void = { + wputs(fd, "__wwi_"); + let i: i32 = 0; + for (i < path.len) { + let hi: u8 = path[i] >> 4u8; + let lo: u8 = path[i] & 15u8; + if (hi < 10u8) { wputb(fd, hi + 48u8); } + else { wputb(fd, hi - 10u8 + 97u8); }; + if (lo < 10u8) { wputb(fd, lo + 48u8); } + else { wputb(fd, lo - 10u8 + 97u8); }; + i += 1; }; - 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 = { @@ -145,22 +162,6 @@ fn wwitypesym(c: *checker, owner: str, source: i32, nm: str) *syntax.sym = { }; } else { 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.skind != syntax.skind.SK_TYPE) { return nil; }; @@ -307,15 +308,20 @@ fn wwirune(fd: i32, cp: u64) void = { 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.kind == syntax.nkind.N_INTLIT) { wputs(fd, strconv.u64tos(e.uval, strconv.base.DEC)); if (e.tsuffix.len > 0) { wputs(fd, e.tsuffix); }; } 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) { - 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, '.'); wputs(fd, e.str); } else { if (e.kind == syntax.nkind.N_TRUE) { @@ -334,20 +340,20 @@ fn wwiexpr(fd: i32, e: *syntax.node) void = { wwirune(fd, e.uval); } else { if (e.kind == syntax.nkind.N_BIN) { wputb(fd, '('); - wwiexpr(fd, e.lhs); + wwiexpr(c, fd, owner, source, e.lhs); wputb(fd, 32u8); wputs(fd, syntax.tokname(e.op)); wputb(fd, 32u8); - wwiexpr(fd, e.rhs); + wwiexpr(c, fd, owner, source, e.rhs); wputb(fd, ')'); } else { if (e.kind == syntax.nkind.N_UN) { 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) { wputb(fd, '('); - wwiexpr(fd, e.lhs); + wwiexpr(c, fd, owner, source, e.lhs); wputs(fd, ": "); - wwitype(fd, e.rhs); + wwitype(c, fd, owner, source, e.rhs); wputb(fd, ')'); } else { 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 `...` wputs(fd, "..."); 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. // 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) { - wwitype(fd, p.lhs.lhs); + wwitype(c, fd, owner, source, p.lhs.lhs); } else { - wwitype(fd, p.lhs); + wwitype(c, fd, owner, source, p.lhs); }; if (p.op == syntax.tkind.TK_ELLIPSIS) { // Hare `T...` variadic 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 wputs(fd, "void"); return; }; // rule-10: unwrap the wwstage-only N_TPARAM tuple-element wrapper // (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.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) { wputb(fd, '*'); - wwitype(fd, t.lhs); + wwitype(c, fd, owner, source, t.lhs); } else { if (t.kind == syntax.nkind.N_TSLICE) { wputs(fd, "[]"); - wwitype(fd, t.lhs); + wwitype(c, fd, owner, source, t.lhs); } else { if (t.kind == syntax.nkind.N_TARRAY) { 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, ']'); - wwitype(fd, t.lhs); + wwitype(c, fd, owner, source, t.lhs); } else { if (t.kind == syntax.nkind.N_TBANG) { wputb(fd, '!'); - wwitype(fd, t.lhs); + wwitype(c, fd, owner, source, t.lhs); } else { if (t.kind == syntax.nkind.N_TCHAN) { wputs(fd, "chan "); - wwitype(fd, t.lhs); + wwitype(c, fd, owner, source, t.lhs); } else { if (t.kind == syntax.nkind.N_TFN) { wputs(fd, "fn("); let p: *syntax.node = t.list; for (p != nil) { if (p != t.list) { wputs(fd, ", "); }; - wwiparam(fd, p); + wwiparam(c, fd, owner, source, p); p = p.next; }; wputs(fd, ") "); - wwitype(fd, t.lhs); + wwitype(c, fd, owner, source, t.lhs); } else { if (t.kind == syntax.nkind.N_TSTRUCT) { // re-emit `@packed` so the flag round-trips through // 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, ": "); }; - wwitype(fd, f.lhs); + wwitype(c, fd, owner, source, f.lhs); f = f.next; }; wputs(fd, " }"); @@ -438,7 +450,7 @@ fn wwitype(fd: i32, t: *syntax.node) void = { let e: *syntax.node = t.list; for (e != nil) { if (e != t.list) { wputs(fd, ", "); }; - wwitype(fd, e); + wwitype(c, fd, owner, source, e); e = e.next; }; wputb(fd, ')'); @@ -451,14 +463,14 @@ fn wwitype(fd: i32, t: *syntax.node) void = { // the consumer's type-store flattens // (ref/hare/hare/unparse/type.ha:290-300). if (e.op == syntax.tkind.TK_ELLIPSIS) { wputs(fd, "..."); }; - wwitype(fd, e); + wwitype(c, fd, owner, source, e); e = e.next; }; wputb(fd, ')'); } else { if (t.kind == syntax.nkind.N_TENUM) { wputs(fd, "enum "); if (t.lhs != nil) { - wwitype(fd, t.lhs); + wwitype(c, fd, owner, source, t.lhs); wputb(fd, 32u8); }; wputs(fd, "{ "); @@ -468,7 +480,7 @@ fn wwitype(fd: i32, t: *syntax.node) void = { wputs(fd, m.str); if (m.lhs != nil) { wputs(fd, " = "); - wwiexpr(fd, m.lhs); + wwiexpr(c, fd, owner, source, m.lhs); }; m = m.next; }; @@ -489,7 +501,7 @@ fn wwiattrrelevant(nm: str) bool = { 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; for (a != nil) { 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; for (arg != nil) { if (arg != a.list) { wputs(fd, ", "); }; - wwiexpr(fd, arg); + wwiexpr(c, fd, owner, d.sourceid, arg); arg = arg.next; }; 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) { - wwiattrs(fd, d); + wwiattrs(c, fd, owner, d); if (d.exported != 0) { wputs(fd, "export fn "); } else { wputs(fd, "fn "); }; wputs(fd, d.str); @@ -521,25 +533,25 @@ fn wwidecl(fd: i32, d: *syntax.node) void = { let p: *syntax.node = d.list; for (p != nil) { if (p != d.list) { wputs(fd, ", "); }; - wwiparam(fd, p); + wwiparam(c, fd, owner, d.sourceid, p); p = p.next; }; wputs(fd, ") "); - wwitype(fd, d.lhs); + wwitype(c, fd, owner, d.sourceid, d.lhs); wputs(fd, ";\n"); } else { if (d.kind == syntax.nkind.N_TYPEDECL) { if (d.exported != 0) { wputs(fd, "export type "); } else { wputs(fd, "type "); }; wputs(fd, d.str); wputs(fd, " = "); - wwitype(fd, d.lhs); + wwitype(c, fd, owner, d.sourceid, d.lhs); wputs(fd, ";\n"); } else { if (d.kind == syntax.nkind.N_DEF) { if (d.exported != 0) { wputs(fd, "export def "); } else { wputs(fd, "def "); }; wputs(fd, d.str); 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 // (#52): emit a value-LESS prototype `export def X: T;`. 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"); } else { wputs(fd, " = "); - wwiexpr(fd, d.rhs); + wwiexpr(c, fd, owner, d.sourceid, d.rhs); wputs(fd, ";\n"); }; } 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"); os.exit(1); }; - wwitype(fd, d.lhs); + wwitype(c, fd, owner, d.sourceid, d.lhs); wputs(fd, ";\n"); };};};}; }; @@ -671,16 +683,6 @@ fn wwifactvaluesym(c: *checker, owner: str, source: i32, }; 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; }; @@ -929,7 +931,8 @@ fn wwiemitimports(fd: i32, file: *syntax.node, owner: str, source: i32, let i: i32 = 0; for (i < nuse) { 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]; }; i += 1; @@ -995,7 +998,8 @@ fn wwiemitprimarysection(c: *checker, fd: i32, file: *syntax.node, let i: i32 = 0; for (i < fs.nprivate) { if (fs.privatenodes[i].sourceid == source) { - wwidecl(fd, fs.privatenodes[i]); + let primaryowner: str; + wwidecl(c, fd, primaryowner, fs.privatenodes[i]); }; i += 1; }; @@ -1004,13 +1008,19 @@ fn wwiemitprimarysection(c: *checker, fd: i32, file: *syntax.node, for (d != nil) { if (wwiprimary(d) && d.sourceid == source && 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; }; }; i = 0; 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; }; }; @@ -1134,23 +1144,13 @@ fn wwiemit(c: *checker, file: *syntax.node, path: str) i32 = { if (lastmod.len == 0 || !syntax.streq(lastmod, mod) || lastsource != source) { wputs(fd, "//ww:module "); wputs(fd, mod); wputs(fd, "\n"); - let factpkg: str = fs.factnodes[fi].pkgname; - if (factpkg.len == 0) { - 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"); + wputs(fd, "package "); wwicanonicalalias(fd, mod); + wputs(fd, ";\n"); wwiemitimports(fd, file, mod, source, true); lastmod = mod; lastsource = source; }; - wwidecl(fd, fs.factnodes[fi]); + wwidecl(c, fd, mod, fs.factnodes[fi]); fi += 1; }; diff --git a/selfhost/cmd/ww/main.ww b/selfhost/cmd/ww/main.ww index ac2155f6..94cfce4a 100644 --- a/selfhost/cmd/ww/main.ww +++ b/selfhost/cmd/ww/main.ww @@ -2853,7 +2853,11 @@ fn sepresolvesourceimport(g: *sepgraph, context: i32, name: *u8, }; 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; }; r = strings.compare(a.file, b.file): i32; if (r != 0) { return r; }; @@ -2977,12 +2981,14 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, context: i32, ui = 0; for (ui < nuse) { u = uses[ui]; - let idp: *u8 = u.usepath.ptr; - let idn: u64 = u.usepath.len: u64; - if (reservedimport(u.usepath)) { + let sourcepath: str = u.usesource; + if (sourcepath.len == 0) { sourcepath = u.usepath; }; + let idp: *u8 = sourcepath.ptr; + let idn: u64 = sourcepath.len: u64; + if (reservedimport(sourcepath)) { cerrpos(u.file, u.line, u.col); cerr(": error: package path "); - cerr(u.usepath); cerr(" is reserved\n"); + cerr(sourcepath); cerr(" is reserved\n"); return -1; }; let nm: []u8; @@ -3085,7 +3091,7 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, context: i32, let childcontext: i32 = sepchildcontextfor(g, context, resolved.entry, resolved.sourceroot); if (childcontext < 0 - || !sepbindadd(bindings, 'D': u8, u.usepath, di, + || !sepbindadd(bindings, 'D': u8, sourcepath, di, u.file, u.line, u.col) || !sepadddep(g, pi, di) || !sepchildrenadd(children, di, childcontext)) { @@ -3108,7 +3114,7 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, context: i32, cerr("\n"); return -1; } else { - if (!sepbindadd(bindings, 'I': u8, u.usepath, -1, + if (!sepbindadd(bindings, 'I': u8, sourcepath, -1, u.file, u.line, u.col)) { return -1; }; @@ -4360,14 +4366,14 @@ fn validatecommandoutputpath(out: *u8) i32 = { fn workdirstamptext(istest: i32, emitasm: i32) str = { if (istest != 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) { - 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 = { diff --git a/selfhost/cmd/wwdump/main.ww b/selfhost/cmd/wwdump/main.ww index 525cd04b..3e8c791d 100644 --- a/selfhost/cmd/wwdump/main.ww +++ b/selfhost/cmd/wwdump/main.ww @@ -97,27 +97,27 @@ export fn main(argc: i32, argv: **u8) i32 = { return 1; }; - let l: lex; - lexinit(&l, argstr(path), buf.ptr, sz: u64); + let l: syntax.lex; + syntax.lexinit(&l, argstr(path), buf.ptr, sz: u64); if (mode == 116) { // '-t' for (true) { - let t: tok; - lexnext(&l, &t); - tokprint(1i32, &t); - if (t.kind == tkind.TK_EOF) { break; }; - if (t.kind == tkind.TK_ERR) { break; }; + let t: syntax.tok; + syntax.lexnext(&l, &t); + syntax.tokprint(1i32, &t); + if (t.kind == syntax.tkind.TK_EOF) { break; }; + if (t.kind == syntax.tkind.TK_ERR) { break; }; }; } else { if (mode == 97) { // '-a' - let ps: parser; - parserinit(&ps, &l); - let f: *node = parsefile(&ps); - astprint(1i32, f); + let ps: syntax.parser; + syntax.parserinit(&ps, &l); + let f: *syntax.node = syntax.parsefile(&ps); + syntax.astprint(1i32, f); if (ps.errs > 0) { return 1; }; } else { if (mode == 114) { // '-r' — name resolve report - let ps: parser; - parserinit(&ps, &l); - let f: *node = parsefile(&ps); + let ps: syntax.parser; + syntax.parserinit(&ps, &l); + let f: *syntax.node = syntax.parsefile(&ps); // #52: gate the resolve report on parse-stage errors. Without // this a parse-errored decl is silently dropped from the AST // 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); if (checkrc != 0) { return 1; }; } else { if (mode == 99) { // '-c' — codegen / emit asm - let ps: parser; - parserinit(&ps, &l); - let f: *node = parsefile(&ps); + let ps: syntax.parser; + syntax.parserinit(&ps, &l); + let f: *syntax.node = syntax.parsefile(&ps); // #52: gate cgen on parse-stage errors BEFORE check/cgen. // A parse-errored decl is silently dropped from the AST; the // remaining file would otherwise emit asm with rc=0 (silent