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:
@@ -123,10 +123,13 @@ fn aliaslookup(c: *cgen, name: str) *node = {
|
||||
let leaf: str;
|
||||
leaf.ptr = name.ptr + ((i + 1): u64);
|
||||
leaf.len = name.len - (i + 1);
|
||||
// M1 #22: map the embedded use ALIAS (`utf8`) to the
|
||||
// dotted import PATH the decl's module now carries.
|
||||
let pkgmod: str = usehint(c, pkg);
|
||||
let b: *aliasent = c.aliases;
|
||||
for (b != nil) {
|
||||
if (streq(b.aname, leaf)) {
|
||||
if (streq(b.amod, pkg)) {
|
||||
if (streq(b.amod, pkgmod)) {
|
||||
return b.target;
|
||||
};
|
||||
};
|
||||
@@ -311,10 +314,13 @@ fn enumlookup(c: *cgen, name: str) *enumtype = {
|
||||
let leaf: str;
|
||||
leaf.ptr = name.ptr + ((i + 1): u64);
|
||||
leaf.len = name.len - (i + 1);
|
||||
// M1 #22: map the embedded use ALIAS (`utf8`) to the
|
||||
// dotted import PATH the decl's module now carries.
|
||||
let pkgmod: str = usehint(c, pkg);
|
||||
let b: *enumtype = c.enums;
|
||||
for (b != nil) {
|
||||
if (streq(b.ename, leaf)) {
|
||||
if (streq(b.emod, pkg)) {
|
||||
if (streq(b.emod, pkgmod)) {
|
||||
return b;
|
||||
};
|
||||
};
|
||||
@@ -485,6 +491,9 @@ type cgen = struct {
|
||||
enums: *enumtype,
|
||||
mods: *modent, // fn (any export status) + non-exported
|
||||
// let/def/type decls → originating module
|
||||
uses: *modent, // M1 #22: N_USE alias → dotted import path,
|
||||
// for the qualified-ref codegen hint
|
||||
// (mname=alias, nmod=path)
|
||||
lets: *letvar, // top-level mutable scalar `let` bindings
|
||||
fnname: str,
|
||||
curmod: str, // current fn's `// MODULE: foo` directive (len=0
|
||||
@@ -2860,7 +2869,7 @@ fn emittuplerowrelocs(c: *cgen, name: str, module: str, backing: bool, rowoff: i
|
||||
// the leaf with the MODULE ident; same-module `&fn`
|
||||
// stays on curmod.
|
||||
if (ev.lhs.kind == nkind.N_DOT) {
|
||||
emitfnname(c, ev.lhs.str, ev.lhs.lhs.str);
|
||||
emitfnname(c, ev.lhs.str, usehint(c, ev.lhs.lhs.str));
|
||||
} else {
|
||||
emitfnname(c, ev.lhs.str, c.curmod);
|
||||
};
|
||||
@@ -3047,7 +3056,7 @@ fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
// leaf with the MODULE ident; same-module `&fn`
|
||||
// stays on curmod.
|
||||
if (r.lhs.kind == nkind.N_DOT) {
|
||||
emitfnname(c, r.lhs.str, r.lhs.lhs.str);
|
||||
emitfnname(c, r.lhs.str, usehint(c, r.lhs.lhs.str));
|
||||
} else {
|
||||
emitfnname(c, r.lhs.str, c.curmod);
|
||||
};
|
||||
@@ -3518,11 +3527,24 @@ fn fnretlookup(c: *cgen, name: str) *node = {
|
||||
// the scrutinee tagged type to `(rune | done)` — flatvariantidx then
|
||||
// can't see arms 2/3 and collapses them onto tag 0 (task #31).
|
||||
fn fnretlookupmod(c: *cgen, name: str, mod: str) *node = {
|
||||
if (mod.len > 0) {
|
||||
// M1 #22 (#199b): the qualifier may be the import ALIAS the user
|
||||
// wrote (`utf8`); fn decls register f.fmod under the dotted import
|
||||
// PATH (`encoding.utf8`). Map alias->path so a nested-package callee
|
||||
// matches its own module instead of falling back to the name-only
|
||||
// pass — which a same-leaf caller-module fn (e.g. strings.next vs
|
||||
// utf8.next) otherwise wins, resolving a match scrutinee to the
|
||||
// caller's union and collapsing arms 2+. usehint is idempotent on a
|
||||
// path / c.curmod (returns the input when no `use` matches), so the
|
||||
// already-mapped callers (cgenexpr.ww:4309/5229) and the bare-ident
|
||||
// c.curmod callers are unaffected. The choke-point twin of the
|
||||
// struct/alias/enum usehint splitters (cgen.ww:128/319,
|
||||
// cgenutil.ww:2173) — closes the whole fnret class by construction.
|
||||
let mk: str = usehint(c, mod);
|
||||
if (mk.len > 0) {
|
||||
let f: *fnret = c.fnrets;
|
||||
for (f != nil) {
|
||||
if (streq(f.fname, name)) {
|
||||
if (streq(f.fmod, mod)) { return f.rtype; };
|
||||
if (streq(f.fmod, mk)) { return f.rtype; };
|
||||
};
|
||||
f = f.frnext;
|
||||
};
|
||||
@@ -3582,11 +3604,15 @@ fn samemodfn(c: *cgen, name: str) bool = {
|
||||
// matching module is registered — mirrors aliaslookup's two-pass shape
|
||||
// (cgen.ww:75, fixed in #27).
|
||||
fn fnparamslookupmod(c: *cgen, name: str, mod: str) *node = {
|
||||
if (mod.len > 0) {
|
||||
// M1 #22 (#199b): map import alias -> dotted path, identical to
|
||||
// fnretlookupmod (the param-side twin). usehint is idempotent on a
|
||||
// path / c.curmod so existing callers are unaffected.
|
||||
let mk: str = usehint(c, mod);
|
||||
if (mk.len > 0) {
|
||||
let f: *fnret = c.fnrets;
|
||||
for (f != nil) {
|
||||
if (streq(f.fname, name)) {
|
||||
if (streq(f.fmod, mod)) { return f.params; };
|
||||
if (streq(f.fmod, mk)) { return f.params; };
|
||||
};
|
||||
f = f.frnext;
|
||||
};
|
||||
@@ -3766,9 +3792,17 @@ type modent = struct {
|
||||
|
||||
fn collectmods(c: *cgen, file: *node) void = {
|
||||
c.mods = nil;
|
||||
c.uses = nil;
|
||||
if (file == nil) { return; };
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
// M1 #22: record alias→path for the qualified-ref hint.
|
||||
if (d.kind == nkind.N_USE) {
|
||||
if (d.usepath.len > 0) {
|
||||
let um: *modent = alloc(modent{mname=d.str, nmod=d.usepath, mnext=c.uses})!;
|
||||
c.uses = um;
|
||||
};
|
||||
};
|
||||
// Mirror collectfnrets' shape exactly (plain prepend in one
|
||||
// branch). Earlier nested-if/early-return variants tickled a
|
||||
// wwstage cgen bug that dropped most prepends.
|
||||
@@ -3786,7 +3820,9 @@ fn collectmods(c: *cgen, file: *node) void = {
|
||||
a = a.next;
|
||||
};
|
||||
if (!isffi) {
|
||||
if (!streq(d.str, "main")) {
|
||||
// M1 #32: the ROOT main (imported==0) stays bare;
|
||||
// an IMPORTED `fn main` mangles on its path.
|
||||
if (!streq(d.str, "main") || d.imported != 0) {
|
||||
let m: *modent = alloc(modent{mname=d.str, nmod=d.nmod, mnext=c.mods})!;
|
||||
c.mods = m;
|
||||
};
|
||||
@@ -3833,6 +3869,19 @@ fn modlookup(c: *cgen, name: str) str = {
|
||||
return empty;
|
||||
};
|
||||
|
||||
// usehint — M1 #22: map a qualified-ref alias (`utf8`) to its dotted
|
||||
// import path (`encoding.utf8`) so the codegen hint keys the path-keyed
|
||||
// mods map. For single-level packages alias == path (no-op). Returns the
|
||||
// alias unchanged when no matching `use` exists.
|
||||
fn usehint(c: *cgen, alias: str) str = {
|
||||
let m: *modent = c.uses;
|
||||
for (m != nil) {
|
||||
if (streq(m.mname, alias)) { return m.nmod; };
|
||||
m = m.mnext;
|
||||
};
|
||||
return alias;
|
||||
};
|
||||
|
||||
// modlookupforfn — hint-aware lookup for fn names. Walks c.mods
|
||||
// preferring entries where module matches `hint`; falls back to the
|
||||
// first leaf-name match when nothing matches the hint (legacy single-
|
||||
|
||||
Reference in New Issue
Block a user