Switch symbol mangling from the import leaf clause to the full dotted import path for directory packages; single-file imports keep package-clause mangling (isdir-gate: imported<=>directory-import). The root build unit's fn main stays bare, every other top-level decl mangles, closing #31's duplicate-main hazard by construction (#32). Both stages, byte-identical. Single commit, not split: the bare rename (f244af3) is red on its own because it unmasks cross-module resolution gaps that do not reproduce pre-M1, so the fixes are intrinsic to making the rename correct. Included: wwstage fnret/fnparamslookupmod map import alias->path (#199b cross-module union-variant scrutinee resolved the wrong fn's union); cstage use_path prefers the referencing module's import for an ambiguous leaf alias (sha256 crypto.math vs strconv math). Tests table-driven: 989_m1mangle_run/_sym, 989_m1union_run (gate-visible per-arm exit codes + cs==ww byte-id).
This commit is contained in:
@@ -60,6 +60,7 @@ lookup_builtin(const char *name)
|
||||
}
|
||||
|
||||
static const char *decl_mod(Node *file, Node *d);
|
||||
static const char *use_path(Node *file, const char *curmod, const char *alias);
|
||||
static void resolve_typedecl(Checker *c, Node *d);
|
||||
|
||||
static Type *
|
||||
@@ -84,9 +85,15 @@ resolve_typename(Checker *c, Node *n)
|
||||
size_t hl = (size_t)(dot - nm);
|
||||
if (hl < sizeof head) memcpy(head, nm, hl);
|
||||
Sym *m = scope_lookup(c->cur, head);
|
||||
if (m && (m->kind == SK_USE || m->use_alias))
|
||||
s = scope_lookup_in_module(c->cur, head,
|
||||
if (m && (m->kind == SK_USE || m->use_alias)) {
|
||||
/* M1 #22: map the qualifier alias to its dotted
|
||||
* import path (symbols are path-keyed). */
|
||||
const char *mk = use_path(c->file, c->cur_mod,
|
||||
head);
|
||||
if (mk == NULL) mk = head;
|
||||
s = scope_lookup_in_module(c->cur, mk,
|
||||
dot + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (s == NULL || s->kind != SK_TYPE)
|
||||
@@ -599,7 +606,10 @@ eval_def_const(Checker *c, Node *n, u64 *out, int depth)
|
||||
}
|
||||
case N_DOT: {
|
||||
if (n->lhs == NULL || n->lhs->kind != N_IDENT) return 0;
|
||||
Sym *s = scope_lookup_in_module(c->cur, n->lhs->str, n->str);
|
||||
/* M1 #22: map the qualifier alias to its dotted import path. */
|
||||
const char *mk = use_path(c->file, c->cur_mod, n->lhs->str);
|
||||
if (mk == NULL) mk = n->lhs->str;
|
||||
Sym *s = scope_lookup_in_module(c->cur, mk, n->str);
|
||||
if (s == NULL || s->kind != SK_DEF ||
|
||||
s->decl == NULL || s->decl->rhs == NULL)
|
||||
return 0;
|
||||
@@ -1343,8 +1353,14 @@ cexpr(Checker *c, Node *n)
|
||||
* same-leaf-name types from different
|
||||
* imports (`bufio.stream`/`io.stream`)
|
||||
* disambiguate to the right one. */
|
||||
/* M1 #22: symbols are keyed on the dotted
|
||||
* import path; map the alias the user wrote to
|
||||
* that path before looking up the leaf. */
|
||||
const char *mk = use_path(c->file, c->cur_mod,
|
||||
n->lhs->str);
|
||||
if (mk == NULL) mk = n->lhs->str;
|
||||
Sym *fs = scope_lookup_in_module(c->cur,
|
||||
n->lhs->str, n->str);
|
||||
mk, n->str);
|
||||
if (fs)
|
||||
return n->type = fs->type;
|
||||
if (ms->kind == SK_USE) {
|
||||
@@ -2671,13 +2687,52 @@ decl_mod(Node *file, Node *d)
|
||||
{
|
||||
if (d == NULL || d->module == NULL || file == NULL) return NULL;
|
||||
for (Node *u = file->list; u; u = u->next) {
|
||||
if (u->kind == N_USE && u->str
|
||||
&& strcmp(u->str, d->module) == 0)
|
||||
/* M1 #22: a decl is imported iff some `use` directive's full
|
||||
* dotted import path equals the decl's module (now the path,
|
||||
* not the leaf). For single-level packages usepath == leaf so
|
||||
* this is unchanged; nested packages (`encoding.utf8`) match
|
||||
* here instead of on the bare leaf. */
|
||||
if (u->kind == N_USE && u->usepath
|
||||
&& strcmp(u->usepath, d->module) == 0)
|
||||
return d->module;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* use_path — map a `use` alias (leaf bareword the user writes, `utf8`)
|
||||
* to the full dotted import path it binds (`encoding.utf8`), for the
|
||||
* module-qualified resolution and the codegen hint (M1 #22, §2.4). For
|
||||
* single-level packages usepath == alias so the result is unchanged.
|
||||
*
|
||||
* The alias→path map is NOT file-global: two modules in the same
|
||||
* concatenated unit may bind the same leaf alias to different paths
|
||||
* (sha256's `import crypto.math` and strconv's `import math` both bind
|
||||
* alias `math`). The import declared in the SAME module as the
|
||||
* reference (`curmod`) is the authoritative one; preferring it closes
|
||||
* the cross-module mis-resolution a first-match scan caused. Falls back
|
||||
* to any matching alias when the referencing module has no own import
|
||||
* (single-occurrence case, unchanged). Returns NULL if no such `use`.
|
||||
*/
|
||||
static const char *
|
||||
use_path(Node *file, const char *curmod, const char *alias)
|
||||
{
|
||||
if (file == NULL || alias == NULL) return NULL;
|
||||
const char *any = NULL;
|
||||
for (Node *u = file->list; u; u = u->next) {
|
||||
if (u->kind != N_USE || u->str == NULL
|
||||
|| strcmp(u->str, alias) != 0)
|
||||
continue;
|
||||
const char *p = u->usepath ? u->usepath : u->str;
|
||||
int same = (u->module == NULL) ? (curmod == NULL)
|
||||
: (curmod != NULL && strcmp(u->module, curmod) == 0);
|
||||
if (same)
|
||||
return p;
|
||||
if (any == NULL) any = p;
|
||||
}
|
||||
return any;
|
||||
}
|
||||
|
||||
/*
|
||||
* resolve_typedecl — resolve d's body into its installed TY_NAMED
|
||||
* placeholder. Reached from check_file's typedecl pass AND on demand
|
||||
@@ -2739,7 +2794,7 @@ src_imports(Node *file, const char *modtag, const char *name)
|
||||
* even though its module tag is also "fmt"; that directive
|
||||
* doesn't introduce a foreign module bareword and lib/fmt's
|
||||
* own `fn bsprintf(fmt: str, ...)` is not a shadow of it. */
|
||||
if (u->module && u->str && strcmp(u->module, u->str) == 0)
|
||||
if (u->module && u->usepath && strcmp(u->module, u->usepath) == 0)
|
||||
continue;
|
||||
/* decl_mod normalises the raw `// MODULE:` tag back to NULL
|
||||
* for primary-source N_USEs (the primary's own tag won't
|
||||
@@ -2817,7 +2872,11 @@ check_file(Checker *c, Node *file)
|
||||
* (b)/(d) membership DEFERRED to task #8 (filename-
|
||||
* keyed pulls lack import->file->symbol provenance). */
|
||||
const char *owner = decl_mod(file, d);
|
||||
if (owner && owner[0] && strcmp(d->str, owner) == 0)
|
||||
/* M1 #22: self-import ⟺ the imported path equals the
|
||||
* use's own (owning) module path. Compares paths, not
|
||||
* leaves, so nested packages are caught too. */
|
||||
if (owner && owner[0] && d->usepath
|
||||
&& strcmp(d->usepath, owner) == 0)
|
||||
err(c, d->pos, "self-import: package "
|
||||
"'%s' cannot import itself", owner);
|
||||
Sym *prev = scope_lookup_local(c->cur, d->str);
|
||||
@@ -2962,16 +3021,14 @@ check_file(Checker *c, Node *file)
|
||||
}
|
||||
c->cur_mod = NULL;
|
||||
|
||||
/* Program-global, name-only, cross-module uniqueness on `main`.
|
||||
* `main` lowers to ONE bare entry symbol, so a second top-level
|
||||
* decl named `main` (any kind, any package) collides with the
|
||||
* entry at link time — today a silent segfault / link-fail in
|
||||
* both stages. The (name, module) duplicate rejects above read a
|
||||
* cross-package `foo.main` and the bare entry as distinct, so they
|
||||
* miss this. Correct multi-main mangling (entry stays bare, the
|
||||
* rest qualify) is deferred (task #32); reject loudly meanwhile
|
||||
* (rule 7). Walks USER decls only — runs before the -T synth main
|
||||
* is appended below — so a hosted-test build never false-counts. */
|
||||
/* Program-global uniqueness on the ENTRY `main`. M1 #32: the entry
|
||||
* is the ROOT-unit main (imported==0) — it alone lowers to the bare
|
||||
* `main` symbol w6l's _start calls. An IMPORTED package's `main`
|
||||
* (imported==1) mangles on its path (`foo.bar.main`) and may coexist
|
||||
* — closing the old dup-main collision by construction (#31). Two
|
||||
* ROOT entries still collide on the bare symbol → reject loud (rule
|
||||
* 7). Walks USER decls only — runs before the -T synth main is
|
||||
* appended below — so a hosted-test build never false-counts. */
|
||||
{
|
||||
Node *firstmain = NULL;
|
||||
for (Node *d = file->list; d; d = d->next) {
|
||||
@@ -2980,12 +3037,14 @@ check_file(Checker *c, Node *file)
|
||||
if (d->kind != N_FNDECL && d->kind != N_LET
|
||||
&& d->kind != N_DEF && d->kind != N_TYPEDECL)
|
||||
continue;
|
||||
if (d->imported)
|
||||
continue;
|
||||
if (firstmain == NULL) {
|
||||
firstmain = d;
|
||||
continue;
|
||||
}
|
||||
err(c, d->pos, "duplicate top-level main: only the "
|
||||
"entry main may exist (task #32)");
|
||||
err(c, d->pos, "duplicate entry main: only one root "
|
||||
"main may exist (#32)");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user