test: prove declared package import semantics

This commit is contained in:
2026-08-14 03:08:16 +09:00
parent 6efe9b70d4
commit a841278333
11 changed files with 1001 additions and 131 deletions

View File

@@ -21,15 +21,16 @@ package wwi_test;
// qualification of the compiler-private name on both stages.
//
// wwileaf — BUG-C (#11) regression pin: a decl-less / export-less
// primary module's `.wwi` `package` line must carry the module's real
// leaf, NOT the literal default "main". Table-driven over the three
// primary module's `.wwi` must preserve its independently parsed declaration,
// rather than substituting the literal default "main". Table-driven over the three
// decl-less shapes routing the wwi_emit fallback (empty, comment-only,
// nested dotted path a.b.c -> leaf c). Each row drives the REAL
// producer->importer flow on BOTH stages: (a) the dep `.wwi` package
// owner marker is the complete path and its package line equals the real leaf,
// (b) the dep `.wwi` is byte-identical
// across stages, (c) a root importing the dep RESOLVES on both stages
// and the two importer `.s` are byte-identical. The owner units and
// and both importers diagnose the same file-local unused binding after
// obtaining that declared name from export data. The owner units and
// separate exports are fed straight to w6c / w6c_ww (not `ww build`):
// the producer-unit shape `//ww:module-reset <path>` + body is exactly
// what the driver's sep_emit_body emits.
@@ -331,8 +332,8 @@ fn leafrow(tag: str, path: str, leaf: str, body: str, want: str) void = {
"package ", leaf, ";\n",
body));
// Leg a — both stages emit the dep `.wwi`; the `package` leaf is
// the module's real leaf, not the default "main".
// Leg a — both stages emit the dep `.wwi`; the `package` declaration is
// the source declaration supplied independently of its canonical owner.
let cav: []str = [testenv.driver("w6c"), "-c", "-I", cswwi,
"-o", css, prod];
if (!runok(td, "csprod", cav)) { fail(tag, "w6c producer errored"); };
@@ -342,7 +343,7 @@ fn leafrow(tag: str, path: str, leaf: str, body: str, want: str) void = {
let wanthead: str = strings.concat(strings.concat(strings.concat(
"//ww:module ", path), "\npackage "), strings.concat(want, ";\n"));
if (!strings.hasprefix(testenv.readfile(cswwi), wanthead)) {
fail(tag, ".wwi owner or package leaf is incomplete (BUG-C)");
fail(tag, ".wwi owner or declared package name is incomplete (BUG-C)");
};
// Leg b — the dep `.wwi` is byte-identical across stages (rule 10).
@@ -350,9 +351,11 @@ fn leafrow(tag: str, path: str, leaf: str, body: str, want: str) void = {
fail(tag, "w6c vs w6c_ww .wwi differ (rule 10)");
};
// Leg c — a root importing the dep RESOLVES from a separate export on
// both stages (pre-fix: "package main does not match import path"
// REJECT).
// Leg c — a root importing the empty dep obtains its default qualifier from
// the separate export on both stages. With no exported declaration to use,
// Go-style file scope requires the import itself to be rejected as unused;
// the diagnostic proves that the reader recovered the declared name rather
// than substituting the historical default `main` declaration.
testenv.writefile(root, strings.concat(
"//ww:module-reset\n",
"package main;\n",
@@ -360,22 +363,59 @@ fn leafrow(tag: str, path: str, leaf: str, body: str, want: str) void = {
"export fn main() i32 = { return 42; };\n"));
let rcav: []str = [testenv.driver("w6c"), "-c", "--import", path,
cswwi, "-o", rcss, root];
if (!runok(td, "csroot", rcav)) {
fail(tag, "w6c rejected the import (BUG-C)");
};
let rwav: []str = [testenv.driver("w6c_ww"), "-c", "--import", path,
wwwwi, "-o", rwws, root];
if (!runok(td, "wsroot", rwav)) {
fail(tag, "w6c_ww rejected the import (BUG-C)");
let rco: testenv.commandout;
let rwo: testenv.commandout;
testenv.runcommand(td, td, "csroot", rcav, lifetime(), &rco);
testenv.runcommand(td, td, "wsroot", rwav, lifetime(), &rwo);
let (prefix, suffix) = strings.rcut(path, ".");
let leaf: str = suffix;
if (leaf.len == 0) { leaf = path; };
let unused: str;
if (testenv.same(want, leaf)) {
unused = strings.concat(strings.concat("\"", path),
"\" imported and not used");
} else {
unused = strings.concat(strings.concat(strings.concat(
"\"", path), "\" imported as "), strings.concat(want,
" and not used"));
};
if (!testenv.same(testenv.readfile(rcss), testenv.readfile(rwws))) {
fail(tag, "importer .s differ cs vs ww (rule 10)");
if (rco.termination != exec.termination.EXIT || rco.code == 0
|| rwo.termination != exec.termination.EXIT || rwo.code == 0
|| !testenv.same(rco.stderr, rwo.stderr)
|| !testenv.has(rco.stderr, unused)) {
fail(tag, "imported-name or unused-import behavior differs by stage");
};
testenv.clean(td);
};
fn resetfallbackrow() void = {
let td: str = testenv.fresh();
let src: str = strings.concat(td, "/reset.ww");
testenv.writefile(src, strings.concat(
"//ww:module-reset raw.owner\n",
"export fn value() i32 = { return 7; };\n"));
let cs: str = strings.concat(td, "/c.wwi");
let ws: str = strings.concat(td, "/w.wwi");
let cav: []str = [testenv.driver("w6c"), "-c", "-I", cs,
"-o", strings.concat(td, "/c.s"), src];
let wav: []str = [testenv.driver("w6c_ww"), "-c", "-I", ws,
"-o", strings.concat(td, "/w.s"), src];
if (!runok(td, "reset-c", cav) || !runok(td, "reset-w", wav)) {
fail("nested", "a package-less reset owner failed to export");
};
let body: str = testenv.readfile(cs);
if (!testenv.same(body, testenv.readfile(ws))
|| !testenv.has(body, "//ww:module raw.owner\npackage main;\n")
|| !testenv.has(body, "export fn value() i32;")) {
fail("nested", "reset-owner fallback lost its source section");
};
testenv.clean(td);
};
// Each shape lacks any module-tagged decl, so wwi_emit's decl-scan
// misses and the leaf must come from the parse-stamped N_FILE identity.
// misses and the declaration must come from the parse-stamped N_FILE fact.
@test fn wwileaf_empty() void = {
leafrow("empty", "emptymod", "emptymod", "", "emptymod");
};
@@ -386,4 +426,5 @@ fn leafrow(tag: str, path: str, leaf: str, body: str, want: str) void = {
@test fn wwileaf_nested() void = {
leafrow("nested", "a.b.c", "c", "", "c");
resetfallbackrow();
};