wcc/ww: module-scope the cgen mangle-hint (#40)

use_hint/usehint were unit-global first-leaf-match: two directory-
packages exporting the same fn leaf, each imported by a different module
aliasing the same bareword, mis-routed every qualified call to whichever
use was collected first. Identically wrong on both stages (byte-id-green
#263-class). Key the hint on (owner-module, alias) and prefer cur_mod,
mirroring the checker's use_path curmod-preference (55f54fb).

989_m1usehint_run: two same-leaf pick() across a.math/b.math, each
module's call routes to its own import (111/222) + cs.s==ww.s.
This commit is contained in:
2026-06-15 19:01:51 +09:00
parent f308818b4b
commit e9c11cb5ae
6 changed files with 364 additions and 44 deletions

View File

@@ -44986,6 +44986,7 @@ fn defisaddressable(c: *cgen, opnd: *node) bool = {
type modent = struct {
mname: str, // the bare ident as it appears in source
nmod: str, // the originating module (`// MODULE: foo`)
omod: str, // owning module of a `use` decl (#40); unused for mods
mnext: *modent,
};
@@ -44998,7 +44999,7 @@ fn collectmods(c: *cgen, file: *node) void = {
// 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})!;
let um: *modent = alloc(modent{mname=d.str, nmod=d.usepath, omod=d.nmod, mnext=c.uses})!;
c.uses = um;
};
};
@@ -45022,7 +45023,7 @@ fn collectmods(c: *cgen, file: *node) void = {
// 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})!;
let m: *modent = alloc(modent{mname=d.str, nmod=d.nmod, omod=d.nmod, mnext=c.mods})!;
c.mods = m;
};
};
@@ -45031,7 +45032,7 @@ fn collectmods(c: *cgen, file: *node) void = {
if (d.kind == nkind.N_DEF) {
if (d.exported == 0) {
if (d.nmod.len > 0) {
let m: *modent = alloc(modent{mname=d.str, nmod=d.nmod, mnext=c.mods})!;
let m: *modent = alloc(modent{mname=d.str, nmod=d.nmod, omod=d.nmod, mnext=c.mods})!;
c.mods = m;
};
};
@@ -45039,7 +45040,7 @@ fn collectmods(c: *cgen, file: *node) void = {
if (d.kind == nkind.N_TYPEDECL) {
if (d.exported == 0) {
if (d.nmod.len > 0) {
let m: *modent = alloc(modent{mname=d.str, nmod=d.nmod, mnext=c.mods})!;
let m: *modent = alloc(modent{mname=d.str, nmod=d.nmod, omod=d.nmod, mnext=c.mods})!;
c.mods = m;
};
};
@@ -45047,7 +45048,7 @@ fn collectmods(c: *cgen, file: *node) void = {
if (d.kind == nkind.N_LET) {
if (d.exported == 0) {
if (d.nmod.len > 0) {
let m: *modent = alloc(modent{mname=d.str, nmod=d.nmod, mnext=c.mods})!;
let m: *modent = alloc(modent{mname=d.str, nmod=d.nmod, omod=d.nmod, mnext=c.mods})!;
c.mods = m;
};
};
@@ -45072,12 +45073,27 @@ fn modlookup(c: *cgen, name: str) str = {
// 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.
//
// NOT file-global (#40): two modules in one unit may bind the same leaf
// alias to different paths (module one's `import a.math` vs module two's
// `import b.math`, both alias `math`). The `use` declared in the SAME
// module as the reference (c.curmod) is authoritative; preferring it
// routes each `math.pick()` to its own package. Falls back to any
// matching alias when c.curmod has no own import. Mirrors the checker's
// use_path curmod-preference (cstage check.c, M1 55f54fb).
fn usehint(c: *cgen, alias: str) str = {
let m: *modent = c.uses;
let any: str;
any.ptr = nil;
any.len = 0;
for (m != nil) {
if (streq(m.mname, alias)) { return m.nmod; };
if (streq(m.mname, alias)) {
if (streq(m.omod, c.curmod)) { return m.nmod; };
if (any.ptr == nil && any.len == 0) { any = m.nmod; };
};
m = m.mnext;
};
if (any.ptr != nil || any.len != 0) { return any; };
return alias;
};