ww: separate package identity from declared name

This commit is contained in:
2026-08-14 03:07:58 +09:00
parent 028da6323e
commit 6efe9b70d4
37 changed files with 1678 additions and 886 deletions

View File

@@ -455,7 +455,9 @@ type cgen = struct {
// bare-IDENT call mangling — `frob()` from
// inside lib/foo binds to `foo.frob` even when
// other modules also export `frob`. Set in cgfn
// before walking the body.
// before walking the body.
cursource: i32, // lexical source-file scope of the current decl;
// selects its own import bindings.
fnret: *syntax.node, // declared return type of current fn (or nil)
looptop: i32,
loopendbuf: []str, // stack of end labels for break
@@ -1420,9 +1422,11 @@ fn letpreintern(c: *cgen, file: *syntax.node) void = {
// fn-ptr relocs — see the same value they did before; letpreintern
// itself only interns, so driving curmod here has no other effect.
let savedmod: str = c.curmod;
let savedsource: i32 = c.cursource;
let d: *syntax.node = file.list;
for (d != nil) {
c.curmod = d.nmod;
c.cursource = d.sourceid;
// #22 M3: skip imported deps so the strlit table (and its _S_
// sequence) is a pure function of THIS package's own decls. A
// dep's body initializer would intern here, but its `.wwi` (init
@@ -1596,6 +1600,7 @@ fn letpreintern(c: *cgen, file: *syntax.node) void = {
d = d.next;
};
c.curmod = savedmod;
c.cursource = savedsource;
};
// emitletdataw — DATAW directive per top-level `let` global.
@@ -2890,8 +2895,12 @@ fn emittupledata(c: *cgen, name: str, module: str, tt: *syntax.node, rhs: *synta
};
fn emitletdataw(c: *cgen, file: *syntax.node) void = {
let savedmod: str = c.curmod;
let savedsource: i32 = c.cursource;
let d: *syntax.node = file.list;
for (d != nil) {
c.curmod = d.nmod;
c.cursource = d.sourceid;
// #22 M3 THE ONE REAL GUARD: a `.wwi` dep value-global is
// initializer-less; emitting a DATAW for it would DUPLICATE the
// definition that lives in the dep's own .o → link collision.
@@ -3226,6 +3235,8 @@ fn emitletdataw(c: *cgen, file: *syntax.node) void = {
};
d = d.next;
};
c.curmod = savedmod;
c.cursource = savedsource;
};
// emitdefconstants — DATA directive per top-level fold-to-literal
@@ -3235,8 +3246,12 @@ fn emitletdataw(c: *cgen, file: *syntax.node) void = {
// N_UN(TK_MINUS, N_INTLIT) — the unary peel is exactly what the
// gate is for.
fn emitdefconstants(c: *cgen, file: *syntax.node) void = {
let savedmod: str = c.curmod;
let savedsource: i32 = c.cursource;
let d: *syntax.node = file.list;
for (d != nil) {
c.curmod = d.nmod;
c.cursource = d.sourceid;
// #22 M3: a `.wwi` dep def with DATA storage (int-fold / float /
// struct / array) must NOT re-emit — the dep's own .o owns the
// symbol. Str defs are inline-spliced (never emitted here), so
@@ -3369,6 +3384,8 @@ fn emitdefconstants(c: *cgen, file: *syntax.node) void = {
};
d = d.next;
};
c.curmod = savedmod;
c.cursource = savedsource;
};
// emitdatasection — DATA directives for every interned strlit.
@@ -3760,6 +3777,7 @@ 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
sourceid: i32, // owning lexical source-file scope for N_USE entries
mnext: *modent,
};
@@ -3772,7 +3790,7 @@ fn collectmods(c: *cgen, file: *syntax.node) void = {
// M1 #22: record alias→path for the qualified-ref hint.
if (d.kind == syntax.nkind.N_USE) {
if (d.usepath.len > 0) {
let um: *modent = alloc(modent{mname=d.str, nmod=d.usepath, omod=d.nmod, mnext=c.uses})!;
let um: *modent = alloc(modent{mname=d.str, nmod=d.usepath, omod=d.nmod, sourceid=d.sourceid, mnext=c.uses})!;
c.uses = um;
};
};
@@ -3811,7 +3829,7 @@ fn collectmods(c: *cgen, file: *syntax.node) void = {
// Explicit entry mode clears sepisdep independently of export
// production; every other package's main remains mangled.
if (!syntax.streq(d.str, "main") || d.imported != 0 || c.sepisdep != 0) {
let m: *modent = alloc(modent{mname=d.str, nmod=d.nmod, omod=d.nmod, mnext=c.mods})!;
let m: *modent = alloc(modent{mname=d.str, nmod=d.nmod, omod=d.nmod, sourceid=d.sourceid, mnext=c.mods})!;
c.mods = m;
};
};
@@ -3827,19 +3845,19 @@ fn collectmods(c: *cgen, file: *syntax.node) void = {
// retained until then (rule-11).
if (d.kind == syntax.nkind.N_DEF) {
if (d.nmod.len > 0) {
let m: *modent = alloc(modent{mname=d.str, nmod=d.nmod, omod=d.nmod, mnext=c.mods})!;
let m: *modent = alloc(modent{mname=d.str, nmod=d.nmod, omod=d.nmod, sourceid=d.sourceid, mnext=c.mods})!;
c.mods = m;
};
};
if (d.kind == syntax.nkind.N_TYPEDECL) {
if (d.nmod.len > 0) {
let m: *modent = alloc(modent{mname=d.str, nmod=d.nmod, omod=d.nmod, mnext=c.mods})!;
let m: *modent = alloc(modent{mname=d.str, nmod=d.nmod, omod=d.nmod, sourceid=d.sourceid, mnext=c.mods})!;
c.mods = m;
};
};
if (d.kind == syntax.nkind.N_LET) {
if (d.nmod.len > 0) {
let m: *modent = alloc(modent{mname=d.str, nmod=d.nmod, omod=d.nmod, mnext=c.mods})!;
let m: *modent = alloc(modent{mname=d.str, nmod=d.nmod, omod=d.nmod, sourceid=d.sourceid, mnext=c.mods})!;
c.mods = m;
};
};
@@ -3859,18 +3877,10 @@ 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.
//
// 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).
// usehint — map a declared default qualifier to its canonical import path so
// codegen mangles on package identity. It is source-file local: separate files
// may bind the same declared name to different paths. Raw non-package
// compilation retains its historical single-occurrence fallback.
fn usehint(c: *cgen, alias: str) str = {
let m: *modent = c.uses;
let any: str;
@@ -3878,8 +3888,12 @@ fn usehint(c: *cgen, alias: str) str = {
any.len = 0;
for (m != nil) {
if (syntax.streq(m.mname, alias)) {
if (syntax.streq(m.omod, c.curmod)) { return m.nmod; };
if (any.ptr == nil && any.len == 0) { any = m.nmod; };
if (m.sourceid == c.cursource && syntax.streq(m.omod, c.curmod)) {
return m.nmod;
};
if (c.sepmode == 0 && any.ptr == nil && any.len == 0) {
any = m.nmod;
};
};
m = m.mnext;
};