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

@@ -448,6 +448,12 @@ export fn parsefile(p: *parser) *node = {
};
} else {
p.curmod = name;
// #11: stamp the primary module identity on the
// N_FILE node so wwi_emit can derive the `package`
// leaf even when the body carries zero module-tagged
// decls. Primary identity only; never the imported
// boundary (TK_MODPATH).
if (f.nmod.len == 0) { f.nmod = name; };
};
continue;
};
@@ -483,6 +489,11 @@ export fn parsefile(p: *parser) *node = {
if (rp.len != 0) {
p.curmod = rp;
p.resetmod = rp;
// #11: path-carrying reset is a primary body identity
// (sep); stamp it for the wwi leaf fallback. A bare
// reset (rp empty) is the root/package-less boundary
// and MUST keep the "main" default — so don't stamp.
if (f.nmod.len == 0) { f.nmod = rp; };
} else {
let empty: str;
empty.ptr = nil;