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

@@ -3,7 +3,8 @@ package wcc;
import syntax;
export fn compilefile(file: *syntax.node, testmode: i32, testpackage: i32,
testmodule: str, sepmode: i32, wwiout: str, entrymode: i32) i32 = {
testmodule: str, testtarget: str, sepmode: i32, wwiout: str,
entrymode: i32) i32 = {
let tc: syntax.tctx;
syntax.typesinit(&tc);
let ck: checker;
@@ -11,6 +12,7 @@ export fn compilefile(file: *syntax.node, testmode: i32, testpackage: i32,
ck.istest = testmode;
ck.istestpackage = testpackage;
if (testmodule.len > 0) { ck.testmodule = testmodule; };
ck.testtarget = testtarget;
ck.sepmode = sepmode;
checkfile(&ck, file);
if (ck.errs > 0) { return 1; };

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;
};

View File

@@ -2,7 +2,6 @@ package wcc;
import os;
import syntax;
import strconv;
fn cgfnparams(c: *cgen, params: *syntax.node) void = {
let p: *syntax.node = params;
@@ -518,6 +517,7 @@ fn cgfn(c: *cgen, fn_: *syntax.node) void = {
cgeninit(c);
c.fnname = fn_.str;
c.curmod = fn_.nmod;
c.cursource = fn_.sourceid;
c.fnret = fn_.lhs;
// sret callee (#23): return type is plain TY_STRUCT > 24B.

View File

@@ -11,7 +11,6 @@ package wcc;
import os;
import syntax;
import strconv;
// cgfloatbits — materialise a float constant in X0: MOVQ the IEEE bits
// into AX, PUSH, MOVSD off the stack into X0. Shared by N_FLOATLIT (bits

View File

@@ -5,7 +5,6 @@ package wcc;
import os;
import syntax;
import strconv;
// slicewrap — synthesise an N_TSLICE node wrapping the given element
// type AST. Used by the Hare-style variadic path so the local entry

View File

@@ -23,6 +23,8 @@ type checker = struct {
istestpackage: i32, // validate/retain package-owned @test bodies and
// export compiler-private metadata; no entry synth
testmodule: str, // generated dispatcher support qualifier
testtarget: str, // canonical target path used only as the
// compiler-owned generated-main qualifier
sepmode: i32, // -c package compilation: imported interfaces are
// present, so absent members are hard export errors.
synthtestrun: *syntax.node, // exact generated support.run DOT; its
@@ -32,7 +34,9 @@ type checker = struct {
curmod: str, // importing-module bareword for the decl
// currently being walked; "" for primary
// compilation unit. Drives same-module
// preference in bare-leaf lookups.
// preference in bare-leaf lookups.
cursource: i32, // lexical source-file scope of the current decl;
// selects only that file's import bindings.
file: *syntax.node, // N_FILE root; used by checkmoduleshadow
// to consult the declaring source's own
// `use` directives.
@@ -154,6 +158,7 @@ fn declmod(file: *syntax.node, d: *syntax.node) str = {
if (d == nil) { return empty; };
if (d.nmod.len == 0) { return empty; };
if (file == nil) { return empty; };
if (d.imported != 0) { return d.nmod; };
let u: *syntax.node = file.list;
for (u != nil) {
// M1 #22: a decl is imported iff some `use` directive's full
@@ -168,17 +173,16 @@ fn declmod(file: *syntax.node, d: *syntax.node) str = {
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
// Only an import owned by the referencing package is visible; a matching
// usepath — map a source-file default qualifier (the imported package's
// declared name) to the full canonical import path it binds, for
// module-qualified resolution and codegen hint (M1 #22). Only an import owned
// by the referencing file is visible; a matching
// alias carried by a transitive interface is deliberately ignored.
fn usepathfor(file: *syntax.node, modtag: str, alias: str) str = {
fn usepathfor(file: *syntax.node, modtag: str, source: i32, alias: str) str = {
let empty: str;
if (file == nil) { return empty; };
if (alias.len == 0) { return empty; };
if (modtag.len != 0) {
if (source == 0 && modtag.len != 0) {
let (prefix, suffix) = strings.rcut(modtag, ".");
let leaf: str = suffix;
if (leaf.len == 0) { leaf = modtag; };
@@ -186,14 +190,15 @@ fn usepathfor(file: *syntax.node, modtag: str, alias: str) str = {
};
let u: *syntax.node = file.list;
for (u != nil) {
if (u.kind == syntax.nkind.N_USE) {
if (u.kind == syntax.nkind.N_USE && u.sourceid == source) {
if (syntax.streq(u.str, alias)) {
let um: str = declmod(file, u);
let same: bool = false;
if (modtag.len == 0) {
if (um.len == 0) { same = true; };
} else { if (syntax.streq(um, modtag)) { same = true; }; };
if (same) {
if (same) {
u.used = 1;
if (u.usepath.len != 0) { return u.usepath; };
return u.str;
};
@@ -207,19 +212,19 @@ fn usepathfor(file: *syntax.node, modtag: str, alias: str) str = {
// modkeyfor — the module key for a directly imported alias. Empty means
// the referencing package did not itself declare that import.
fn modkeyfor(c: *checker, alias: str) str = {
return usepathfor(c.file, c.curmod, alias);
return usepathfor(c.file, c.curmod, c.cursource, alias);
};
// 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
// return for primary-source decls.
fn srcimports(file: *syntax.node, modtag: str, name: str) bool = {
fn srcimports(file: *syntax.node, modtag: str, source: i32, name: str) bool = {
if (file == nil) { return false; };
if (name.len == 0) { return false; };
let u: *syntax.node = file.list;
for (u != nil) {
if (u.kind == syntax.nkind.N_USE) {
if (u.kind == syntax.nkind.N_USE && u.sourceid == source) {
// Skip self-imports: lib/fmt/fmt_test.ww carries
// `use fmt;` while its module tag is also "fmt".
// That directive doesn't introduce a foreign
@@ -251,11 +256,23 @@ fn srcimports(file: *syntax.node, modtag: str, name: str) bool = {
// symbol iff the referencing source package itself imported its module path.
fn directmodvisible(c: *checker, mod: str) bool = {
if (mod.len == 0) { return false; };
let (prefix, suffix) = strings.rcut(mod, ".");
let alias: str = suffix;
if (alias.len == 0) { alias = mod; };
let path: str = usepathfor(c.file, c.curmod, alias);
return path.len != 0 && syntax.streq(path, mod);
let u: *syntax.node = c.file.list;
for (u != nil) {
if (u.kind == syntax.nkind.N_USE && u.sourceid == c.cursource) {
let um: str = declmod(c.file, u);
let same: bool = false;
if (c.curmod.len == 0) { same = um.len == 0; }
else { same = syntax.streq(um, c.curmod); };
let path: str = u.usepath;
if (path.len == 0) { path = u.str; };
if (same && syntax.streq(path, mod)) {
u.used = 1;
return true;
};
};
u = u.next;
};
return false;
};
fn lookupvisible(c: *checker, name: str) *syntax.sym = {
@@ -268,7 +285,7 @@ fn lookupvisible(c: *checker, name: str) *syntax.sym = {
// Flat scope installation coalesces same-leaf N_USE entries. The
// source-owned alias map, not the retained marker's mod field, decides
// whether this package can use the qualifier.
if (usepathfor(c.file, c.curmod, name).len != 0) {
if (usepathfor(c.file, c.curmod, c.cursource, name).len != 0) {
let q: *syntax.scope = c.cur;
for (q != nil) {
let u: *syntax.sym = q.first;
@@ -354,9 +371,11 @@ fn builtintypename(name: str) bool = {
fn packageaccesserr(c: *checker, e: *syntax.node, pkg: str, member: str,
missing: bool) void = {
cerr(e.file); cerr(":");
cerr(strconv.i32tos(e.line, strconv.base.DEC)); cerr(":");
cerr(strconv.i32tos(e.col, strconv.base.DEC));
let at: *syntax.node = e;
for (at.kind == syntax.nkind.N_DOT && at.lhs != nil) { at = at.lhs; };
cerr(at.file); cerr(":");
cerr(strconv.i32tos(at.line, strconv.base.DEC)); cerr(":");
cerr(strconv.i32tos(at.col, strconv.base.DEC));
cerr(": error: package '"); cerr(pkg);
if (missing) {
cerr("' has no exported declaration '"); cerr(member); cerr("'\n");
@@ -396,7 +415,7 @@ fn checkmoduleshadow(c: *checker, name: str, kindstr: str) void = {
if (s != nil) { s = s.parent; };
};
if (!seen) { return; };
if (!srcimports(c.file, c.curmod, name)) { return; };
if (!srcimports(c.file, c.curmod, c.cursource, name)) { return; };
cerr(kindstr);
cerr(" '");
cerr(name);
@@ -437,10 +456,8 @@ fn installdecl(c: *checker, file: *syntax.node, d: *syntax.node) void = {
let k: syntax.nkind = d.kind;
let nm: str = d.str;
let mod: str = declmod(file, d);
// check-(c) self-import: a package may not import itself. Pure
// owner==leaf string compare, package-model-independent. check-(a)
// unused + (b)/(d) membership DEFERRED to task #8 (filename-keyed
// pulls lack import->file->symbol provenance). Message byte-identical
// A package may not import its own canonical owner. Import usage and
// membership are checked with source-file provenance. Message byte-identical
// to cstage check.c.
if (k == syntax.nkind.N_USE) {
// M1 #22: self-import ⟺ the imported path equals the use's own
@@ -2696,10 +2713,13 @@ fn tinfofornode(c: *checker, n: *syntax.node) *syntax.tinfo = {
// owns it. A direct dependency's fact may be demanded while
// walking a consumer-owned type; keeping the consumer module
// here can bind bare names in the fact to the wrong package.
let savedmod: str = c.curmod;
c.curmod = s.mod;
let under: *syntax.tinfo = tinfofornode(c, body);
c.curmod = savedmod;
let savedmod: str = c.curmod;
let savedsource: i32 = c.cursource;
c.curmod = s.mod;
if (s.decl != nil) { c.cursource = s.decl.sourceid; };
let under: *syntax.tinfo = tinfofornode(c, body);
c.curmod = savedmod;
c.cursource = savedsource;
// #62/#69: alias-root cycle (`type a = b;
// type b = a` / `type a = a`) — checked
// BEFORE clearing the flag so self-aliases
@@ -3821,6 +3841,9 @@ fn exprtype(c: *checker, e: *syntax.node, hint: *syntax.node) *syntax.node = {
return tn;
};
if (k == syntax.nkind.N_IDENT) {
// resolvewalk visits the dot receiver before its parent. Preserve the
// first causal undefined error just as cstage's cached cexpr does.
if (e.type_ == c.tc.tyerr: *void) { return nil; };
// #55: bare-leaf value-ident must prefer curmod. Flat-scope
// scopelookup bucket-walks and can bind a same-leaf symbol from
// the wrong module under a foreign curmod, dragging its decl's
@@ -4406,7 +4429,12 @@ fn exprtype(c: *checker, e: *syntax.node, hint: *syntax.node) *syntax.node = {
// name path stays primary because a fn-NAME callee node in wwstage
// already carries its RETURN type (fn-decl.lhs), not its fn-type —
// so a `fn make() fn() void` callee would otherwise mis-yield void.
let ct: *syntax.node = resolvealias(c, unwrapbang(exprtype(c, callee, nil)));
let calleetn: *syntax.node = exprtype(c, callee, nil);
if (callee.type_ == c.tc.tyerr: *void) {
e.type_ = c.tc.tyerr: *void;
return nil;
};
let ct: *syntax.node = resolvealias(c, unwrapbang(calleetn));
// #14/#181-cgen: ONE pointer-peel only, mirroring cstage
// cmd/wcc/check.c:1947. #181-cgen lowers an indirect call by using the
// callee VALUE as the target, the fn address for a single `*fn` but only
@@ -4554,6 +4582,10 @@ fn exprtype(c: *checker, e: *syntax.node, hint: *syntax.node) *syntax.node = {
// check.c:805-832. Peel one TPTR for `(*EnumT).MEMBER` (rare
// but cstage handles it at L808).
let basetn: *syntax.node = exprtype(c, lhsn, nil);
if (lhsn != nil && lhsn.type_ == c.tc.tyerr: *void) {
e.type_ = c.tc.tyerr: *void;
return nil;
};
if (basetn != nil) {
let bu: *syntax.node = resolvealias(c, unwrapbang(basetn));
if (bu != nil) { if (bu.kind == syntax.nkind.N_TPTR) {
@@ -7499,6 +7531,90 @@ fn asserttyped(c: *checker, n: *syntax.node, indot: bool) void = {
};
};
fn importdiagprefix(n: *syntax.node) void = {
cerr(n.file); cerr(":");
cerr(strconv.i32tos(n.line, strconv.base.DEC)); cerr(":");
cerr(strconv.i32tos(n.col, strconv.base.DEC)); cerr(": error: ");
};
fn importdiagalt(n: *syntax.node, name: str) void = {
cerr("\t"); cerr(n.file); cerr(":");
cerr(strconv.i32tos(n.line, strconv.base.DEC)); cerr(":");
cerr(strconv.i32tos(n.col, strconv.base.DEC));
cerr(": other declaration of "); cerr(name); cerr("\n");
};
fn topdeclkind(d: *syntax.node) bool = {
return d != nil && (d.kind == syntax.nkind.N_TYPEDECL
|| d.kind == syntax.nkind.N_DEF || d.kind == syntax.nkind.N_FNDECL
|| d.kind == syntax.nkind.N_LET);
};
fn checkimportredeclarations(c: *checker, file: *syntax.node) void = {
if (c.sepmode == 0) { return; };
let u: *syntax.node = file.list;
for (u != nil) {
if (u.kind == syntax.nkind.N_USE && u.imported == 0) {
let v: *syntax.node = file.list;
for (v != u) {
if (v.kind == syntax.nkind.N_USE && v.imported == 0
&& v.sourceid == u.sourceid && syntax.streq(v.str, u.str)) {
importdiagprefix(u); cerr(u.str);
cerr(" redeclared in this block\n");
c.errs += 1;
importdiagalt(v, u.str);
break;
};
v = v.next;
};
};
u = u.next;
};
};
fn checkimportusageandcollisions(c: *checker, file: *syntax.node) void = {
if (c.sepmode == 0) { return; };
let u: *syntax.node = file.list;
for (u != nil) {
if (u.kind == syntax.nkind.N_USE && u.imported == 0 && u.used == 0) {
let path: str = u.usepath;
if (path.len == 0) { path = u.str; };
let (prefix, suffix) = strings.rcut(path, ".");
let leaf: str = suffix;
if (leaf.len == 0) { leaf = path; };
importdiagprefix(u); cerr("\""); cerr(path);
if (syntax.streq(u.str, leaf)) {
cerr("\" imported and not used\n");
} else {
cerr("\" imported as "); cerr(u.str);
cerr(" and not used\n");
};
c.errs += 1;
};
u = u.next;
};
let d: *syntax.node = file.list;
for (d != nil) {
if (d.imported == 0 && topdeclkind(d)) {
u = file.list;
for (u != nil) {
if (u.kind == syntax.nkind.N_USE && u.imported == 0
&& syntax.streq(d.str, u.str)) {
let path: str = u.usepath;
if (path.len == 0) { path = u.str; };
importdiagprefix(d); cerr(d.str);
cerr(" already declared through import of package ");
cerr(u.str); cerr(" (\""); cerr(path); cerr("\")\n");
c.errs += 1;
importdiagalt(u, d.str);
};
u = u.next;
};
};
d = d.next;
};
};
fn checkinit(c: *checker, tc: *syntax.tctx) void = {
c.tc = tc;
c.top = syntax.newscope(nil);
@@ -7509,12 +7625,15 @@ fn checkinit(c: *checker, tc: *syntax.tctx) void = {
c.istest = 0i32; // #15: caller (w6c main) sets it after init
c.istestpackage = 0i32;
c.testmodule = "test";
let emptytesttarget: str;
c.testtarget = emptytesttarget;
c.sepmode = 0i32; // caller (w6c main) sets it from -c
c.synthtestrun = nil;
c.verbose = 0;
c.fnret = nil;
let empty: str;
c.curmod = empty;
c.cursource = 0;
c.file = nil;
c.allococtx = nil;
seedprimitives(c);
@@ -7538,25 +7657,35 @@ fn checkfile(c: *checker, file: *syntax.node) void = {
let present: bool = false;
let su: *syntax.node = file.list;
for (su != nil) {
if (c.testtarget.len > 0 && su.kind == syntax.nkind.N_USE
&& su.imported == 0
&& (syntax.streq(su.usepath, c.testtarget)
|| syntax.streq(su.usepath, c.testmodule))) {
su.used = 1i32;
};
if (su.kind == syntax.nkind.N_USE && su.imported == 0
&& syntax.streq(su.usepath, c.testmodule)) {
present = true;
break;
};
su = su.next;
};
if (!present) {
let usenode: *syntax.node = syntax.newnode(syntax.nkind.N_USE, file.file, file.line, file.col);
usenode.str = c.testmodule;
usenode.usepath = c.testmodule;
usenode.next = file.list;
usenode.str = c.testmodule;
usenode.usepath = c.testmodule;
usenode.pkgname = file.pkgname;
usenode.sourceid = file.sourceid;
if (c.testtarget.len > 0) { usenode.used = 1i32; };
usenode.next = file.list;
file.list = usenode;
};
};
};
checkimportredeclarations(c, file);
// Pass 1: install all top-level names.
let d: *syntax.node = file.list;
for (d != nil) {
c.cursource = d.sourceid;
installdecl(c, file, d);
d = d.next;
};
@@ -7693,11 +7822,14 @@ fn checkfile(c: *checker, file: *syntax.node) void = {
};
let nm: *syntax.node = syntax.newnode(syntax.nkind.N_STRLIT, pf, pl, pc);
nm.str = t.str;
let id: *syntax.node;
if (t.imported != 0 && t.nmod.len > 0) {
let (prefix, suffix) = strings.rcut(t.nmod, ".");
let alias: str = suffix;
let id: *syntax.node;
if (t.imported != 0 && t.nmod.len > 0) {
let alias: str = t.pkgname;
if (alias.len == 0) { alias = t.nmod; };
if (c.testtarget.len > 0
&& syntax.streq(t.nmod, c.testtarget)) {
alias = c.testtarget;
};
id = syntax.newnode(syntax.nkind.N_DOT, pf, pl, pc);
id.lhs = syntax.newnode(syntax.nkind.N_IDENT, pf, pl, pc);
id.lhs.str = alias;
@@ -7758,8 +7890,10 @@ fn checkfile(c: *checker, file: *syntax.node) void = {
let arr: *syntax.node = syntax.newnode(syntax.nkind.N_ARRLIT, pf, pl, pc);
arr.list = rhead;
tab = syntax.newnode(syntax.nkind.N_LET, pf, pl, pc);
tab.op = syntax.tkind.TK_CONST;
tab.str = "__wwtests";
tab.op = syntax.tkind.TK_CONST;
tab.str = "__wwtests";
tab.pkgname = file.pkgname;
tab.sourceid = file.sourceid;
tab.lhs = tsl;
tab.rhs = arr;
// pass 1 already ran; install the table name now so main
@@ -7795,8 +7929,10 @@ fn checkfile(c: *checker, file: *syntax.node) void = {
// the synthesized call type-resolves. Pass 1 installs the use.
};
let m: *syntax.node = syntax.newnode(syntax.nkind.N_FNDECL, pf, pl, pc);
m.str = "main";
m.exported = 1i32;
m.str = "main";
m.exported = 1i32;
m.pkgname = file.pkgname;
m.sourceid = file.sourceid;
let rety: *syntax.node = syntax.newnode(syntax.nkind.N_TNAME, pf, pl, pc);
rety.str = "i32";
m.lhs = rety;
@@ -7824,6 +7960,7 @@ fn checkfile(c: *checker, file: *syntax.node) void = {
d = file.list;
for (d != nil) {
c.curmod = declmod(file, d);
c.cursource = d.sourceid;
// A.6.2.1-pre — attr-subtree gap: top-level dispatch below walks
// d.lhs / d.body per kind but never d.attr, leaving `@symbol("…")`
// arg literals (N_STRLIT) outside the post-order exprtype
@@ -7918,6 +8055,7 @@ fn checkfile(c: *checker, file: *syntax.node) void = {
};
d = d.next;
};
checkimportusageandcollisions(c, file);
// Pass 3 (#15, A.6.2.1e): post-checker invariant gate. Walks each
// decl with its curmod set so asserttyped's gate lookups resolve
@@ -7925,6 +8063,7 @@ fn checkfile(c: *checker, file: *syntax.node) void = {
d = file.list;
for (d != nil) {
c.curmod = declmod(file, d);
c.cursource = d.sourceid;
asserttyped(c, d, false);
d = d.next;
};
@@ -7967,5 +8106,6 @@ fn checkfile(c: *checker, file: *syntax.node) void = {
let empty: str;
c.curmod = empty;
c.cursource = 0;
};

View File

@@ -82,24 +82,11 @@ fn wwimodeq(a: str, b: str) bool = {
// Map an import alias in the source package that owns the reference. The
// flattened parser file contains every imported interface's N_USE nodes, so
// this owner filter is what prevents cross-package alias capture.
fn wwiusepath(c: *checker, owner: str, alias: str) str = {
if (owner.len > 0) {
let dotidx: i32 = -1;
let i: i32 = 0;
for (i < owner.len) {
if (owner[i] == 46u8) { dotidx = i; };
i += 1;
};
let leaf: str = owner;
if (dotidx >= 0) {
leaf.ptr = owner.ptr + ((dotidx + 1): u64);
leaf.len = owner.len - dotidx - 1;
};
if (syntax.streq(alias, leaf)) { return owner; };
};
fn wwiusepath(c: *checker, owner: str, source: i32, alias: str) str = {
let u: *syntax.node = c.file.list;
for (u != nil) {
if (u.kind == syntax.nkind.N_USE && syntax.streq(u.str, alias)) {
if (u.kind == syntax.nkind.N_USE && u.sourceid == source
&& syntax.streq(u.str, alias)) {
let same: bool = false;
if (owner.len == 0) {
same = u.imported == 0;
@@ -117,24 +104,27 @@ fn wwiusepath(c: *checker, owner: str, alias: str) str = {
return empty;
};
fn wwidirectmodvisible(c: *checker, owner: str, mod: str) bool = {
fn wwidirectmodvisible(c: *checker, owner: str, source: i32, mod: str) bool = {
if (mod.len == 0) { return false; };
let dotidx: i32 = -1;
let i: i32 = 0;
for (i < mod.len) {
if (mod[i] == 46u8) { dotidx = i; };
i += 1;
let u: *syntax.node = c.file.list;
for (u != nil) {
if (u.kind == syntax.nkind.N_USE && u.sourceid == source) {
let same: bool = false;
if (owner.len == 0) {
same = u.imported == 0;
} else {
same = u.imported != 0 && wwimodeq(u.nmod, owner);
};
let path: str = u.str;
if (u.usepath.len > 0) { path = u.usepath; };
if (same && syntax.streq(path, mod)) { return true; };
};
u = u.next;
};
let alias: str = mod;
if (dotidx >= 0) {
alias.ptr = mod.ptr + ((dotidx + 1): u64);
alias.len = mod.len - dotidx - 1;
};
let path: str = wwiusepath(c, owner, alias);
return path.len > 0 && syntax.streq(path, mod);
return false;
};
fn wwitypesym(c: *checker, owner: str, nm: str) *syntax.sym = {
fn wwitypesym(c: *checker, owner: str, source: i32, nm: str) *syntax.sym = {
let dotidx: i32 = -1;
let i: i32 = 0;
for (i < nm.len) {
@@ -149,7 +139,7 @@ fn wwitypesym(c: *checker, owner: str, nm: str) *syntax.sym = {
let leaf: str;
leaf.ptr = nm.ptr + ((dotidx + 1): u64);
leaf.len = nm.len - dotidx - 1;
let mod: str = wwiusepath(c, owner, head);
let mod: str = wwiusepath(c, owner, source, head);
if (mod.len > 0) {
s = syntax.scopelookupinmodule(c.cur, mod, leaf);
};
@@ -162,7 +152,7 @@ fn wwitypesym(c: *checker, owner: str, nm: str) *syntax.sym = {
for (b != nil) {
if (b.skind == syntax.skind.SK_TYPE
&& syntax.streq(b.name, nm)
&& wwidirectmodvisible(c, owner, b.mod)) {
&& wwidirectmodvisible(c, owner, source, b.mod)) {
s = b;
break;
};
@@ -669,7 +659,8 @@ fn wwifactsame(mod: str, rank: i32, d: *syntax.node,
return rank == srank && wwimodeq(mod, smod) && syntax.streq(d.str, sd.str);
};
fn wwifactvaluesym(c: *checker, owner: str, name: str) *syntax.sym = {
fn wwifactvaluesym(c: *checker, owner: str, source: i32,
name: str) *syntax.sym = {
let p: *syntax.scope = c.top;
for (p != nil) {
let s: *syntax.sym = p.first;
@@ -685,7 +676,7 @@ fn wwifactvaluesym(c: *checker, owner: str, name: str) *syntax.sym = {
let s: *syntax.sym = p.first;
for (s != nil) {
if (s.skind == syntax.skind.SK_DEF && syntax.streq(s.name, name)
&& wwidirectmodvisible(c, owner, s.mod)) { return s; };
&& wwidirectmodvisible(c, owner, source, s.mod)) { return s; };
s = s.snext;
};
p = p.parent;
@@ -757,27 +748,30 @@ fn wwicollectdecl(fs: *wwifactset, owner: str, d: *syntax.node) void = {
if (d.kind == syntax.nkind.N_FNDECL) {
let p: *syntax.node = d.list;
for (p != nil) { wwicollecttype(fs, owner, p.lhs); p = p.next; };
wwicollecttype(fs, owner, d.lhs);
for (p != nil) { wwicollecttype(fs, owner, d.sourceid, p.lhs); p = p.next; };
wwicollecttype(fs, owner, d.sourceid, d.lhs);
} else { if (d.kind == syntax.nkind.N_TYPEDECL) {
wwicollecttype(fs, owner, d.lhs);
wwicollecttype(fs, owner, d.sourceid, d.lhs);
} else { if (d.kind == syntax.nkind.N_DEF) {
wwicollecttype(fs, owner, d.lhs);
wwicollectexpr(fs, owner, d.rhs);
wwicollecttype(fs, owner, d.sourceid, d.lhs);
wwicollectexpr(fs, owner, d.sourceid, d.rhs);
} else { if (d.kind == syntax.nkind.N_LET) {
wwicollecttype(fs, owner, d.lhs);
wwicollecttype(fs, owner, d.sourceid, d.lhs);
};};};};
};
fn wwicollectexpr(fs: *wwifactset, owner: str, e: *syntax.node) void = {
fn wwicollectexpr(fs: *wwifactset, owner: str, source: i32,
e: *syntax.node) void = {
if (e == nil) { return; };
let s: *syntax.sym = nil;
if (e.kind == syntax.nkind.N_IDENT) {
s = wwifactvaluesym(fs.c, owner, e.str);
s = wwifactvaluesym(fs.c, owner, source, e.str);
} else { if (e.kind == syntax.nkind.N_DOT && e.lhs != nil) {
if (e.lhs.kind == syntax.nkind.N_IDENT) {
let mod: str = wwiusepath(fs.c, owner, e.lhs.str);
if (mod.len > 0) { s = wwifactvaluesym(fs.c, mod, e.str); };
let mod: str = wwiusepath(fs.c, owner, source, e.lhs.str);
if (mod.len > 0) {
s = wwifactvaluesym(fs.c, mod, source, e.str);
};
};
}; };
if (s != nil && s.decl != nil) {
@@ -790,30 +784,37 @@ fn wwicollectexpr(fs: *wwifactset, owner: str, e: *syntax.node) void = {
return;
};
if (e.kind == syntax.nkind.N_BIN) {
wwicollectexpr(fs, owner, e.lhs);
wwicollectexpr(fs, owner, e.rhs);
wwicollectexpr(fs, owner, source, e.lhs);
wwicollectexpr(fs, owner, source, e.rhs);
} else { if (e.kind == syntax.nkind.N_UN) {
wwicollectexpr(fs, owner, e.lhs);
wwicollectexpr(fs, owner, source, e.lhs);
} else { if (e.kind == syntax.nkind.N_CAST) {
wwicollectexpr(fs, owner, e.lhs);
wwicollecttype(fs, owner, e.rhs);
wwicollectexpr(fs, owner, source, e.lhs);
wwicollecttype(fs, owner, source, e.rhs);
}; }; };
};
fn wwievalconst(fs: *wwifactset, owner: str, e: *syntax.node,
fn wwievalconst(fs: *wwifactset, owner: str, source: i32, e: *syntax.node,
out: *u64) bool = {
let saved: str = fs.c.curmod;
let savesource: i32 = fs.c.cursource;
fs.c.curmod = owner;
fs.c.cursource = source;
let ok: bool = evaldefconst(fs.c, e, out, 0);
fs.c.curmod = saved;
fs.c.cursource = savesource;
return ok;
};
fn wwicollecttype(fs: *wwifactset, owner: str, t: *syntax.node) void = {
fn wwicollecttype(fs: *wwifactset, owner: str, source: i32,
t: *syntax.node) void = {
if (t == nil) { return; };
if (t.kind == syntax.nkind.N_TPARAM) { wwicollecttype(fs, owner, t.lhs); return; };
if (t.kind == syntax.nkind.N_TPARAM) {
wwicollecttype(fs, owner, source, t.lhs);
return;
};
if (t.kind == syntax.nkind.N_TNAME) {
let s: *syntax.sym = wwitypesym(fs.c, owner, t.str);
let s: *syntax.sym = wwitypesym(fs.c, owner, source, t.str);
if (s != nil && s.decl != nil) {
if (s.decl.kind == syntax.nkind.N_TYPEDECL) {
wwicollectdecl(fs, s.mod, s.decl);
@@ -823,14 +824,14 @@ fn wwicollecttype(fs: *wwifactset, owner: str, t: *syntax.node) void = {
t.kind == syntax.nkind.N_TPTR || t.kind == syntax.nkind.N_TSLICE ||
t.kind == syntax.nkind.N_TBANG || t.kind == syntax.nkind.N_TCHAN
) {
wwicollecttype(fs, owner, t.lhs);
wwicollecttype(fs, owner, source, t.lhs);
} else { if (t.kind == syntax.nkind.N_TARRAY) {
// Array length is resolved type identity, not a source name
// dependency. Canonicalize it so private constants stay private
// and consumers never need implementation defs merely for layout.
if (t.rhs != nil && t.rhs.kind != syntax.nkind.N_INTLIT) {
let len: u64 = 0u64;
if (!wwievalconst(fs, owner, t.rhs, &len)) {
if (!wwievalconst(fs, owner, source, t.rhs, &len)) {
wwiencodearrayreject(t);
fs.bad = 1;
} else {
@@ -841,19 +842,19 @@ fn wwicollecttype(fs: *wwifactset, owner: str, t: *syntax.node) void = {
let empty: str; e.tsuffix = empty;
};
};
wwicollecttype(fs, owner, t.lhs);
wwicollecttype(fs, owner, source, t.lhs);
} else { if (t.kind == syntax.nkind.N_TFN) {
let p: *syntax.node = t.list;
for (p != nil) { wwicollecttype(fs, owner, p.lhs); p = p.next; };
wwicollecttype(fs, owner, t.lhs);
for (p != nil) { wwicollecttype(fs, owner, source, p.lhs); p = p.next; };
wwicollecttype(fs, owner, source, t.lhs);
} else { if (t.kind == syntax.nkind.N_TSTRUCT) {
let f: *syntax.node = t.list;
for (f != nil) { wwicollecttype(fs, owner, f.lhs); f = f.next; };
for (f != nil) { wwicollecttype(fs, owner, source, f.lhs); f = f.next; };
} else { if (t.kind == syntax.nkind.N_TTAGGED || t.kind == syntax.nkind.N_TTUPLE) {
let e: *syntax.node = t.list;
for (e != nil) { wwicollecttype(fs, owner, e); e = e.next; };
for (e != nil) { wwicollecttype(fs, owner, source, e); e = e.next; };
} else { if (t.kind == syntax.nkind.N_TENUM) {
wwicollecttype(fs, owner, t.lhs);
wwicollecttype(fs, owner, source, t.lhs);
// Member identifiers refer to prior siblings in this enum, not
// package defs; the declaration already carries the whole list.
};};};};};};};
@@ -866,6 +867,9 @@ fn wwisortfacts(fs: *wwifactset) void = {
let j: i32 = i + 1;
for (j < fs.nfacts) {
let r: i32 = wwistrcmp(fs.factmods[j], fs.factmods[best]);
if (r == 0) {
r = fs.factnodes[j].sourceid - fs.factnodes[best].sourceid;
};
if (r == 0) { r = fs.factranks[j] - fs.factranks[best]; };
if (r == 0) { r = wwistrcmp(fs.factnodes[j].str, fs.factnodes[best].str); };
if (r < 0) { best = j; };
@@ -880,22 +884,40 @@ fn wwisortfacts(fs: *wwifactset) void = {
};
};
fn wwiowneduse(u: *syntax.node, owner: str) bool = {
fn wwiowneduse(u: *syntax.node, owner: str, source: i32) bool = {
return u.kind == syntax.nkind.N_USE && u.imported != 0
&& syntax.streq(u.nmod, owner);
&& u.sourceid == source && wwimodeq(u.nmod, owner);
};
fn wwiemitfactimports(fd: i32, file: *syntax.node, owner: str) void = {
fn wwiemitimports(fd: i32, file: *syntax.node, owner: str, source: i32,
imported: bool) void = {
let nuse: i32 = 0;
let u: *syntax.node = file.list;
for (u != nil) { if (wwiowneduse(u, owner)) { nuse += 1; }; u = u.next; };
for (u != nil) {
let owned: bool = false;
if (imported) {
owned = wwiowneduse(u, owner, source);
} else {
owned = u.kind == syntax.nkind.N_USE && u.imported == 0
&& u.sourceid == source;
};
if (owned) { nuse += 1; };
u = u.next;
};
if (nuse == 0) { return; };
let paths: []str = alloc([], nuse: u64)!; paths.len = nuse;
let nodes: []*syntax.node = alloc([], nuse: u64)!; nodes.len = nuse;
let k: i32 = 0;
u = file.list;
for (u != nil) {
if (wwiowneduse(u, owner)) {
let owned: bool = false;
if (imported) {
owned = wwiowneduse(u, owner, source);
} else {
owned = u.kind == syntax.nkind.N_USE && u.imported == 0
&& u.sourceid == source;
};
if (owned) {
if (u.usepath.len > 0) { paths[k] = u.usepath; } else { paths[k] = u.str; };
nodes[k] = u;
k += 1;
@@ -914,6 +936,85 @@ fn wwiemitfactimports(fd: i32, file: *syntax.node, owner: str) void = {
};
};
fn wwiprimarysectionhas(c: *checker, file: *syntax.node,
fs: *wwifactset, exports: []*syntax.node, nexports: i32,
source: i32) bool = {
let u: *syntax.node = file.list;
for (u != nil) {
if (u.kind == syntax.nkind.N_USE && u.imported == 0
&& u.sourceid == source) { return true; };
u = u.next;
};
let i: i32 = 0;
for (i < fs.nprivate) {
if (fs.privatenodes[i].sourceid == source) { return true; };
i += 1;
};
i = 0;
for (i < nexports) {
if (exports[i].sourceid == source) { return true; };
i += 1;
};
if (c.istestpackage != 0) {
let d: *syntax.node = file.list;
for (d != nil) {
if (wwiprimary(d) && d.sourceid == source
&& d.kind == syntax.nkind.N_FNDECL && d.exported == 0
&& wwihasattr(d, "test")) { return true; };
d = d.next;
};
};
return false;
};
fn wwifirstprimarysource(file: *syntax.node) i32 = {
let found: bool = false;
let source: i32 = 0;
let d: *syntax.node = file.list;
for (d != nil) {
if (wwiprimary(d) && (!found || d.sourceid < source)) {
source = d.sourceid;
found = true;
};
d = d.next;
};
if (found) { return source; };
return file.sourceid;
};
fn wwiemitprimarysection(c: *checker, fd: i32, file: *syntax.node,
fs: *wwifactset, exports: []*syntax.node, nexports: i32,
owner: str, pkgname: str, source: i32) void = {
if (owner.len > 0) {
wputs(fd, "//ww:module "); wputs(fd, owner); wputs(fd, "\n");
};
let pkg: str = pkgname;
if (pkg.len == 0) { pkg = "main"; };
wputs(fd, "package "); wputs(fd, pkg); wputs(fd, ";\n");
wwiemitimports(fd, file, owner, source, false);
let i: i32 = 0;
for (i < fs.nprivate) {
if (fs.privatenodes[i].sourceid == source) {
wwidecl(fd, fs.privatenodes[i]);
};
i += 1;
};
if (c.istestpackage != 0) {
let d: *syntax.node = file.list;
for (d != nil) {
if (wwiprimary(d) && d.sourceid == source
&& d.kind == syntax.nkind.N_FNDECL && d.exported == 0
&& wwihasattr(d, "test")) { wwidecl(fd, d); };
d = d.next;
};
};
i = 0;
for (i < nexports) {
if (exports[i].sourceid == source) { wwidecl(fd, exports[i]); };
i += 1;
};
};
fn wwiemit(c: *checker, file: *syntax.node, path: str) i32 = {
// §5: check_exported_type FIRST, before any byte — a producer
// without it can emit a dangling `.wwi`.
@@ -959,140 +1060,24 @@ fn wwiemit(c: *checker, file: *syntax.node, path: str) i32 = {
wwisortdecls(fs.privatekeys, fs.privatenodes, fs.nprivate);
wwisortfacts(&fs);
let fd: i32 = os.open(path,
os.flag.WRONLY | os.flag.CREATE | os.flag.TRUNC, 420i32); // 0o644
if (fd < 0) {
wputs(2, "w6c: cannot open ");
wputs(2, path);
wputs(2, "\n");
return 1i32;
};
// package line: leaf of the first primary decl's module tag.
let pkg: str = "main";
let found: i32 = 0;
let pd: *syntax.node = file.list;
for (pd != nil) {
if (wwiprimary(pd) && pd.nmod.len > 0) {
let dotidx: i32 = -1;
let i: i32 = 0;
for (i < pd.nmod.len) {
if (pd.nmod[i] == 46u8) { dotidx = i; };
i += 1;
};
if (dotidx >= 0) {
let leaf: str;
leaf.ptr = pd.nmod.ptr + ((dotidx + 1): u64);
leaf.len = pd.nmod.len - dotidx - 1;
pkg = leaf;
} else {
pkg = pd.nmod;
};
found = 1;
pd = nil;
} else {
pd = pd.next;
};
};
// #11: a decl-less / export-less primary body carries no
// module-tagged decl, so the scan above finds nothing; fall back to
// the primary module identity stamped on the N_FILE node at parse
// time. A raw single-file `package main` root arrives via a bare reset
// and leaves file.nmod empty, so it stays "main". The detector is
// scan-miss (found==0), NOT pkg=="main", to match cstage byte-for-
// byte (rule 10) when a tagged decl legitimately leafs to "main".
if (found == 0 && file.nmod.len > 0) {
let dotidx: i32 = -1;
let i: i32 = 0;
for (i < file.nmod.len) {
if (file.nmod[i] == 46u8) { dotidx = i; };
i += 1;
};
if (dotidx >= 0) {
let leaf: str;
leaf.ptr = file.nmod.ptr + ((dotidx + 1): u64);
leaf.len = file.nmod.len - dotidx - 1;
pkg = leaf;
} else {
pkg = file.nmod;
};
};
if (file.nmod.len > 0) {
wputs(fd, "//ww:module "); wputs(fd, file.nmod); wputs(fd, "\n");
};
wputs(fd, "package ");
wputs(fd, pkg);
wputs(fd, ";\n");
// imports — primary N_USE, byte-sorted by import path.
let nuse: i32 = 0;
let u: *syntax.node = file.list;
for (u != nil) {
if (u.kind == syntax.nkind.N_USE && wwiprimary(u)) { nuse += 1; };
u = u.next;
};
if (nuse > 0) {
let upaths: []str = alloc([], nuse: u64)!;
upaths.len = nuse;
let unodes: []*syntax.node = alloc([], nuse: u64)!;
unodes.len = nuse;
let k: i32 = 0;
u = file.list;
for (u != nil) {
if (u.kind == syntax.nkind.N_USE && wwiprimary(u)) {
if (u.usepath.len > 0) { upaths[k] = u.usepath; } else { upaths[k] = u.str; };
unodes[k] = u;
k += 1;
};
u = u.next;
};
wwisortdecls(upaths, unodes, nuse);
let i: i32 = 0;
let previous: str = "";
for (i < nuse) {
if (previous.len == 0 || !syntax.streq(previous, upaths[i])) {
wputs(fd, "import ");
wputs(fd, upaths[i]);
wputs(fd, ";\n");
previous = upaths[i];
};
i += 1;
};
};
// Compiler-private owner nominals precede public declarations. They are
// available to export decoding but remain absent from source visibility.
let pri: i32 = 0;
for (pri < fs.nprivate) {
wwidecl(fd, fs.privatenodes[pri]);
pri += 1;
};
// Package-test metadata is private and deterministic. Only the distinct
// generated-main package consumes these declarations through --import.
if (c.istestpackage != 0) {
d = file.list;
for (d != nil) {
if (wwiprimary(d) && d.kind == syntax.nkind.N_FNDECL
&& d.exported == 0 && wwihasattr(d, "test")) {
wwidecl(fd, d);
};
d = d.next;
};
};
// decls — exported primary, byte-sorted by symbol name.
// Exported declarations are sorted within their source-file sections.
let ndecl: i32 = 0;
d = file.list;
for (d != nil) {
if (wwiprimary(d) && d.exported != 0 && wwiisdecl(d)) { ndecl += 1; };
if (wwiprimary(d) && d.exported != 0 && wwiisdecl(d)) {
ndecl += 1;
};
d = d.next;
};
let dkeys: []str;
let dnodes: []*syntax.node;
if (ndecl > 0) {
let dkeys: []str = alloc([], ndecl: u64)!;
dkeys.len = ndecl;
let dnodes: []*syntax.node = alloc([], ndecl: u64)!;
dnodes.len = ndecl;
let keys: []str = alloc([], ndecl: u64)!;
keys.len = ndecl;
dkeys = keys;
let nodes: []*syntax.node = alloc([], ndecl: u64)!;
nodes.len = ndecl;
dnodes = nodes;
let k: i32 = 0;
d = file.list;
for (d != nil) {
@@ -1104,32 +1089,66 @@ fn wwiemit(c: *checker, file: *syntax.node, path: str) i32 = {
d = d.next;
};
wwisortdecls(dkeys, dnodes, ndecl);
let i: i32 = 0;
for (i < ndecl) {
wwidecl(fd, dnodes[i]);
i += 1;
};
let fd: i32 = os.open(path,
os.flag.WRONLY | os.flag.CREATE | os.flag.TRUNC, 420i32); // 0o644
if (fd < 0) {
wputs(2, "w6c: cannot open ");
wputs(2, path);
wputs(2, "\n");
return 1i32;
};
// Canonical ownership, declared name, and lexical source scope are
// independent export facts. Repeated owner sections preserve the file
// that owns each binding while every symbol/action remains keyed by owner.
let nsection: i32 = 0;
let p: *syntax.node = file.body;
for (p != nil) {
if (p.imported == 0 && wwiprimarysectionhas(c, file, &fs,
dnodes, ndecl, p.sourceid)) {
let owner: str = p.nmod;
if (owner.len == 0) { owner = file.nmod; };
let pkg: str = p.pkgname;
if (pkg.len == 0) { pkg = file.pkgname; };
wwiemitprimarysection(c, fd, file, &fs, dnodes, ndecl,
owner, pkg, p.sourceid);
nsection += 1;
};
p = p.next;
};
if (nsection == 0) {
wwiemitprimarysection(c, fd, file, &fs, dnodes, ndecl,
file.nmod, file.pkgname, wwifirstprimarysource(file));
};
// Compiler-owned public fact closure. Origin markers preserve nominal
// ownership but do not create source imports in the eventual consumer.
let lastmod: str;
let lastsource: i32 = -1;
let fi: i32 = 0;
for (fi < fs.nfacts) {
let mod: str = fs.factmods[fi];
if (lastmod.len == 0 || !syntax.streq(lastmod, mod)) {
let source: i32 = fs.factnodes[fi].sourceid;
if (lastmod.len == 0 || !syntax.streq(lastmod, mod)
|| lastsource != source) {
wputs(fd, "//ww:module "); wputs(fd, mod); wputs(fd, "\n");
let dotidx: i32 = -1;
let mi: i32 = 0;
for (mi < mod.len) { if (mod[mi] == 46u8) { dotidx = mi; }; mi += 1; };
let leaf: str = mod;
if (dotidx >= 0) {
leaf.ptr = mod.ptr + ((dotidx + 1): u64);
leaf.len = mod.len - dotidx - 1;
let factpkg: str = fs.factnodes[fi].pkgname;
if (factpkg.len == 0) {
let dotidx: i32 = -1;
let mi: i32 = 0;
for (mi < mod.len) { if (mod[mi] == 46u8) { dotidx = mi; }; mi += 1; };
factpkg = mod;
if (dotidx >= 0) {
factpkg.ptr = mod.ptr + ((dotidx + 1): u64);
factpkg.len = mod.len - dotidx - 1;
};
};
wputs(fd, "package "); wputs(fd, leaf); wputs(fd, ";\n");
wwiemitfactimports(fd, file, mod);
wputs(fd, "package "); wputs(fd, factpkg); wputs(fd, ";\n");
wwiemitimports(fd, file, mod, source, true);
lastmod = mod;
lastsource = source;
};
wwidecl(fd, fs.factnodes[fi]);
fi += 1;