wcc/ww: serialize aggregate exported defs as value-less .wwi prototypes (BUG-2, #70)

The .wwi (separate-compile interface) producer could not serialize an
exported def whose initializer is a struct/array literal (N_STRUCTLIT/
N_ARRLIT) — `export def f64info: floatinfo = floatinfo{...}` aborted with
"unhandled const-expr node kind 15". Such a def is a DATA-global per the
#52 model, so its value lives once in the defining package's .o; the
interface needs only the type+symbol. Emit a value-less prototype
`export def X: T;` for aggregate-initializer defs; scalar fold-eligible
defs keep their value (the importer const-folds those). The parser gains
an optional-init arm so the importer can parse the prototype — value-less
`def X: T;` is now legal in any source, symmetric with the existing
bodyless-fn prototype `fn f();` (USER ruling: unconditional; a value-less
def with no defining .o is a loud undefined-symbol error at link, never
silent). Both stages; producer + parser fold into one commit (the
producer's output is unparseable without the parser arm).

M3-tail commit-6 prerequisite #2 (surfaced by the c6 scout). The
aggregate-def-field const-fold boundary is documented inline (#71). Gate
989_sepstructdef_run proves struct+array exported defs sep-build, link,
and run via external DATA refs, cs==ww, with a value-less .wwi.
This commit is contained in:
2026-06-16 18:04:54 +09:00
parent 2ff54c8bd4
commit 10d005ef58
8 changed files with 346 additions and 17 deletions

View File

@@ -452,9 +452,23 @@ fn wwidecl(fd: i32, d: *node) void = {
wputs(fd, d.str);
wputs(fd, ": ");
wwitype(fd, d.lhs);
wputs(fd, " = ");
wwiexpr(fd, d.rhs);
wputs(fd, ";\n");
// An aggregate initializer (N_STRUCTLIT/N_ARRLIT) is a DATA-global
// (#52): emit a value-LESS prototype `export def X: T;`. The
// defining package's own .o emits the struct/array DATA; the
// importer registers the def by TYPE only (DATA-suppression
// already in cgen) and field/element reads become external refs
// the linker fills. Boundary (rule 7): this gives up importer-
// side const-fold of an aggregate def's FIELDS — a no-op, ww
// never folds struct-literal field access, and an aggregate-def
// field demanded in a const-fold context stays a LOUD error (#71).
if (d.rhs != nil && (d.rhs.kind == nkind.N_STRUCTLIT ||
d.rhs.kind == nkind.N_ARRLIT)) {
wputs(fd, ";\n");
} else {
wputs(fd, " = ");
wwiexpr(fd, d.rhs);
wputs(fd, ";\n");
};
} else { if (d.kind == nkind.N_LET) {
wputs(fd, "export let ");
wputs(fd, d.str);