compiler: separate package exports from entry roots
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
// wwi.ww — `.wwi` export-data producer (w6c_ww -I): a re-parseable
|
||||
// ww-prototype rendering of a package's EXPORTED surface. Since the
|
||||
// ww-prototype rendering of a package's exported surface and compiler-private
|
||||
// closure. Since the
|
||||
// sep-compile flip (epic #22) this is the LIVE import path — the driver
|
||||
// runs one `w6c -c -I` per package and feeds each dep's `.wwi` to its
|
||||
// importers through a separate canonical `--import` input.
|
||||
@@ -10,9 +11,9 @@
|
||||
//
|
||||
// - Unparse walks the AST type-expr subtree (the N_T* nodes), NOT the
|
||||
// tinfo — tinfo collapses nominal pkg.Name identity.
|
||||
// - check_exported_type rides the producer entry (flag-gated), off on
|
||||
// the normal `.s` path. It rejects exactly one thing: an exported
|
||||
// signature naming a non-exported nominal type.
|
||||
// - Reachable owner-private nominal types are encoded without `export`.
|
||||
// Consumers reconstruct public signatures from them, but source cannot
|
||||
// qualify those private spellings.
|
||||
// - A `.wwi` is ONE package's self-contained interface. Its primary
|
||||
// section is followed by compiler-owned origin sections containing the
|
||||
// exported foreign type/const facts reachable from the public surface.
|
||||
@@ -196,28 +197,8 @@ fn wwichecktype(c: *checker, owner: str, d: *syntax.node, t: *syntax.node) i32 =
|
||||
if (t.kind == syntax.nkind.N_TPARAM) { return wwichecktype(c, owner, d, t.lhs); };
|
||||
let bad: i32 = 0;
|
||||
if (t.kind == syntax.nkind.N_TNAME) {
|
||||
let s: *syntax.sym = wwitypesym(c, owner, t.str);
|
||||
// sym.exported is vestigial (never set); the nominal's export
|
||||
// status lives on its decl node, parser-set.
|
||||
if (s != nil) {
|
||||
if (s.decl != nil) {
|
||||
if (s.decl.kind == syntax.nkind.N_TYPEDECL) {
|
||||
// file.len == 0 ⇒ a checkinit-synthesized
|
||||
// predeclared builtin (the ONLY empty-source
|
||||
// decls: nomemdecl check.ww:126, the `void`
|
||||
// TNAME check.ww:122), not a user nominal — ww's
|
||||
// analogue of harec's STORAGE_NOMEM leaf-arm, and
|
||||
// why cstage (lookup_builtin, no scope SK_TYPE)
|
||||
// needs no such guard.
|
||||
if (s.decl.file.len > 0) {
|
||||
if (s.decl.exported == 0) {
|
||||
wwireject(d, t.str);
|
||||
bad = 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
// Nominal references, including private ones, are collected into
|
||||
// the self-contained fact closure below.
|
||||
} else { if (
|
||||
t.kind == syntax.nkind.N_TPTR ||
|
||||
t.kind == syntax.nkind.N_TSLICE ||
|
||||
@@ -513,10 +494,9 @@ fn wwitype(fd: i32, t: *syntax.node) void = {
|
||||
// fficollect — dropping it makes sep-compile emit `CALL malloc` for a
|
||||
// `@symbol("rt_malloc")` fn). Named for the class so @align/@offset would
|
||||
// slot in here IF ww ever grows field-layout attributes — it has none
|
||||
// today (task #47 report). @test never reaches a `.wwi` (test fns are not
|
||||
// export-marked), so it needs no exclusion arm.
|
||||
// today (task #47 report). @test is compiler-private package-test metadata.
|
||||
fn wwiattrrelevant(nm: str) bool = {
|
||||
return syntax.streq(nm, "symbol");
|
||||
return syntax.streq(nm, "symbol") || syntax.streq(nm, "test");
|
||||
};
|
||||
|
||||
fn wwiattrs(fd: i32, d: *syntax.node) void = {
|
||||
@@ -544,7 +524,8 @@ fn wwiattrs(fd: i32, d: *syntax.node) void = {
|
||||
fn wwidecl(fd: i32, d: *syntax.node) void = {
|
||||
if (d.kind == syntax.nkind.N_FNDECL) {
|
||||
wwiattrs(fd, d);
|
||||
wputs(fd, "export fn ");
|
||||
if (d.exported != 0) { wputs(fd, "export fn "); }
|
||||
else { wputs(fd, "fn "); };
|
||||
wputs(fd, d.str);
|
||||
wputb(fd, '(');
|
||||
let p: *syntax.node = d.list;
|
||||
@@ -557,13 +538,15 @@ fn wwidecl(fd: i32, d: *syntax.node) void = {
|
||||
wwitype(fd, d.lhs);
|
||||
wputs(fd, ";\n");
|
||||
} else { if (d.kind == syntax.nkind.N_TYPEDECL) {
|
||||
wputs(fd, "export type ");
|
||||
if (d.exported != 0) { wputs(fd, "export type "); }
|
||||
else { wputs(fd, "type "); };
|
||||
wputs(fd, d.str);
|
||||
wputs(fd, " = ");
|
||||
wwitype(fd, d.lhs);
|
||||
wputs(fd, ";\n");
|
||||
} else { if (d.kind == syntax.nkind.N_DEF) {
|
||||
wputs(fd, "export def ");
|
||||
if (d.exported != 0) { wputs(fd, "export def "); }
|
||||
else { wputs(fd, "def "); };
|
||||
wputs(fd, d.str);
|
||||
wputs(fd, ": ");
|
||||
wwitype(fd, d.lhs);
|
||||
@@ -585,7 +568,8 @@ fn wwidecl(fd: i32, d: *syntax.node) void = {
|
||||
wputs(fd, ";\n");
|
||||
};
|
||||
} else { if (d.kind == syntax.nkind.N_LET) {
|
||||
wputs(fd, "export let ");
|
||||
if (d.exported != 0) { wputs(fd, "export let "); }
|
||||
else { wputs(fd, "let "); };
|
||||
wputs(fd, d.str);
|
||||
wputs(fd, ": ");
|
||||
if (d.lhs == nil) {
|
||||
@@ -609,6 +593,17 @@ fn wwiisdecl(d: *syntax.node) bool = {
|
||||
d.kind == syntax.nkind.N_DEF || d.kind == syntax.nkind.N_LET;
|
||||
};
|
||||
|
||||
fn wwihasattr(d: *syntax.node, name: str) bool = {
|
||||
let a: *syntax.node = d.attr;
|
||||
for (a != nil) {
|
||||
if (a.kind == syntax.nkind.N_ATTR && syntax.streq(a.str, name)) {
|
||||
return true;
|
||||
};
|
||||
a = a.next;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
// Deterministic ordering (rob §3): byte-lexicographic, mirror C strcmp
|
||||
// sign (<0/0/>0). Both stages key the sort identically, so the `.wwi`
|
||||
// order is deterministic.
|
||||
@@ -648,6 +643,9 @@ fn wwisortdecls(keys: []str, nodes: []*syntax.node, n: i32) void = {
|
||||
// representation narrow and make the deterministic ordering explicit.
|
||||
type wwifactset = struct {
|
||||
c: *checker,
|
||||
privatenodes: []*syntax.node,
|
||||
privatekeys: []str,
|
||||
nprivate: i32,
|
||||
factnodes: []*syntax.node,
|
||||
factmods: []str,
|
||||
factranks: []i32,
|
||||
@@ -730,8 +728,21 @@ fn wwicollectdecl(fs: *wwifactset, owner: str, d: *syntax.node) void = {
|
||||
fs.seenranks[fs.nseen] = rank;
|
||||
fs.nseen += 1;
|
||||
|
||||
if (owner.len > 0) {
|
||||
if (d.exported == 0) {
|
||||
if (owner.len == 0 && d.exported == 0) {
|
||||
// checkinit's synthetic nomem/void declarations have no source file.
|
||||
// They are builtins, not owner-private package facts; cstage resolves
|
||||
// these through lookup_builtin and therefore never collects them.
|
||||
if (d.file.len == 0) { return; };
|
||||
if (d.kind != syntax.nkind.N_TYPEDECL) {
|
||||
wwifactreject(d, "def", d.str);
|
||||
fs.bad = 1;
|
||||
return;
|
||||
};
|
||||
fs.privatenodes[fs.nprivate] = d;
|
||||
fs.privatekeys[fs.nprivate] = d.str;
|
||||
fs.nprivate += 1;
|
||||
} else { if (owner.len > 0) {
|
||||
if (d.exported == 0 && d.kind != syntax.nkind.N_TYPEDECL) {
|
||||
let kind: str = "def";
|
||||
if (d.kind == syntax.nkind.N_TYPEDECL) { kind = "type"; };
|
||||
wwifactreject(d, kind, d.str);
|
||||
@@ -742,7 +753,7 @@ fn wwicollectdecl(fs: *wwifactset, owner: str, d: *syntax.node) void = {
|
||||
fs.factmods[fs.nfacts] = owner;
|
||||
fs.factranks[fs.nfacts] = rank;
|
||||
fs.nfacts += 1;
|
||||
};
|
||||
}; };
|
||||
|
||||
if (d.kind == syntax.nkind.N_FNDECL) {
|
||||
let p: *syntax.node = d.list;
|
||||
@@ -925,12 +936,15 @@ fn wwiemit(c: *checker, file: *syntax.node, path: str) i32 = {
|
||||
if (nall == 0) { nall = 1; };
|
||||
let fs: wwifactset;
|
||||
fs.c = c;
|
||||
let privatenodes: []*syntax.node = alloc([], nall: u64)!; privatenodes.len = nall;
|
||||
let privatekeys: []str = alloc([], nall: u64)!; privatekeys.len = nall;
|
||||
let factnodes: []*syntax.node = alloc([], nall: u64)!; factnodes.len = nall;
|
||||
let factmods: []str = alloc([], nall: u64)!; factmods.len = nall;
|
||||
let factranks: []i32 = alloc([], nall: u64)!; factranks.len = nall;
|
||||
let seennodes: []*syntax.node = alloc([], nall: u64)!; seennodes.len = nall;
|
||||
let seenmods: []str = alloc([], nall: u64)!; seenmods.len = nall;
|
||||
let seenranks: []i32 = alloc([], nall: u64)!; seenranks.len = nall;
|
||||
fs.privatenodes = privatenodes; fs.privatekeys = privatekeys;
|
||||
fs.factnodes = factnodes; fs.factmods = factmods; fs.factranks = factranks;
|
||||
fs.seennodes = seennodes; fs.seenmods = seenmods; fs.seenranks = seenranks;
|
||||
let primary: str;
|
||||
@@ -942,6 +956,7 @@ fn wwiemit(c: *checker, file: *syntax.node, path: str) i32 = {
|
||||
d = d.next;
|
||||
};
|
||||
if (fs.bad != 0) { return 1i32; };
|
||||
wwisortdecls(fs.privatekeys, fs.privatenodes, fs.nprivate);
|
||||
wwisortfacts(&fs);
|
||||
|
||||
let fd: i32 = os.open(path,
|
||||
@@ -982,7 +997,7 @@ fn wwiemit(c: *checker, file: *syntax.node, path: str) i32 = {
|
||||
// #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 real root `package main` arrives via a bare module-reset
|
||||
// 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".
|
||||
@@ -1042,6 +1057,27 @@ fn wwiemit(c: *checker, file: *syntax.node, path: str) i32 = {
|
||||
};
|
||||
};
|
||||
|
||||
// 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.
|
||||
let ndecl: i32 = 0;
|
||||
d = file.list;
|
||||
|
||||
Reference in New Issue
Block a user