parse: enforce strict-package — reject package-less files (#24a)
Flip the soft-default to a hard "missing package clause" error symmetrically in both stages (cmd/wcc/parse.c + lib/ww/syntax/parse.ww): the first real decl of a primary section with empty pathmod/resetmod and no seen clause is now rejected. Closes the documented soft-default divergence (the 63-wrapper carve-out). The gate flip can't be split from the migration it breaks, so this is one atomic commit: ~80 test/wcc wrappers gain `package main;` via a shared wwtestpkg.h helper, 6 data fixtures plus 17 asm-grep assertions update for the bare->main.<leaf> root-helper mangle shift, and rt/ declares `package rt;` with @symbol pinning the bare rt_ensure/rt_malloc linker names. Root mangling narrows: the executable entry `main` stays bare (existing carve-out), but root helper symbols become main.X. The #84 cluster is rewritten to assert main.run distinct from aa.run/test.run; its cgen fix and bare machinery are retained — still load-bearing for package-less module-reset deps. New table-driven test 782_strict_package.c (6 rows, both stages). Retiring //ww:module-reset is deferred to #24b: it is load-bearing (clears the .wwi pathmod so the body's package clause asserts), not a vestige; fusing its removal here would be a silent mismatch. All byte-id gates green; full make test reports "all 335 tests passed".
This commit is contained in:
@@ -12,27 +12,23 @@
|
||||
* class silent miscompile, gate-blind and symmetric cs==ww. Surfaced by
|
||||
* #80's `ww test` coexist (lib/test exports `run`).
|
||||
*
|
||||
* Fixture (the minimal real repro): a dir-package `aa` exporting `run`,
|
||||
* imported by a PACKAGE-LESS root that defines its OWN bare `fn run` and
|
||||
* calls it from `main`. Package-less is REQUIRED: a `package main` root's
|
||||
* decls mangle `main.<leaf>` (the package clause sets the module), so its
|
||||
* `run` would be `main.run` — no collision with `aa.run`, vacuous. Only a
|
||||
* package-less primary yields a truly BARE `run`, the #84 trigger.
|
||||
* Strict-package (#24a) form: a dir-package `aa` exporting `run`, imported
|
||||
* by a `package main;` root that defines its OWN `fn run` and calls it from
|
||||
* `main`. The root's `run` mangles `main.run` — DISTINCT from the imported
|
||||
* `aa.run` and from the bare entry `main`. The original #84 trigger (a
|
||||
* truly BARE `run` from a PACKAGE-LESS root) is unreachable now that
|
||||
* strict-package forbids package-less primaries; the #84 cgen machinery
|
||||
* (mod_collect / mod_lookup_for_fn) stays dormant-but-load-bearing for
|
||||
* genuinely package-less `//ww:module-reset` deps until #24b/#26.
|
||||
*
|
||||
* Asserts (combined `ww build`, BOTH driver stages; the fix lives in
|
||||
* mod_collect/mod_lookup_for_fn which run in every mode, so combined is
|
||||
* the minimal repro — `--sep` reproduces identically):
|
||||
* 1. Build + run, BOTH stages → exit 9 (the USER's bare `run`, return 9),
|
||||
* Asserts (combined `ww build`, BOTH driver stages; `--sep` reproduces
|
||||
* identically):
|
||||
* 1. Build + run, BOTH stages → exit 9 (the user's `main.run`, return 9),
|
||||
* never aa.run (return 5).
|
||||
* 2. cs==ww (rule 10): the combined `.s` is byte-identical between the
|
||||
* two driver stages.
|
||||
* 3. NON-VACUITY + SOUNDNESS (the #84-specific signal): the `.s` has
|
||||
* EXACTLY ONE `TEXT run` (the user's, bare) AND EXACTLY ONE `TEXT
|
||||
* aa.run` (the import) — i.e. DISTINCT symbols, no dup. Pre-fix this
|
||||
* row fails: two `TEXT aa.run` (the user's `run` mis-mangled onto the
|
||||
* import) and zero bare `TEXT run`. (Exit alone is not a reliable
|
||||
* signal: pre-fix the dup happened to bind the user's copy under this
|
||||
* link order and still exited 9 — the label count is the proof.)
|
||||
* 3. DISTINCTNESS: the `.s` has EXACTLY ONE `TEXT main.run` (the user's)
|
||||
* AND EXACTLY ONE `TEXT aa.run` (the import) — distinct symbols, no dup.
|
||||
*
|
||||
* Light wwstage-driver test (CLAUDE.md rule 14): all intermediates are
|
||||
* `-o`-redirected to /tmp, phase-parallel-safe. Models 989_sepbuild_run.c.
|
||||
@@ -136,9 +132,13 @@ static const char *aa_src =
|
||||
"package aa;\n"
|
||||
"export fn run() i32 = { return 5; };\n";
|
||||
|
||||
/* PACKAGE-LESS root (no `package` clause) → its `fn run` is bare (the #84
|
||||
* trigger); a `package main` root would mangle it `main.run` (vacuous). */
|
||||
/* Strict-package (#24a) root: declares `package main;`, so its `fn run`
|
||||
* mangles `main.run` — DISTINCT from the imported `aa.run`, and from the
|
||||
* bare entry `main`. The bare call `run()` resolves to the local `main.run`
|
||||
* via the standard exact-hint path (the #84 special-case is bypassed for a
|
||||
* moduled root). */
|
||||
static const char *root_src =
|
||||
"package main;\n"
|
||||
"import aa;\n"
|
||||
"fn run() i32 = { return 9; };\n"
|
||||
"export fn main() i32 = { return run(); };\n";
|
||||
@@ -191,21 +191,20 @@ main(void)
|
||||
fail++;
|
||||
continue;
|
||||
}
|
||||
/* (1) the user's bare `run` (9) must win over imported aa.run (5). */
|
||||
/* (1) the user's `main.run` (9) must win over imported aa.run (5). */
|
||||
int rc = runwait(stg[s].prog);
|
||||
if (rc != 9) {
|
||||
fprintf(stderr, "84 FAIL: %s prog exit=%d expected 9 "
|
||||
"(bare user run, not aa.run=5)\n", stg[s].drv, rc);
|
||||
"(user main.run, not aa.run=5)\n", stg[s].drv, rc);
|
||||
fail++;
|
||||
}
|
||||
/* (3) distinct symbols, no dup: exactly one bare `run` + one
|
||||
* `aa.run`. Pre-fix: two `aa.run` (user run mis-mangled) + zero
|
||||
* bare `run`. */
|
||||
int nrun = count_text_label(stg[s].sfile, "run");
|
||||
/* (3) distinct symbols, no dup: exactly one `main.run` + one
|
||||
* `aa.run`. */
|
||||
int nrun = count_text_label(stg[s].sfile, "main.run");
|
||||
int naa = count_text_label(stg[s].sfile, "aa.run");
|
||||
if (nrun != 1 || naa != 1) {
|
||||
fprintf(stderr, "84 FAIL: %s labels TEXT run=%d aa.run=%d "
|
||||
"(want 1/1 — bare user run distinct from import, no dup)\n",
|
||||
fprintf(stderr, "84 FAIL: %s labels TEXT main.run=%d aa.run=%d "
|
||||
"(want 1/1 — user main.run distinct from import, no dup)\n",
|
||||
stg[s].drv, nrun, naa);
|
||||
fail++;
|
||||
}
|
||||
@@ -224,8 +223,8 @@ out:
|
||||
fprintf(stderr, "84: %d check(s) failed\n", fail);
|
||||
return 1;
|
||||
}
|
||||
printf("barefn_collide: package-less root `fn run` (bare) coexists with "
|
||||
"imported aa.run — distinct labels, no dup, user run wins (exit 9), "
|
||||
"cs==ww, both driver stages (#84)\n");
|
||||
printf("barefn_collide: `package main;` root `fn run` (main.run) coexists "
|
||||
"with imported aa.run — distinct labels, no dup, user run wins "
|
||||
"(exit 9), cs==ww, both driver stages (#84/#24a)\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user