wwi: derive decl-less module's .wwi package leaf from parse-stamped path (#11)

wwi_emit took the .wwi `package` leaf from the first primary decl's module tag;
a fully empty primary module body (zero decls) had none, so the leaf stayed the
literal default "main" and the importer rejected it ("package main does not
match import path <leaf>"). The module identity is only available at parse time
(curmod is overwritten by imported //ww:module sections before emit), so stamp
the primary path onto the N_FILE node (TK_MODULE and TK_MODRESET rp!=NULL sites,
only-if-empty so a bare-reset `package main` root stays "main") and, when the
decl-scan finds no leaf, fall back to that stamped path. Symmetric cstage+
selfhost; both detect scan-miss via the same found-flag so the emitted .wwi
stays byte-identical.

Regression: test/wcc/989_wwileaf_run.c, table-driven over {empty body,
comment-only, nested a.b.c} decl-less shapes, non-vacuity proven.
This commit is contained in:
2026-06-22 21:17:30 +09:00
parent a1484aef28
commit 6525e137ae
6 changed files with 352 additions and 4 deletions

View File

@@ -1,8 +1,9 @@
/*
* wwi.c — `.wwi` export-data producer (w6c -I). M2 DEAD-CODE: writes a
* re-parseable ww-prototype rendering of a package's EXPORTED surface.
* Nothing consumes `.wwi` yet (combined.ww stays the live path); the only
* caller is the new -I flag, off on every existing invocation.
* wwi.c — `.wwi` export-data producer (w6c -I): a re-parseable ww-prototype
* rendering of a package's EXPORTED surface. 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 as import scope;
* the combined.ww amalgamator is gone.
*
* Specs: .ai/rob-M2-spec.md (producer) + .ai/drew-M2-checkexported.md
* (check_exported_type). Two load-bearing choices follow them:
@@ -492,13 +493,27 @@ wwi_emit(Checker *c, FILE *of, Node *file)
/* package line: leaf of the first primary decl's module tag. */
const char *pkg = "main";
int found = 0;
for (Node *d = file->list; d; d = d->next) {
if (wwi_primary(d) && d->module && d->module[0]) {
const char *dot = strrchr(d->module, '.');
pkg = dot ? dot + 1 : d->module;
found = 1;
break;
}
}
/* #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
* and leaves file->module NULL, so it stays "main". The detector is
* scan-miss (`!found`), NOT pkg=="main": a body whose first tagged
* decl legitimately leafs to "main" must keep that, and must match
* selfhost's `found` flag byte-for-byte (rule 10). */
if (!found && file->module && file->module[0]) {
const char *dot = strrchr(file->module, '.');
pkg = dot ? dot + 1 : file->module;
}
fprintf(of, "package %s;\n", pkg);
/* imports — primary N_USE, byte-sorted by import path. */