runww: //ww:error arm asserts both stages reject (dual-stage)
A rejected program emits no .s, so the test-lang-byteid (T2) gate cannot cover wwstage-reject -- yet the retired C twins asserted that BOTH stages reject with the same diagnostic. runww's //ww:error arm was cstage-only, so migrating reject rows onto it would silently drop the wwstage-reject coverage the C twins carried. Run w6c_ww (wwstage) alongside w6c (cstage) on each //ww:error case and require both to fail with the shared diagnostic body present. The body is identical across stages; only cstage's leading prefix differs, so the substring matches the body alone (no file:line). An ERROR row now reports PASS dual / FAIL cstage / FAIL wwstage. w6c_ww resolves off the same $BIN as the C twins -- no new harness threading. Two pilot reject cases (runww_dup_main_reject, runww_dup_type_reject) exercise the dual-stage path; the wwstage leg is proven non-vacuous (a cstage-rejects/wwstage-accepts case reports FAIL wwstage). Prerequisite for migrating fold-3 reject rows onto runww.
This commit is contained in:
@@ -5,14 +5,22 @@
|
|||||||
// directive holds. This replaces the per-test C harnesses that embedded ww
|
// directive holds. This replaces the per-test C harnesses that embedded ww
|
||||||
// source as C string literals and duplicated fork/exec/grep boilerplate.
|
// source as C string literals and duplicated fork/exec/grep boilerplate.
|
||||||
//
|
//
|
||||||
// This is T1: the routine behavioral-correctness gate, cstage-only. The
|
// This is T1: the routine behavioral-correctness gate, cstage-only for the
|
||||||
// wwstage-behavior run is deliberately NOT here — per the rob/USER tier+stage
|
// RUN/RUNEXIT/COMPILE arms. The wwstage-behavior run is deliberately NOT here
|
||||||
// split, a wwstage miscompile is already caught by T2 (cstage.s vs wwstage.s
|
// — per the rob/USER tier+stage split, a wwstage miscompile is already caught
|
||||||
// byte-id over the same case.ww corpus, strictly more sensitive than re-running
|
// by T2 (cstage.s vs wwstage.s byte-id over the same case.ww corpus, strictly
|
||||||
// behavior) or by T1's own cstage run (the both-wrong-identical blind spot is
|
// more sensitive than re-running behavior) or by T1's own cstage run (the
|
||||||
// cstage being wrong, which only a behavioral check on cstage catches). So
|
// both-wrong-identical blind spot is cstage being wrong, which only a
|
||||||
// rule-10 stage-symmetry is enforced by T2 (a separate pre-push tool), not by
|
// behavioral check on cstage catches). So rule-10 stage-symmetry is enforced
|
||||||
// runww; routine runs stay cstage-only.
|
// by T2 (a separate pre-push tool), not by runww; routine runs stay cstage-only.
|
||||||
|
//
|
||||||
|
// The ERROR arm is the one exception: it runs BOTH stages. A rejected program
|
||||||
|
// emits no `.s`, so T2's `.s`-cmp gate structurally CANNOT cover wwstage-reject
|
||||||
|
// (drew-fold3-spec §1a). The retired C reject twins (948/949/944) asserted both
|
||||||
|
// w6c AND w6c_ww reject with the same diagnostic body, so runww's ERROR arm runs
|
||||||
|
// w6c_ww directly on the same case.ww and asserts it ALSO fails with the
|
||||||
|
// substring present (#20 non-vacuity) — the coverage-equivalence safety net for
|
||||||
|
// reject-row migration.
|
||||||
//
|
//
|
||||||
// Directives (first `//ww:` line of the case):
|
// Directives (first `//ww:` line of the case):
|
||||||
// //ww:run compile + execute, expect exit 0
|
// //ww:run compile + execute, expect exit 0
|
||||||
@@ -244,6 +252,29 @@ fn runcase(drv: str, casepath: str, d: *dirv, errpath: str, tmpout: str) bool =
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// wwerror — the ERROR arm's wwstage leg: invoke w6c_ww directly on the same
|
||||||
|
// case.ww (mirroring the retired C twins' `$BIN/w6c_ww -o <out> <src>` direct
|
||||||
|
// invocation, not the `ww` driver — w6c_ww is the bare frontend compiler) and
|
||||||
|
// assert it ALSO hard-rejects with the shared diagnostic body present. #20
|
||||||
|
// non-vacuity: passes ONLY if w6c_ww failed AND the substring is on stderr.
|
||||||
|
fn wwerror(wdrv: str, casepath: str, d: *dirv, errpath: str, tmpout: str) bool = {
|
||||||
|
let argv: []*u8 = alloc([], 5u64)!;
|
||||||
|
argv.len = 5;
|
||||||
|
argv[0] = cstr(wdrv);
|
||||||
|
argv[1] = cstr("-o");
|
||||||
|
argv[2] = cstr(tmpout);
|
||||||
|
argv[3] = cstr(casepath);
|
||||||
|
argv[4] = nil: *u8;
|
||||||
|
|
||||||
|
let rc: i32 = runcap(wdrv, argv, errpath);
|
||||||
|
if (rc == 0) { return false; }; // w6c_ww accepted → wwstage-reject dropped
|
||||||
|
let eb: []u8 = alloc([], 65536u64)!;
|
||||||
|
eb.len = 65536;
|
||||||
|
let en: i64 = readfile(errpath, eb);
|
||||||
|
if (en < 0i64) { return false; };
|
||||||
|
return contains(eb.ptr, en: i32, mkstr(d.subp, d.subn));
|
||||||
|
};
|
||||||
|
|
||||||
export fn main() int = {
|
export fn main() int = {
|
||||||
let a: []str = os.args();
|
let a: []str = os.args();
|
||||||
if (a.len < 2) {
|
if (a.len < 2) {
|
||||||
@@ -257,6 +288,9 @@ export fn main() int = {
|
|||||||
case void => { };
|
case void => { };
|
||||||
};
|
};
|
||||||
let cdrv: str = joinp(bin, "/ww");
|
let cdrv: str = joinp(bin, "/ww");
|
||||||
|
// ERROR arm goes dual-stage: w6c_ww (the bare wwstage frontend) is
|
||||||
|
// resolved off the SAME $BIN the C reject twins used ($BIN/w6c_ww).
|
||||||
|
let wdrv: str = joinp(bin, "/w6c_ww");
|
||||||
|
|
||||||
// Fixed scratch paths: the pilot drives cases sequentially in one
|
// Fixed scratch paths: the pilot drives cases sequentially in one
|
||||||
// process, so reuse is safe; per-pid uniqueness for parallel invocation
|
// process, so reuse is safe; per-pid uniqueness for parallel invocation
|
||||||
@@ -289,11 +323,20 @@ export fn main() int = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
total += 1;
|
total += 1;
|
||||||
if (runcase(cdrv, casepath, &d, errpath, tmpout)) {
|
let pass: bool = runcase(cdrv, casepath, &d, errpath, tmpout);
|
||||||
pr("PASS cstage "); pr(casepath); pr("\n");
|
if (!pass) {
|
||||||
} else {
|
|
||||||
pr("FAIL cstage "); pr(casepath); pr("\n");
|
pr("FAIL cstage "); pr(casepath); pr("\n");
|
||||||
fail += 1;
|
fail += 1;
|
||||||
|
} else if (d.kind == dkind.ERROR) {
|
||||||
|
// cstage rejected; now demand wwstage rejects identically.
|
||||||
|
if (wwerror(wdrv, casepath, &d, errpath, tmpout)) {
|
||||||
|
pr("PASS dual "); pr(casepath); pr("\n");
|
||||||
|
} else {
|
||||||
|
pr("FAIL wwstage "); pr(casepath); pr("\n");
|
||||||
|
fail += 1;
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
pr("PASS cstage "); pr(casepath); pr("\n");
|
||||||
};
|
};
|
||||||
|
|
||||||
ci += 1;
|
ci += 1;
|
||||||
|
|||||||
8
test/wcc/data/runww_dup_main_reject/case.ww
Normal file
8
test/wcc/data/runww_dup_main_reject/case.ww
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
//ww:error "duplicate fn main"
|
||||||
|
// Pilot for #7a: two `fn main` definitions must be LOUD-rejected by BOTH
|
||||||
|
// stages. The substring is the shared diagnostic body (no file:line prefix,
|
||||||
|
// which differs cstage vs wwstage) — the #20 non-vacuity anchor and the
|
||||||
|
// dual-stage (w6c AND w6c_ww) reject-coverage proof.
|
||||||
|
package main;
|
||||||
|
fn main() i32 = { return 0; };
|
||||||
|
fn main() i32 = { return 1; };
|
||||||
9
test/wcc/data/runww_dup_type_reject/case.ww
Normal file
9
test/wcc/data/runww_dup_type_reject/case.ww
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
//ww:error "duplicate type t"
|
||||||
|
// Pilot for #7a: a redefined type alias must be LOUD-rejected by BOTH stages.
|
||||||
|
// The substring is the shared diagnostic body (no file:line prefix, which
|
||||||
|
// differs cstage vs wwstage) — the #20 non-vacuity anchor and the dual-stage
|
||||||
|
// (w6c AND w6c_ww) reject-coverage proof.
|
||||||
|
package main;
|
||||||
|
type t = i32;
|
||||||
|
type t = i64;
|
||||||
|
fn main() i32 = { return 0; };
|
||||||
Reference in New Issue
Block a user