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-
|
||||
|
||||
@@ -602,9 +602,17 @@ fn cgfn(c: *cgen, fn_: *node) void = {
|
||||
|
||||
// Emit the TEXT label via emitfnname so the def site picks up the
|
||||
// same skip rule (FFI / `main` / empty-module) and the same module
|
||||
// hint (this fn's own module) that the call sites use.
|
||||
// hint (this fn's own module) that the call sites use. M1 #32: the
|
||||
// ROOT-unit main (imported==0) is the bare `_start` entry — emit it
|
||||
// bare directly, mirroring collectmods' skip; without this its
|
||||
// fn_.nmod hint would fall through modlookupforfn's first-leaf match
|
||||
// onto an IMPORTED package's now-registered `pkg.main`.
|
||||
emitline("TEXT ");
|
||||
emitfnname(c, fn_.str, fn_.nmod);
|
||||
if (streq(fn_.str, "main") && fn_.imported == 0) {
|
||||
emitbytes(fn_.str.ptr, fn_.str.len: u64);
|
||||
} else {
|
||||
emitfnname(c, fn_.str, fn_.nmod);
|
||||
};
|
||||
emitline(",$");
|
||||
emitint(frame: i64);
|
||||
emitline("\n");
|
||||
|
||||
@@ -4306,10 +4306,10 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
// lhs.str is the explicit module hint so a same-leaf
|
||||
// def in another module (head of c.fnrets) can't shadow
|
||||
// the explicit qualifier (#17 N_DOT-arm omission audit).
|
||||
let frt: *node = fnretlookupmod(c, fld, lhs.str);
|
||||
let frt: *node = fnretlookupmod(c, fld, usehint(c, lhs.str));
|
||||
if (frt != nil) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitfnname(c, fld, lhs.str);
|
||||
emitfnname(c, fld, usehint(c, lhs.str));
|
||||
emitline("(SB), AX\n");
|
||||
return;
|
||||
};
|
||||
@@ -4321,7 +4321,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
// the explicit module hint — a 3rd-module qualifier
|
||||
// `alpha.MSG` from gamma needs alpha (not c.curmod)
|
||||
// to beat a head-of-c.defs beta.MSG collision (#11).
|
||||
let drhs: *node = deflookuprhsmod(c, fld, lhs.str);
|
||||
let drhs: *node = deflookuprhsmod(c, fld, usehint(c, lhs.str));
|
||||
if (drhs != nil) {
|
||||
if (drhs.kind == nkind.N_STRLIT) {
|
||||
let bytes: str = drhs.str;
|
||||
@@ -4342,11 +4342,11 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
// threads lhs.str via emitfnname.
|
||||
if (streq(mqop, "MOVQ")) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitsymnamehint(c, fld, lhs.str);
|
||||
emitsymnamehint(c, fld, usehint(c, lhs.str));
|
||||
emitline("(SB), AX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymnamehint(c, fld, lhs.str);
|
||||
emitsymnamehint(c, fld, usehint(c, lhs.str));
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\t");
|
||||
emitline(mqop);
|
||||
@@ -5226,10 +5226,10 @@ fn cgun(c: *cgen, n: *node) void = {
|
||||
// pre-#149 gap (file as #150-family).
|
||||
if (!isletvar(c, basenm) && !deflookup(c, basenm)) {
|
||||
let fld: str = opnd.str;
|
||||
let frt: *node = fnretlookupmod(c, fld, basenm);
|
||||
let frt: *node = fnretlookupmod(c, fld, usehint(c, basenm));
|
||||
if (frt != nil) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitfnname(c, fld, basenm);
|
||||
emitfnname(c, fld, usehint(c, basenm));
|
||||
emitline("(SB), AX\n");
|
||||
return;
|
||||
};
|
||||
@@ -5238,7 +5238,7 @@ fn cgun(c: *cgen, n: *node) void = {
|
||||
// &aa.v takes aa's global, not a
|
||||
// same-leaf collision.
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymnamehint(c, fld, basenm);
|
||||
emitsymnamehint(c, fld, usehint(c, basenm));
|
||||
emitline("(SB), AX\n");
|
||||
return;
|
||||
};
|
||||
@@ -8227,7 +8227,7 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
hint.len = 0;
|
||||
if (callee.lhs != nil) {
|
||||
if (callee.lhs.kind == nkind.N_IDENT) {
|
||||
hint = callee.lhs.str;
|
||||
hint = usehint(c, callee.lhs.str);
|
||||
};
|
||||
};
|
||||
emitfnname(c, calleename, hint);
|
||||
|
||||
@@ -2167,10 +2167,14 @@ fn structlookup(c: *cgen, name: str) *structinfo = {
|
||||
let leaf: str;
|
||||
leaf.ptr = name.ptr + ((i + 1): u64);
|
||||
leaf.len = name.len - (i + 1);
|
||||
// M1 #22: the embedded qualifier (`utf8`) is the use ALIAS;
|
||||
// the struct's smod is now the dotted import PATH
|
||||
// (`encoding.utf8`). Map alias→path before comparing.
|
||||
let pkgmod: str = usehint(c, pkg);
|
||||
let b: *structinfo = c.structs;
|
||||
for (b != nil) {
|
||||
if (streq(b.sname, leaf)) {
|
||||
if (streq(b.smod, pkg)) {
|
||||
if (streq(b.smod, pkgmod)) {
|
||||
return b;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -164,14 +164,48 @@ fn declmod(file: *node, d: *node) str = {
|
||||
if (file == nil) { return empty; };
|
||||
let u: *node = file.list;
|
||||
for (u != nil) {
|
||||
// M1 #22: a decl is imported iff some `use` directive's full
|
||||
// dotted import path equals the decl's module (now the path).
|
||||
// Single-level packages have usepath == leaf so this is
|
||||
// unchanged; nested (`encoding.utf8`) match here, not on leaf.
|
||||
if (u.kind == nkind.N_USE) {
|
||||
if (streq(u.str, d.nmod)) { return d.nmod; };
|
||||
if (streq(u.usepath, d.nmod)) { return d.nmod; };
|
||||
};
|
||||
u = u.next;
|
||||
};
|
||||
return empty;
|
||||
};
|
||||
|
||||
// usepath — 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 codegen hint (M1 #22). Single-level
|
||||
// packages have usepath == alias so the result is unchanged. The
|
||||
// current tree has one occurrence per leaf, so the map is unambiguous.
|
||||
fn usepathfor(file: *node, alias: str) str = {
|
||||
let empty: str;
|
||||
if (file == nil) { return empty; };
|
||||
if (alias.len == 0) { return empty; };
|
||||
let u: *node = file.list;
|
||||
for (u != nil) {
|
||||
if (u.kind == nkind.N_USE) {
|
||||
if (streq(u.str, alias)) {
|
||||
if (u.usepath.len != 0) { return u.usepath; };
|
||||
return u.str;
|
||||
};
|
||||
};
|
||||
u = u.next;
|
||||
};
|
||||
return empty;
|
||||
};
|
||||
|
||||
// modkeyfor — usepathfor with leaf-alias fallback: the module key for a
|
||||
// path-keyed scopelookupinmodule given the alias the user wrote (M1 #22).
|
||||
fn modkeyfor(c: *checker, alias: str) str = {
|
||||
let mk: str = usepathfor(c.file, alias);
|
||||
if (mk.len == 0) { return alias; };
|
||||
return mk;
|
||||
};
|
||||
|
||||
// srcimports — does the source file that contributed decl-module
|
||||
// `modtag` carry `use <name>;`? Mirrors cstage's src_imports —
|
||||
// `modtag.len == 0` means primary, matching declmod's empty-str
|
||||
@@ -188,7 +222,7 @@ fn srcimports(file: *node, modtag: str, name: str) bool = {
|
||||
// module bareword and lib/fmt's own
|
||||
// `fn bsprintf(fmt: str, ...)` is not a shadow.
|
||||
if (u.nmod.len > 0) {
|
||||
if (streq(u.nmod, u.str)) {
|
||||
if (streq(u.nmod, u.usepath)) {
|
||||
u = u.next;
|
||||
continue;
|
||||
};
|
||||
@@ -277,7 +311,9 @@ fn installdecl(c: *checker, file: *node, d: *node) void = {
|
||||
// pulls lack import->file->symbol provenance). Message byte-identical
|
||||
// to cstage check.c.
|
||||
if (k == nkind.N_USE) {
|
||||
if (mod.len != 0 && streq(nm, mod)) {
|
||||
// M1 #22: self-import ⟺ the imported path equals the use's own
|
||||
// (owning) module path. Compares paths, not leaves.
|
||||
if (mod.len != 0 && streq(d.usepath, mod)) {
|
||||
cerr("self-import: package '"); cerr(mod);
|
||||
cerr("' cannot import itself\n"); c.errs += 1i32;
|
||||
};
|
||||
@@ -486,7 +522,7 @@ fn resolvewalk(c: *checker, n: *node) void = {
|
||||
let leaf: str;
|
||||
leaf.ptr = nm.ptr + (dot + 1): u64;
|
||||
leaf.len = nm.len - (dot + 1);
|
||||
s = scopelookupinmodule(c.cur, head, leaf);
|
||||
s = scopelookupinmodule(c.cur, modkeyfor(c, head), leaf);
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -884,7 +920,7 @@ fn aliassym(c: *checker, n: *node) *sym = {
|
||||
let leaf: str;
|
||||
leaf.ptr = nm.ptr + ((dotidx + 1): u64);
|
||||
leaf.len = nm.len - dotidx - 1;
|
||||
s = scopelookupinmodule(c.cur, head, leaf);
|
||||
s = scopelookupinmodule(c.cur, modkeyfor(c, head), leaf);
|
||||
} else {
|
||||
// #53: same-module preference. Mirrors cstage
|
||||
// cmd/wcc/check.c:66 scope_lookup_prefer. Without this,
|
||||
@@ -1135,7 +1171,7 @@ fn scruttype(c: *checker, e: *node) *node = {
|
||||
if (e.kind == nkind.N_DOT) {
|
||||
if (e.lhs == nil) { return nil; };
|
||||
if (e.lhs.kind != nkind.N_IDENT) { return nil; };
|
||||
let s: *sym = scopelookupinmodule(c.cur, e.lhs.str, e.str);
|
||||
let s: *sym = scopelookupinmodule(c.cur, modkeyfor(c, e.lhs.str), e.str);
|
||||
if (s == nil) { return nil; };
|
||||
if (s.decl == nil) { return nil; };
|
||||
return s.decl.lhs;
|
||||
@@ -1674,7 +1710,7 @@ fn evaldefconst(c: *checker, n: *node, out: *u64, depth: i32) bool = {
|
||||
if (k == nkind.N_DOT) {
|
||||
if (n.lhs == nil) { return false; };
|
||||
if (n.lhs.kind != nkind.N_IDENT) { return false; };
|
||||
let s: *sym = scopelookupinmodule(c.cur, n.lhs.str, n.str);
|
||||
let s: *sym = scopelookupinmodule(c.cur, modkeyfor(c, n.lhs.str), n.str);
|
||||
if (s == nil) { return false; };
|
||||
if (s.skind != skind.SK_DEF) { return false; };
|
||||
if (s.decl == nil) { return false; };
|
||||
@@ -2791,7 +2827,7 @@ fn unoptype(c: *checker, e: *node) *node = {
|
||||
// cstage's actual acceptance reason.
|
||||
if (e.lhs.kind == nkind.N_DOT) {
|
||||
if (e.lhs.lhs != nil && e.lhs.lhs.kind == nkind.N_IDENT) {
|
||||
let fs: *sym = scopelookupinmodule(c.cur, e.lhs.lhs.str, e.lhs.str);
|
||||
let fs: *sym = scopelookupinmodule(c.cur, modkeyfor(c, e.lhs.lhs.str), e.lhs.str);
|
||||
if (fs != nil) {
|
||||
if (fs.skind == skind.SK_FN) {
|
||||
if (fs.decl != nil) {
|
||||
@@ -3449,7 +3485,7 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
|
||||
// global-leaf path) and harec check_autodereference
|
||||
// (ref/harec/src/check.c:1566-1581).
|
||||
if (ms != nil && (ms.skind == skind.SK_USE || ms.use_alias != 0i32)) {
|
||||
s = scopelookupinmodule(c.cur, callee.lhs.str, nm);
|
||||
s = scopelookupinmodule(c.cur, modkeyfor(c, callee.lhs.str), nm);
|
||||
};
|
||||
};
|
||||
if (s != nil) { if (s.skind == skind.SK_FN) { if (s.decl != nil) {
|
||||
@@ -3509,7 +3545,7 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
|
||||
// qualified resolution and the lenient checker policy
|
||||
// keeps the silent miss documented at scruttype L656.
|
||||
if (ms.skind == skind.SK_USE || ms.use_alias != 0i32) {
|
||||
let fs: *sym = scopelookupinmodule(c.cur, lhsn.str, e.str);
|
||||
let fs: *sym = scopelookupinmodule(c.cur, modkeyfor(c, lhsn.str), e.str);
|
||||
if (fs != nil) { if (fs.decl != nil) {
|
||||
// #34: a module-qualified bare fn rvalue `mod.fn` types as
|
||||
// its FN TYPE (twin of the N_IDENT arm, :2688); decl.lhs is
|
||||
@@ -5366,7 +5402,7 @@ fn calleefndecl(c: *checker, callee: *node) *node = {
|
||||
};
|
||||
if (ms != nil) {
|
||||
if (ms.skind == skind.SK_USE || ms.use_alias != 0i32) {
|
||||
let fs: *sym = scopelookupinmodule(c.cur, callee.lhs.str, callee.str);
|
||||
let fs: *sym = scopelookupinmodule(c.cur, modkeyfor(c, callee.lhs.str), callee.str);
|
||||
if (fs != nil) {
|
||||
if (fs.skind == skind.SK_FN) { return fs.decl; };
|
||||
};
|
||||
@@ -6320,29 +6356,25 @@ export fn checkfile(c: *checker, file: *node) void = {
|
||||
d = d.next;
|
||||
};
|
||||
|
||||
// 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 in installtop
|
||||
// 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.
|
||||
// Twin of cmd/wcc/check.c.
|
||||
// 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. 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. Twin of cmd/wcc/check.c.
|
||||
let firstmain: *node = nil;
|
||||
let mm: *node = file.list;
|
||||
for (mm != nil) {
|
||||
let ismain: bool = (mm.kind == nkind.N_FNDECL
|
||||
|| mm.kind == nkind.N_LET || mm.kind == nkind.N_DEF
|
||||
|| mm.kind == nkind.N_TYPEDECL) && streq(mm.str, "main");
|
||||
if (ismain) {
|
||||
if (ismain && mm.imported == 0) {
|
||||
if (firstmain == nil) {
|
||||
firstmain = mm;
|
||||
} else {
|
||||
cerr(mm.file);
|
||||
cerr(": error: duplicate top-level main: only the entry main may exist (task #32)\n");
|
||||
cerr(": error: duplicate entry main: only one root main may exist (#32)\n");
|
||||
c.errs += 1;
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user