ww: separate package identity from declared name
This commit is contained in:
@@ -4,7 +4,6 @@
|
||||
package main;
|
||||
|
||||
import os;
|
||||
import rt;
|
||||
import strings;
|
||||
|
||||
export fn emitbyte(a: *asm_, b: u8) void = {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
package main;
|
||||
|
||||
import os;
|
||||
import rt;
|
||||
import strings;
|
||||
|
||||
fn cstreq(a: *u8, lit: str) bool = {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
package main;
|
||||
|
||||
import os;
|
||||
import rt;
|
||||
import strings;
|
||||
import syntax;
|
||||
import wcc;
|
||||
@@ -95,13 +94,93 @@ fn allocimportptrs(count: i32) ([]*u8 | nomem) = {
|
||||
return value;
|
||||
};
|
||||
|
||||
fn importleaf(path: *u8) str = {
|
||||
let whole: str = pathstr(path);
|
||||
let (prefix, suffix) = strings.rcut(whole, ".");
|
||||
// rcut returns (whole, empty) when absent and (prefix, empty) for a
|
||||
// trailing delimiter. Preserve that distinction to match strrchr.
|
||||
if (suffix.len != 0 || prefix.len != whole.len) { return suffix; };
|
||||
return whole;
|
||||
fn allocnodeptrs(count: i32) ([]*syntax.node | nomem) = {
|
||||
let value: []*syntax.node = alloc([], count: u64)?;
|
||||
return value;
|
||||
};
|
||||
|
||||
// Export data carries canonical owner and declared name independently. Each
|
||||
// direct interface's package-clause markers also describe its reachable fact
|
||||
// closure, so search all parsed interfaces for a canonical owner.
|
||||
fn importpkgname(asts: []*syntax.node, nasts: i32, path: str,
|
||||
primary: *syntax.node, conflict: *bool) str = {
|
||||
let name: str;
|
||||
let i: i32 = 0;
|
||||
for (i < nasts) {
|
||||
let f: *syntax.node = asts[i];
|
||||
let p: *syntax.node = nil;
|
||||
if (f != nil) { p = f.body; };
|
||||
for (p != nil) {
|
||||
if (p.nmod.len > 0 && p.pkgname.len > 0
|
||||
&& syntax.streq(p.nmod, path)) {
|
||||
if (name.len > 0 && !syntax.streq(name, p.pkgname)) {
|
||||
*conflict = true;
|
||||
let empty: str;
|
||||
return empty;
|
||||
};
|
||||
name = p.pkgname;
|
||||
};
|
||||
p = p.next;
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
let p: *syntax.node = nil;
|
||||
if (primary != nil) { p = primary.body; };
|
||||
for (p != nil) {
|
||||
if (p.nmod.len > 0 && p.pkgname.len > 0
|
||||
&& syntax.streq(p.nmod, path)) {
|
||||
if (name.len > 0 && !syntax.streq(name, p.pkgname)) {
|
||||
*conflict = true;
|
||||
let empty: str;
|
||||
return empty;
|
||||
};
|
||||
name = p.pkgname;
|
||||
};
|
||||
p = p.next;
|
||||
};
|
||||
return name;
|
||||
};
|
||||
|
||||
fn bindimportnames(list: *syntax.node, asts: []*syntax.node, nasts: i32,
|
||||
primary: *syntax.node, testsupport: *u8) bool = {
|
||||
let u: *syntax.node = list;
|
||||
for (u != nil) {
|
||||
if (u.kind == syntax.nkind.N_USE && u.usepath.len > 0) {
|
||||
let reserved: bool = testsupport != nil
|
||||
&& cstreq(testsupport, "__wwtest")
|
||||
&& syntax.streq(u.usepath, "__wwtest");
|
||||
if (!reserved) {
|
||||
let conflict: bool = false;
|
||||
let name: str = importpkgname(asts, nasts, u.usepath,
|
||||
primary, &conflict);
|
||||
if (conflict) {
|
||||
let pre: str = "w6c: package ";
|
||||
let post: str = " has conflicting declared names in export data\n";
|
||||
os.write(2, pre.ptr, pre.len: u64);
|
||||
os.write(2, u.usepath.ptr, u.usepath.len: u64);
|
||||
os.write(2, post.ptr, post.len: u64);
|
||||
return false;
|
||||
};
|
||||
if (name.len > 0) {
|
||||
u.str = name;
|
||||
} else { if (u.imported == 0) {
|
||||
let pre: str = "w6c: import ";
|
||||
let post: str = " has no declared package name in direct export data\n";
|
||||
os.write(2, pre.ptr, pre.len: u64);
|
||||
os.write(2, u.usepath.ptr, u.usepath.len: u64);
|
||||
os.write(2, post.ptr, post.len: u64);
|
||||
return false;
|
||||
} else {
|
||||
// A closure-only import may have no declarations in this
|
||||
// interface. Its canonical path must never become a leaf
|
||||
// qualifier by fallback.
|
||||
u.str = u.usepath;
|
||||
}; };
|
||||
};
|
||||
};
|
||||
u = u.next;
|
||||
};
|
||||
return true;
|
||||
};
|
||||
|
||||
export fn main(argc: i32, argv: **u8) i32 = {
|
||||
@@ -109,6 +188,7 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
||||
let out: *u8 = nil;
|
||||
let wwiout: *u8 = nil; // -I <out.wwi>: M2 export-data producer
|
||||
let testsupport: *u8 = nil;
|
||||
let testtarget: *u8 = nil;
|
||||
let testmode: i32 = 0i32; // #15: `-T` test-mode
|
||||
let testpackage: i32 = 0i32;
|
||||
let commandpackage: i32 = 0i32;
|
||||
@@ -120,6 +200,8 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
||||
let fileallocation: ([]*u8 | nomem) = allocimportptrs(argc);
|
||||
let importpaths: []*u8;
|
||||
let importfiles: []*u8;
|
||||
let astallocation: ([]*syntax.node | nomem) = allocnodeptrs(argc);
|
||||
let importasts: []*syntax.node;
|
||||
match (pathallocation) {
|
||||
case let value: []*u8 => importpaths = value;
|
||||
case nomem => {
|
||||
@@ -138,6 +220,15 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
||||
};
|
||||
importpaths.len = argc;
|
||||
importfiles.len = argc;
|
||||
match (astallocation) {
|
||||
case let value: []*syntax.node => importasts = value;
|
||||
case nomem => {
|
||||
let m: str = "w6c: out of memory\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
return 1;
|
||||
};
|
||||
};
|
||||
importasts.len = argc;
|
||||
let nimports: i32 = 0;
|
||||
let mapallocation: ([]importmap | nomem) = allocimportmaps(argc);
|
||||
let importmaps: []importmap;
|
||||
@@ -187,6 +278,14 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
||||
return 2;
|
||||
};
|
||||
testsupport = argv[i];
|
||||
} else { if (cstreq(a, "--test-target-package")) {
|
||||
i += 1;
|
||||
if (i >= argc) {
|
||||
let m: str = "w6c: --test-target-package requires arg\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
return 2;
|
||||
};
|
||||
testtarget = argv[i];
|
||||
} else { if (cstreq(a, "-c")) {
|
||||
sepmode = 1i32;
|
||||
} else { if (cstreq(a, "--import")) {
|
||||
@@ -221,12 +320,12 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
||||
return 2;
|
||||
};
|
||||
src = a;
|
||||
}; }; }; }; }; }; }; }; }; }; };
|
||||
}; }; }; }; }; }; }; }; }; }; }; };
|
||||
i += 1;
|
||||
};
|
||||
|
||||
if (src == nil) {
|
||||
let m: str = "usage: w6c_ww [-T|--test-package] [--command-package] [--entry] [-c] [-I out.wwi] [--import path dep.wwi]... [--import-map source path]... [-o out.s] file.ww\n";
|
||||
let m: str = "usage: w6c_ww [-T|--test-package] [--command-package] [--entry] [--test-target-package path] [-c] [-I out.wwi] [--import path dep.wwi]... [--import-map source path]... [-o out.s] file.ww\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
return 2;
|
||||
};
|
||||
@@ -280,12 +379,6 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
return 2;
|
||||
};
|
||||
if (!syntax.streq(importleaf(importmaps[mapi].source),
|
||||
importleaf(importmaps[mapi].path))) {
|
||||
let m: str = "w6c: --import-map must preserve import leaf\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
return 2;
|
||||
};
|
||||
let direct: bool = false;
|
||||
let directi: i32 = 0;
|
||||
for (directi < nimports) {
|
||||
@@ -310,6 +403,27 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
return 2;
|
||||
};
|
||||
if (testtarget != nil && (sepmode == 0 || testmode == 0
|
||||
|| testtarget[0u64] == 0u8)) {
|
||||
let m: str = "w6c: invalid --test-target-package\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
return 2;
|
||||
};
|
||||
if (testtarget != nil) {
|
||||
let direct: bool = false;
|
||||
importi = 0;
|
||||
for (importi < nimports) {
|
||||
if (cstreq(importpaths[importi], pathstr(testtarget))) {
|
||||
direct = true;
|
||||
};
|
||||
importi += 1;
|
||||
};
|
||||
if (!direct) {
|
||||
let m: str = "w6c: --test-target-package is not a direct import\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
return 2;
|
||||
};
|
||||
};
|
||||
|
||||
let importhead: *node = nil;
|
||||
let importtail: *node = nil;
|
||||
@@ -352,6 +466,9 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
||||
if (testsupport != nil) { ips.testmodule = pathstr(testsupport); };
|
||||
let imported: *node = parsefile(&ips);
|
||||
if (il.errs > 0 || ips.errs > 0) { return 1; };
|
||||
importasts[importi] = imported;
|
||||
if (!bindimportnames(imported.list, importasts, importi + 1,
|
||||
nil, testsupport)) { return 1; };
|
||||
let d: *node = imported.list;
|
||||
if (d != nil) {
|
||||
if (importhead == nil) { importhead = d; }
|
||||
@@ -408,6 +525,35 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
||||
};
|
||||
mapi += 1;
|
||||
};
|
||||
// A later direct interface may supply metadata for an origin section used
|
||||
// by an earlier interface, so bind once more against the complete set.
|
||||
importi = 0;
|
||||
for (importi < nimports) {
|
||||
if (!bindimportnames(importasts[importi].list, importasts, nimports,
|
||||
nil, testsupport)) { return 1; };
|
||||
importi += 1;
|
||||
};
|
||||
if (!bindimportnames(f.list, importasts, nimports, f, testsupport)) {
|
||||
return 1;
|
||||
};
|
||||
if (testtarget != nil) {
|
||||
let seen: i32 = 0;
|
||||
let targetuse: *syntax.node = f.list;
|
||||
for (targetuse != nil) {
|
||||
if (targetuse.kind == syntax.nkind.N_USE
|
||||
&& targetuse.imported == 0
|
||||
&& syntax.streq(targetuse.usepath, pathstr(testtarget))) {
|
||||
targetuse.str = targetuse.usepath;
|
||||
seen += 1;
|
||||
};
|
||||
targetuse = targetuse.next;
|
||||
};
|
||||
if (seen != 1) {
|
||||
let m: str = "w6c: generated test target import is not unique\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
return 2;
|
||||
};
|
||||
};
|
||||
if (importhead != nil) {
|
||||
importtail.next = f.list;
|
||||
f.list = importhead;
|
||||
@@ -433,8 +579,10 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
||||
|
||||
let testmodule: str;
|
||||
if (testsupport != nil) { testmodule = pathstr(testsupport); };
|
||||
let testtargetmodule: str;
|
||||
if (testtarget != nil) { testtargetmodule = pathstr(testtarget); };
|
||||
let interfaceout: str;
|
||||
if (wwiout != nil) { interfaceout = pathstr(wwiout); };
|
||||
return wcc.compilefile(f, testmode, testpackage, testmodule, sepmode,
|
||||
interfaceout, entrymode);
|
||||
return wcc.compilefile(f, testmode, testpackage, testmodule,
|
||||
testtargetmodule, sepmode, interfaceout, entrymode);
|
||||
};
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
package main;
|
||||
|
||||
import os;
|
||||
import rt;
|
||||
import strings;
|
||||
|
||||
def ET_DYN_SO: u16 = 3u16;
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
package main;
|
||||
|
||||
import os;
|
||||
import rt;
|
||||
import strings;
|
||||
|
||||
def ET_EXEC_D: u16 = 2u16;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
package main;
|
||||
|
||||
import os;
|
||||
import rt;
|
||||
import strings;
|
||||
|
||||
def BASE: u64 = 4194304u64; // 0x400000
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
package main;
|
||||
|
||||
import os;
|
||||
import rt;
|
||||
import strings;
|
||||
|
||||
def ET_REL: i32 = 1;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
package main;
|
||||
|
||||
import os;
|
||||
import rt;
|
||||
|
||||
def ET_EXEC: u16 = 2u16;
|
||||
def EM_X86_64_W: u16 = 62u16;
|
||||
|
||||
@@ -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; };
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -17,7 +17,6 @@ import crypto.sha256;
|
||||
import hash;
|
||||
import os;
|
||||
import os.exec;
|
||||
import rt;
|
||||
import strings;
|
||||
import syntax;
|
||||
|
||||
@@ -893,6 +892,9 @@ type lflags = struct {
|
||||
type sepbind = struct {
|
||||
kind: u8,
|
||||
name: str,
|
||||
source: str,
|
||||
line: i32,
|
||||
col: i32,
|
||||
dep: i32,
|
||||
};
|
||||
|
||||
@@ -919,6 +921,7 @@ type seppkg = struct {
|
||||
root: bool, // requested usage; never package-action identity
|
||||
linkentry: bool,
|
||||
generatedmain: bool,
|
||||
generatedtarget: i32,
|
||||
failed: bool,
|
||||
testsupport: bool,
|
||||
loaded: bool,
|
||||
@@ -1609,15 +1612,13 @@ fn seprootiscommand(p: *seppkg) bool = {
|
||||
};
|
||||
|
||||
fn sepforbiddencommandimport(g: *sepgraph, importer: i32, dep: i32) bool = {
|
||||
let from: *seppkg = &g.pkg[importer];
|
||||
let to: *seppkg = &g.pkg[dep];
|
||||
if (to.name == nil || !cstreqlit(to.name, "main")
|
||||
|| to.role != SEP_ROLE_NORMAL) { return false; };
|
||||
// An external test's exact colocated production edge is variant wiring,
|
||||
// not a general source-importable command-package alias.
|
||||
return !(from.variant == SEP_VARIANT_EXTERNAL
|
||||
&& sepcommanddeclaredname(from)
|
||||
&& cstreq(from.canon, to.canon));
|
||||
return !(sepexternalproductionedge(g, importer, dep)
|
||||
&& sepexternalnamematchesproduction(g, importer, dep));
|
||||
};
|
||||
|
||||
fn sepinternalparentcount(path: *u8, parents: *u64) bool = {
|
||||
@@ -2085,6 +2086,10 @@ fn sepgraphfree(g: *sepgraph) void = {
|
||||
os.free(g.pkg[i].bindings[bi].name.ptr: *void,
|
||||
g.pkg[i].bindings[bi].name.cap: u64);
|
||||
};
|
||||
if (g.pkg[i].bindings[bi].source.ptr != nil) {
|
||||
os.free(g.pkg[i].bindings[bi].source.ptr: *void,
|
||||
g.pkg[i].bindings[bi].source.cap: u64);
|
||||
};
|
||||
bi += 1;
|
||||
};
|
||||
if (g.pkg[i].bindings.ptr != nil) {
|
||||
@@ -2556,41 +2561,41 @@ fn sepvalidateartifactpaths(g: *sepgraph, scratch: *u8) i32 = {
|
||||
return 0;
|
||||
};
|
||||
|
||||
fn sepexternalname(pkg: *seppkg, path: *u8, n: u64,
|
||||
leafonly: bool) bool = {
|
||||
fn sepexternalname(pkg: *seppkg, path: *u8, n: u64) bool = {
|
||||
if (pkg.variant != SEP_VARIANT_EXTERNAL || pkg.testpackage == nil) {
|
||||
return false;
|
||||
};
|
||||
let begin: u64 = 0u64;
|
||||
if (leafonly) {
|
||||
let i: u64 = 0u64;
|
||||
for (i < n) {
|
||||
if (path[i] == '.') { begin = i + 1u64; };
|
||||
i += 1u64;
|
||||
};
|
||||
};
|
||||
let leafn: u64 = n - begin;
|
||||
let tn: u64 = cstrlen(pkg.testpackage);
|
||||
if (tn != leafn + 5u64) { return false; };
|
||||
if (bytecmp(pkg.testpackage, leafn, path + begin, leafn) != 0) {
|
||||
if (tn != n + 5u64) { return false; };
|
||||
if (bytecmp(pkg.testpackage, n, path, n) != 0) {
|
||||
return false;
|
||||
};
|
||||
return pkg.testpackage[leafn] == '_'
|
||||
&& pkg.testpackage[leafn + 1u64] == 't'
|
||||
&& pkg.testpackage[leafn + 2u64] == 'e'
|
||||
&& pkg.testpackage[leafn + 3u64] == 's'
|
||||
&& pkg.testpackage[leafn + 4u64] == 't';
|
||||
return pkg.testpackage[n] == '_'
|
||||
&& pkg.testpackage[n + 1u64] == 't'
|
||||
&& pkg.testpackage[n + 2u64] == 'e'
|
||||
&& pkg.testpackage[n + 3u64] == 's'
|
||||
&& pkg.testpackage[n + 4u64] == 't';
|
||||
};
|
||||
|
||||
fn sepexternalproductionedge(g: *sepgraph, importer: i32, dep: i32) bool = {
|
||||
let from: *seppkg = &g.pkg[importer];
|
||||
let to: *seppkg = &g.pkg[dep];
|
||||
return from.variant == SEP_VARIANT_EXTERNAL
|
||||
&& to.variant == SEP_VARIANT_PRODUCTION
|
||||
&& to.role == SEP_ROLE_NORMAL
|
||||
&& cstreq(from.canon, to.canon);
|
||||
};
|
||||
|
||||
fn sepexternalnamematchesproduction(g: *sepgraph, importer: i32,
|
||||
dep: i32) bool = {
|
||||
let from: *seppkg = &g.pkg[importer];
|
||||
let to: *seppkg = &g.pkg[dep];
|
||||
if (from.name == nil || to.name == nil) { return false; };
|
||||
return sepexternalname(from, to.name, cstrlen(to.name));
|
||||
};
|
||||
|
||||
fn sepbindadd(bindings: *[]sepbind, kind: u8, name: str,
|
||||
dep: i32) bool = {
|
||||
let i: i32 = 0;
|
||||
for (i < len(*bindings)) {
|
||||
let b: sepbind = (*bindings)[i];
|
||||
if (b.kind == kind && b.dep == dep
|
||||
&& syntax.streq(b.name, name)) { return true; };
|
||||
i += 1;
|
||||
};
|
||||
dep: i32, source: str, line: i32, col: i32) bool = {
|
||||
if (bindings.len == SEP_COUNT_MAX) { sepfailsize(); return false; };
|
||||
if (!sepreservebinds(bindings, bindings.len + 1)) {
|
||||
return false;
|
||||
@@ -2601,9 +2606,24 @@ fn sepbindadd(bindings: *[]sepbind, kind: u8, name: str,
|
||||
case let value: str => copied = value;
|
||||
case nomem => { sepfailnomem(); return false; };
|
||||
};
|
||||
let sourceallocation: (str | nomem) = sepdupstr(source);
|
||||
let sourcecopy: str;
|
||||
match (sourceallocation) {
|
||||
case let value: str => sourcecopy = value;
|
||||
case nomem => {
|
||||
if (copied.ptr != nil) {
|
||||
os.free(copied.ptr: *void, copied.cap: u64);
|
||||
};
|
||||
sepfailnomem();
|
||||
return false;
|
||||
};
|
||||
};
|
||||
append(*bindings, sepbind {
|
||||
kind = kind,
|
||||
name = copied,
|
||||
source = sourcecopy,
|
||||
line = line,
|
||||
col = col,
|
||||
dep = dep,
|
||||
});
|
||||
return true;
|
||||
@@ -2616,6 +2636,12 @@ fn sepbindcmp(a: sepbind, b: sepbind) i32 = {
|
||||
if (a.kind > b.kind) { return 1; };
|
||||
if (a.dep < b.dep) { return -1; };
|
||||
if (a.dep > b.dep) { return 1; };
|
||||
r = strings.compare(a.source, b.source): i32;
|
||||
if (r != 0) { return r; };
|
||||
if (a.line < b.line) { return -1; };
|
||||
if (a.line > b.line) { return 1; };
|
||||
if (a.col < b.col) { return -1; };
|
||||
if (a.col > b.col) { return 1; };
|
||||
return 0;
|
||||
};
|
||||
|
||||
@@ -2633,17 +2659,63 @@ fn sepbindsort(bindings: *[]sepbind) void = {
|
||||
};
|
||||
};
|
||||
|
||||
fn sepbindsemanticsame(a: sepbind, b: sepbind) bool = {
|
||||
return a.kind == b.kind && a.dep == b.dep
|
||||
&& syntax.streq(a.name, b.name);
|
||||
};
|
||||
|
||||
fn sepbindsame(a: []sepbind, b: []sepbind) bool = {
|
||||
if (len(a) != len(b)) { return false; };
|
||||
let ai: i32 = 0;
|
||||
let bi: i32 = 0;
|
||||
for (ai < len(a) && bi < len(b)) {
|
||||
if (!sepbindsemanticsame(a[ai], b[bi])) { return false; };
|
||||
let av: sepbind = a[ai];
|
||||
let bv: sepbind = b[bi];
|
||||
ai += 1;
|
||||
for (ai < len(a) && sepbindsemanticsame(av, a[ai])) { ai += 1; };
|
||||
bi += 1;
|
||||
for (bi < len(b) && sepbindsemanticsame(bv, b[bi])) { bi += 1; };
|
||||
};
|
||||
return ai == len(a) && bi == len(b);
|
||||
};
|
||||
|
||||
fn sepvalidatebindings(g: *sepgraph, bindings: []sepbind) bool = {
|
||||
let name: str;
|
||||
let dep: i32 = -1;
|
||||
let i: i32 = 0;
|
||||
for (i < len(a)) {
|
||||
if (a[i].kind != b[i].kind || a[i].dep != b[i].dep
|
||||
|| !syntax.streq(a[i].name, b[i].name)) { return false; };
|
||||
for (i < bindings.len) {
|
||||
let b: sepbind = bindings[i];
|
||||
if (b.kind == 'D': u8) {
|
||||
if (name.len > 0 && syntax.streq(name, b.name) && dep != b.dep) {
|
||||
cerrpos(b.source, b.line, b.col);
|
||||
cerr(": error: package path "); cerr(b.name);
|
||||
cerr(" resolves to both "); cerr(pathstr(g.pkg[dep].path));
|
||||
cerr(" and "); cerr(pathstr(g.pkg[b.dep].path)); cerr("\n");
|
||||
return false;
|
||||
};
|
||||
name = b.name;
|
||||
dep = b.dep;
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
return true;
|
||||
};
|
||||
|
||||
fn sepbindneedsmap(g: *sepgraph, b: sepbind) bool = {
|
||||
return b.kind == 'D': u8 && b.dep >= 0 && b.dep < g.n
|
||||
&& !syntax.streq(b.name, pathstr(g.pkg[b.dep].path));
|
||||
};
|
||||
|
||||
fn sepbindfirstmap(g: *sepgraph, bindings: []sepbind, i: i32) bool = {
|
||||
if (!sepbindneedsmap(g, bindings[i])) { return false; };
|
||||
let j: i32 = i - 1;
|
||||
for (j >= 0 && syntax.streq(bindings[j].name, bindings[i].name)) {
|
||||
if (sepbindneedsmap(g, bindings[j])) { return false; };
|
||||
j -= 1;
|
||||
};
|
||||
return true;
|
||||
};
|
||||
|
||||
fn sepchildrenadd(children: *[]sepchild, pkg: i32, context: i32) bool = {
|
||||
let i: i32 = 0;
|
||||
for (i < children.len) {
|
||||
@@ -2780,6 +2852,18 @@ fn sepresolvesourceimport(g: *sepgraph, context: i32, name: *u8,
|
||||
return 1;
|
||||
};
|
||||
|
||||
fn sepusecmp(a: *syntax.node, b: *syntax.node) i32 = {
|
||||
let r: i32 = strings.compare(a.usepath, b.usepath): i32;
|
||||
if (r != 0) { return r; };
|
||||
r = strings.compare(a.file, b.file): i32;
|
||||
if (r != 0) { return r; };
|
||||
if (a.line < b.line) { return -1; };
|
||||
if (a.line > b.line) { return 1; };
|
||||
if (a.col < b.col) { return -1; };
|
||||
if (a.col > b.col) { return 1; };
|
||||
return 0;
|
||||
};
|
||||
|
||||
// Scan one already-selected source file for its leading package clause
|
||||
// (when it is an owned directory source) and top-level imports. A DIRECTORY
|
||||
// import is a package boundary: add as a direct dep of pi. A FILE import is an
|
||||
@@ -2880,8 +2964,7 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, context: i32,
|
||||
for (si < nuse) {
|
||||
let sj: i32 = si;
|
||||
for (sj > 0) {
|
||||
if (strings.compare(uses[sj - 1].usepath,
|
||||
uses[sj].usepath) <= 0) { sj = 0; }
|
||||
if (sepusecmp(uses[sj - 1], uses[sj]) <= 0) { sj = 0; }
|
||||
else {
|
||||
let t: *syntax.node = uses[sj];
|
||||
uses[sj] = uses[sj - 1];
|
||||
@@ -2891,14 +2974,9 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, context: i32,
|
||||
};
|
||||
si += 1;
|
||||
};
|
||||
let previous: str = "";
|
||||
ui = 0;
|
||||
for (ui < nuse) {
|
||||
u = uses[ui];
|
||||
let duplicate: bool = previous.len > 0
|
||||
&& syntax.streq(previous, u.usepath);
|
||||
if (!duplicate) {
|
||||
previous = u.usepath;
|
||||
let idp: *u8 = u.usepath.ptr;
|
||||
let idn: u64 = u.usepath.len: u64;
|
||||
if (reservedimport(u.usepath)) {
|
||||
@@ -2933,11 +3011,10 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, context: i32,
|
||||
oi += 1u64;
|
||||
};
|
||||
let literalself: bool = routesuffix != nil
|
||||
&& cstreq(pathform, routesuffix)
|
||||
&& sepexternalname(&g.pkg[pi], idp, idn, true);
|
||||
&& cstreq(pathform, routesuffix);
|
||||
if (routesuffix == nil) {
|
||||
literalself = ordinaryleaf
|
||||
&& sepexternalname(&g.pkg[pi], idp, idn, false);
|
||||
&& sepexternalname(&g.pkg[pi], idp, idn);
|
||||
};
|
||||
if (literalself) {
|
||||
if (g.pkg[pi].importbase != nil) {
|
||||
@@ -2961,9 +3038,7 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, context: i32,
|
||||
let externalproduction: bool = false;
|
||||
let self: bool = os.samefile(pathstr(resolved.entry),
|
||||
pathstr(g.pkg[pi].entry));
|
||||
if (self && (sepexternalname(&g.pkg[pi], idp, idn, true)
|
||||
|| (g.pkg[pi].variant == SEP_VARIANT_EXTERNAL
|
||||
&& sepcommanddeclaredname(&g.pkg[pi])))) {
|
||||
if (self && g.pkg[pi].variant == SEP_VARIANT_EXTERNAL) {
|
||||
externalproduction = true;
|
||||
};
|
||||
if (self && !externalproduction) {
|
||||
@@ -3010,26 +3085,19 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, context: i32,
|
||||
let childcontext: i32 = sepchildcontextfor(g, context,
|
||||
resolved.entry, resolved.sourceroot);
|
||||
if (childcontext < 0
|
||||
|| !sepbindadd(bindings, 'D': u8, u.usepath, di)
|
||||
|| !sepbindadd(bindings, 'D': u8, u.usepath, di,
|
||||
u.file, u.line, u.col)
|
||||
|| !sepadddep(g, pi, di)
|
||||
|| !sepchildrenadd(children, di, childcontext)) {
|
||||
return -1;
|
||||
};
|
||||
} else {
|
||||
let lstart: u64 = 0u64;
|
||||
let lk: u64 = 0u64;
|
||||
for (lk < idn) {
|
||||
if (idp[lk] == 46u8) { lstart = lk + 1u64; }; // '.'
|
||||
lk += 1u64;
|
||||
};
|
||||
let leafp: *u8 = idp + lstart;
|
||||
let leafn: u64 = idn - lstart;
|
||||
let inlinepackage: bool = false;
|
||||
if (g.pkg[pi].isdir == 0) {
|
||||
let pm: *syntax.node = imports.body;
|
||||
for (pm != nil) {
|
||||
if (bytecmp(pm.nmod.ptr, pm.nmod.len: u64,
|
||||
leafp, leafn) == 0) { inlinepackage = true; };
|
||||
idp, idn) == 0) { inlinepackage = true; };
|
||||
pm = pm.next;
|
||||
};
|
||||
};
|
||||
@@ -3040,13 +3108,13 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, context: i32,
|
||||
cerr("\n");
|
||||
return -1;
|
||||
} else {
|
||||
if (!sepbindadd(bindings, 'I': u8, u.usepath, -1)) {
|
||||
if (!sepbindadd(bindings, 'I': u8, u.usepath, -1,
|
||||
u.file, u.line, u.col)) {
|
||||
return -1;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
ui += 1;
|
||||
ui += 1;
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
@@ -3160,6 +3228,7 @@ fn sepaddgeneratedmain(g: *sepgraph, product: *sepproduct, ordinal: i32,
|
||||
p.root = true;
|
||||
p.linkentry = true;
|
||||
p.generatedmain = true;
|
||||
p.generatedtarget = variant;
|
||||
p.failed = false;
|
||||
p.testsupport = false;
|
||||
p.loaded = true;
|
||||
@@ -3239,30 +3308,12 @@ fn seppreparepkgcontext(g: *sepgraph, pi: i32, context: i32,
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
if (rc == 0 && g.pkg[pi].path[0u64] != 0u8
|
||||
&& !g.pkg[pi].testsupport) {
|
||||
let plen: u64 = cstrlen(g.pkg[pi].path);
|
||||
let leaf: *u8 = g.pkg[pi].path;
|
||||
let j: u64 = 0u64;
|
||||
for (j < plen) {
|
||||
if (g.pkg[pi].path[j] == '.') { leaf = g.pkg[pi].path + j + 1u64; };
|
||||
j += 1u64;
|
||||
};
|
||||
if (!cstreq(g.pkg[pi].name, leaf)
|
||||
&& !sepcommanddeclaredname(&g.pkg[pi])) {
|
||||
cerr("ww: package ");
|
||||
cerr(pathstr(g.pkg[pi].name));
|
||||
cerr(" does not match import path ");
|
||||
cerr(pathstr(g.pkg[pi].path));
|
||||
cerr("\n");
|
||||
rc = -1;
|
||||
};
|
||||
};
|
||||
} else { if (rc == 0) {
|
||||
rc = sepscanfile(g, pi, g.pkg[pi].entry, context,
|
||||
&fv, &bindings, children, 0);
|
||||
}; };
|
||||
sepbindsort(&bindings);
|
||||
if (rc == 0 && !sepvalidatebindings(g, bindings)) { rc = -1; };
|
||||
if (rc == 0 && g.pkg[pi].emitcontext < 0) {
|
||||
g.pkg[pi].bindings = bindings;
|
||||
g.pkg[pi].emitcontext = context;
|
||||
@@ -3415,6 +3466,20 @@ fn seploadpkg(g: *sepgraph, pi: i32, context: i32) i32 = {
|
||||
if (f.pendingdep >= 0) {
|
||||
let dep: i32 = f.pendingdep;
|
||||
f.pendingdep = -1;
|
||||
if (dep != f.pkg && sepexternalproductionedge(g, f.pkg, dep)
|
||||
&& !sepexternalnamematchesproduction(g, f.pkg, dep)) {
|
||||
cerr("ww: external test package ");
|
||||
cerr(pathstr(g.pkg[f.pkg].name));
|
||||
cerr(" does not match production package ");
|
||||
cerr(pathstr(g.pkg[dep].name)); cerr("\n");
|
||||
let fi: i32 = 0;
|
||||
for (fi < nframe) {
|
||||
g.pkg[frames[fi].pkg].failed = true;
|
||||
sepclearchildren(&frames[fi].children);
|
||||
fi += 1;
|
||||
};
|
||||
return sepfinishloadframes(frames, -1);
|
||||
};
|
||||
if (dep != f.pkg && sepforbiddencommandimport(g, f.pkg, dep)) {
|
||||
cerr("ww: package ");
|
||||
if (g.pkg[dep].path[0u64] != 0u8) {
|
||||
@@ -3603,30 +3668,14 @@ fn sepreverseimportbase(g: *sepgraph, p: *seppkg, context: i32,
|
||||
return 0;
|
||||
};
|
||||
|
||||
fn sepordinarydeclaredname(p: *seppkg) *u8 = {
|
||||
if (p.name == nil || p.name[0u64] == 0u8) { return nil; };
|
||||
let n: u64 = cstrlen(p.name);
|
||||
if (p.variant == SEP_VARIANT_EXTERNAL) {
|
||||
if (n <= 5u64 || !cstrendswithlit(p.name, "_test")) {
|
||||
cerr("ww: package-test selector does not name an external package\n");
|
||||
return nil;
|
||||
};
|
||||
n -= 5u64;
|
||||
};
|
||||
return sepdupcstr(p.name, n);
|
||||
};
|
||||
|
||||
// The reserved local namespace is reversible, so filesystem identity never
|
||||
// depends on a hash, request order, output name, or another selected package.
|
||||
// depends on a hash, request order, output name, declared package name, or
|
||||
// another selected package.
|
||||
fn seplocalimportbase(p: *seppkg) *u8 = {
|
||||
let leaf: *u8 = sepordinarydeclaredname(p);
|
||||
if (leaf == nil) { return nil; };
|
||||
let need: u64 = 0u64;
|
||||
if (!sepaddbytes(&need, SEP_LOCAL_IMPORT_PREFIX.len: u64)
|
||||
|| !sepaddbytes(&need, 2u64)
|
||||
|| !sepmuladdbytes(&need, cstrlen(p.canon), 4u64)
|
||||
|| !sepaddbytes(&need, 1u64)
|
||||
|| !sepaddbytes(&need, cstrlen(leaf))
|
||||
|| !sepaddbytes(&need, 1u64)) { return nil; };
|
||||
let out: []u8;
|
||||
if (!sepmakebytes(need, &out)) { return nil; };
|
||||
@@ -3656,8 +3705,6 @@ fn seplocalimportbase(p: *seppkg) *u8 = {
|
||||
}; };
|
||||
i += 1u64;
|
||||
};
|
||||
off = byteinto(out.ptr, off, '.': u8);
|
||||
off = cstrinto(out.ptr, off, leaf);
|
||||
cstrseal(out.ptr, off);
|
||||
return out.ptr;
|
||||
};
|
||||
@@ -3725,23 +3772,6 @@ fn sepfinalizedirectoryidentities(g: *sepgraph) i32 = {
|
||||
for (pi < g.n) {
|
||||
let p: *seppkg = &g.pkg[pi];
|
||||
if (p.isdir != 0 && !p.generatedmain && !p.failed && p.loaded) {
|
||||
let plen: u64 = cstrlen(p.path);
|
||||
let leaf: *u8 = p.path;
|
||||
let j: u64 = 0u64;
|
||||
for (j < plen) {
|
||||
if (p.path[j] == '.') { leaf = p.path + j + 1u64; };
|
||||
j += 1u64;
|
||||
};
|
||||
let supportalias: bool = p.role == SEP_ROLE_TEST_SUPPORT
|
||||
&& cstreqlit(p.path, SEP_TEST_SUPPORT_MODULE)
|
||||
&& cstreqlit(p.name, "test");
|
||||
if (!supportalias && !cstreq(p.name, leaf)
|
||||
&& !sepcommanddeclaredname(p)) {
|
||||
cerr("ww: package "); cerr(pathstr(p.name));
|
||||
cerr(" does not match import path "); cerr(pathstr(p.path));
|
||||
cerr("\n");
|
||||
return -1;
|
||||
};
|
||||
p.artifact = nil;
|
||||
if (p.variant == SEP_VARIANT_SAME_TEST) {
|
||||
p.artifact = sepappendlit(p.path, "-internal-test");
|
||||
@@ -4014,8 +4044,7 @@ fn sepcomposeunit(g: *sepgraph, pi: i32, unitf: *u8) i32 = {
|
||||
let bi: i32 = 0;
|
||||
for (bi < g.pkg[pi].bindings.len && bodyrc == 0) {
|
||||
let b: sepbind = g.pkg[pi].bindings[bi];
|
||||
if (b.kind == 'D': u8 && b.dep >= 0 && b.dep < g.n
|
||||
&& !syntax.streq(b.name, pathstr(g.pkg[b.dep].path))) {
|
||||
if (sepbindfirstmap(g, g.pkg[pi].bindings, bi)) {
|
||||
let pre: str = "//ww:import-map ";
|
||||
let space: str = " ";
|
||||
let newline: str = "\n";
|
||||
@@ -4331,14 +4360,14 @@ fn validatecommandoutputpath(out: *u8) i32 = {
|
||||
fn workdirstamptext(istest: i32, emitasm: i32) str = {
|
||||
if (istest != 0) {
|
||||
if (emitasm != 0) {
|
||||
return "ww workdir fmt 12 mode test asm 1\n";
|
||||
return "ww workdir fmt 13 mode test asm 1\n";
|
||||
};
|
||||
return "ww workdir fmt 12 mode test asm 0\n";
|
||||
return "ww workdir fmt 13 mode test asm 0\n";
|
||||
};
|
||||
if (emitasm != 0) {
|
||||
return "ww workdir fmt 13 mode build asm 1\n";
|
||||
return "ww workdir fmt 14 mode build asm 1\n";
|
||||
};
|
||||
return "ww workdir fmt 13 mode build asm 0\n";
|
||||
return "ww workdir fmt 14 mode build asm 0\n";
|
||||
};
|
||||
|
||||
fn stampmatches(path: *u8, want: str) bool = {
|
||||
@@ -4434,6 +4463,22 @@ fn invalidateworkdirunits(scratch: *u8) i32 = {
|
||||
return rc;
|
||||
};
|
||||
|
||||
fn sepdiscardactionstaging(warm: bool, unit: *u8, wwi: *u8,
|
||||
assembly: *u8, object: *u8, archive: *u8) i32 = {
|
||||
if (!warm) { return 0; };
|
||||
let paths: []*u8 = [unit, wwi, assembly, object, archive];
|
||||
let i: i32 = 0;
|
||||
for (i < paths.len) {
|
||||
let rr: i32 = os.remove(pathstr(paths[i]));
|
||||
if (rr != 0 && rr != -2) {
|
||||
cerr("ww: cannot remove staged package artifacts\n");
|
||||
return -1;
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
|
||||
type sepcreateddirs = struct {
|
||||
path: [4096]u8,
|
||||
offset: [2048]u16,
|
||||
@@ -5092,7 +5137,16 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
cu = unitnew; cw = wwinew; cs = asmnew;
|
||||
co = objnew; ca = anew;
|
||||
};
|
||||
if (sepdiscardactionstaging(warm, unitnew, wwinew, asmnew,
|
||||
objnew, anew) < 0) {
|
||||
g.pkg[pi].failed = true;
|
||||
anyfailed = true;
|
||||
oi += 1;
|
||||
continue;
|
||||
};
|
||||
if (sepcomposeunit(g, pi, cu) < 0) {
|
||||
sepdiscardactionstaging(warm, unitnew, wwinew, asmnew,
|
||||
objnew, anew);
|
||||
g.pkg[pi].failed = true;
|
||||
anyfailed = true;
|
||||
oi += 1;
|
||||
@@ -5122,7 +5176,11 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
};
|
||||
};
|
||||
};
|
||||
if (sepfatalallocation) { return 1; };
|
||||
if (sepfatalallocation) {
|
||||
sepdiscardactionstaging(warm, unitnew, wwinew, asmnew,
|
||||
objnew, anew);
|
||||
return 1;
|
||||
};
|
||||
if (fresh) {
|
||||
if (os.remove(pathstr(unitnew)) != 0) {
|
||||
cerrpath("ww: cannot remove ", unitnew, "\n");
|
||||
@@ -5144,17 +5202,19 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
let nmaps: i32 = 0;
|
||||
let mapk: i32 = 0;
|
||||
for (mapk < g.pkg[pi].bindings.len) {
|
||||
let b: sepbind = g.pkg[pi].bindings[mapk];
|
||||
if (b.kind == 'D': u8 && b.dep >= 0 && b.dep < g.n
|
||||
&& !syntax.streq(b.name,
|
||||
pathstr(g.pkg[b.dep].path))) {
|
||||
if (nmaps == SEP_COUNT_MAX) { sepfailsize(); return 1; };
|
||||
if (sepbindfirstmap(g, g.pkg[pi].bindings, mapk)) {
|
||||
if (nmaps == SEP_COUNT_MAX) {
|
||||
sepfailsize();
|
||||
sepdiscardactionstaging(warm, unitnew, wwinew,
|
||||
asmnew, objnew, anew);
|
||||
return 1;
|
||||
};
|
||||
nmaps += 1;
|
||||
};
|
||||
mapk += 1;
|
||||
};
|
||||
let alen: i32 = 8;
|
||||
if (gent) { alen += 4; }
|
||||
if (gent) { alen += 4; if (g.pkg[pi].generatedmain) { alen += 2; }; }
|
||||
else {
|
||||
if (testpkg) { alen += 1; };
|
||||
if (commandpkg) { alen += 1; };
|
||||
@@ -5163,11 +5223,15 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
};
|
||||
if (g.pkg[pi].ndeps > (SEP_COUNT_MAX - alen) / 3) {
|
||||
sepfailsize();
|
||||
sepdiscardactionstaging(warm, unitnew, wwinew, asmnew,
|
||||
objnew, anew);
|
||||
return 1;
|
||||
};
|
||||
alen += g.pkg[pi].ndeps * 3;
|
||||
if (nmaps > (SEP_COUNT_MAX - alen) / 3) {
|
||||
sepfailsize();
|
||||
sepdiscardactionstaging(warm, unitnew, wwinew, asmnew,
|
||||
objnew, anew);
|
||||
return 1;
|
||||
};
|
||||
alen += nmaps * 3;
|
||||
@@ -5175,7 +5239,12 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
let argv: []str;
|
||||
match (allocation) {
|
||||
case let value: []str => argv = value;
|
||||
case nomem => { sepfailnomem(); return 1; };
|
||||
case nomem => {
|
||||
sepfailnomem();
|
||||
sepdiscardactionstaging(warm, unitnew, wwinew, asmnew,
|
||||
objnew, anew);
|
||||
return 1;
|
||||
};
|
||||
};
|
||||
append(argv, "w6c");
|
||||
if (gent) {
|
||||
@@ -5183,6 +5252,10 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
append(argv, "--entry");
|
||||
append(argv, "--test-support-module");
|
||||
append(argv, testsupportmodule);
|
||||
if (g.pkg[pi].generatedmain) {
|
||||
append(argv, "--test-target-package");
|
||||
append(argv, pathstr(g.pkg[g.pkg[pi].generatedtarget].path));
|
||||
};
|
||||
} else {
|
||||
if (testpkg) { append(argv, "--test-package"); };
|
||||
if (commandpkg) { append(argv, "--command-package"); };
|
||||
@@ -5199,16 +5272,18 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
append(argv, "--import");
|
||||
append(argv, pathstr(g.pkg[dj].path));
|
||||
let depinterface: *u8 = sepfname(g, dj, scratch, ".wwi");
|
||||
if (depinterface == nil) { return 1; };
|
||||
if (depinterface == nil) {
|
||||
sepdiscardactionstaging(warm, unitnew, wwinew,
|
||||
asmnew, objnew, anew);
|
||||
return 1;
|
||||
};
|
||||
append(argv, pathstr(depinterface));
|
||||
importk += 1;
|
||||
};
|
||||
mapk = 0;
|
||||
for (mapk < g.pkg[pi].bindings.len) {
|
||||
let b: sepbind = g.pkg[pi].bindings[mapk];
|
||||
if (b.kind == 'D': u8 && b.dep >= 0 && b.dep < g.n
|
||||
&& !syntax.streq(b.name,
|
||||
pathstr(g.pkg[b.dep].path))) {
|
||||
if (sepbindfirstmap(g, g.pkg[pi].bindings, mapk)) {
|
||||
append(argv, "--import-map");
|
||||
append(argv, b.name);
|
||||
append(argv, pathstr(g.pkg[b.dep].path));
|
||||
@@ -5237,6 +5312,8 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
};
|
||||
g.pkg[pi].failed = true;
|
||||
anyfailed = true;
|
||||
sepdiscardactionstaging(warm, unitnew, wwinew, asmnew,
|
||||
objnew, anew);
|
||||
oi += 1;
|
||||
continue;
|
||||
};
|
||||
@@ -5244,13 +5321,22 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
if (!warm || !fileequal(wwinew, wwi)) {
|
||||
g.pkg[pi].exportchanged = true;
|
||||
};
|
||||
if (sepfatalallocation) { return 1; };
|
||||
if (sepfatalallocation) {
|
||||
sepdiscardactionstaging(warm, unitnew, wwinew, asmnew,
|
||||
objnew, anew);
|
||||
return 1;
|
||||
};
|
||||
if (emitasm == 0) {
|
||||
let argallocation: ([]str | nomem) = sepallocstrs(4);
|
||||
let argv: []str;
|
||||
match (argallocation) {
|
||||
case let value: []str => argv = value;
|
||||
case nomem => { sepfailnomem(); return 1; };
|
||||
case nomem => {
|
||||
sepfailnomem();
|
||||
sepdiscardactionstaging(warm, unitnew, wwinew, asmnew,
|
||||
objnew, anew);
|
||||
return 1;
|
||||
};
|
||||
};
|
||||
append(argv, "w6a");
|
||||
append(argv, "-o");
|
||||
@@ -5273,6 +5359,8 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
};
|
||||
g.pkg[pi].failed = true;
|
||||
anyfailed = true;
|
||||
sepdiscardactionstaging(warm, unitnew, wwinew, asmnew,
|
||||
objnew, anew);
|
||||
oi += 1;
|
||||
continue;
|
||||
};
|
||||
@@ -5284,6 +5372,8 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
cerr("ww: archive failed\n");
|
||||
g.pkg[pi].failed = true;
|
||||
anyfailed = true;
|
||||
sepdiscardactionstaging(warm, unitnew, wwinew, asmnew,
|
||||
objnew, anew);
|
||||
oi += 1;
|
||||
continue;
|
||||
};
|
||||
@@ -5328,6 +5418,8 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
};
|
||||
g.pkg[pi].failed = true;
|
||||
anyfailed = true;
|
||||
sepdiscardactionstaging(warm, unitnew, wwinew, asmnew,
|
||||
objnew, anew);
|
||||
oi += 1;
|
||||
continue;
|
||||
};
|
||||
|
||||
@@ -148,7 +148,7 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
||||
// silently). Mirrors w6c main.ww:162 / cmd/w6c/main.c.
|
||||
if (l.errs > 0 || ps.errs > 0) { return 1; };
|
||||
let empty: str;
|
||||
if (wcc.compilefile(f, 0, 0, empty, 0, empty, 0) != 0) { return 1; };
|
||||
if (wcc.compilefile(f, 0, 0, empty, empty, 0, empty, 0) != 0) { return 1; };
|
||||
};};};};
|
||||
|
||||
if (l.errs > 0) { return 1; };
|
||||
|
||||
Reference in New Issue
Block a user