test: port the @test drop/keep/link observer to ww; retire 911_attest_drop

This commit is contained in:
2026-08-08 14:48:00 +09:00
parent b711e8c69d
commit a183e4f708
3 changed files with 179 additions and 276 deletions

177
test/tool/attest_test.ww Normal file
View File

@@ -0,0 +1,177 @@
package attest_test;
// The #6 @test DROP/KEEP/LINK emission property, driven by w6c /
// w6c_ww DIRECTLY on the import-free test/wcc/data/attest_* fixtures.
// Port of the retired native carrier test/wcc/911_attest_drop.c;
// every assertion preserved.
//
// Direct-frontend is the only host that can compare -T and non-T on
// the SAME input: `ww test` is always -T, `ww build` always non-T.
// A @test fn is spliced out of a non-test build (harec-faithful,
// ref/harec/src/check.c:3941 — checked but never emitted) and kept
// under -T (the synth entry references it); the -T synth's `test.run`
// callee stays an external CALL, so no driver resolution is needed.
//
// nondrop — attest_nondrop.ww compiled four ways (cs/ww x non-T/-T):
// the plain fn's TEXT def is present in both modes; the three @test
// TEXT defs are absent non-T and present under -T. The fixture layout
// exercises the splice loop's unlink edges (head @test, surviving
// plain fn, two consecutive @test). The presence rows run against the
// cstage .s; the cs==ww byte-id in BOTH modes (rule 10) carries them
// onto the wwstage .s by construction.
//
// undefbody — a @test body referencing an undefined symbol rejects in
// non-T on both frontends: the body is type-checked BEFORE the splice
// drops it (harec checks at :3913, drops at :3941).
//
// linkfail — non-test code CALLs a @test fn: non-T compile+assemble
// succeed per stage triple, but the link MUST fail, and the link
// stderr must name calldropped_test (non-vacuity: the failure is ON
// the dropped @test symbol — a libwwrt path drift failing every link
// would otherwise mask the property).
import os;
import os.exec;
import strings;
import testenv;
import time;
fn fail(label: str, why: str) void = {
let m: str = strings.concat("attest FAIL: ", label, " -- ", why,
"\n");
os.write(2, m.ptr, m.len: u64);
assert(false);
};
fn tmo() time.duration = {
return (60i64 * (time.second: i64)): time.duration;
};
fn runcode(dir: str, name: str, argv: []str) i32 = {
let co: testenv.commandout;
testenv.runcommand(dir, dir, name, argv, tmo(), &co);
if (co.termination != exec.termination.EXIT) { return -1; };
return co.code;
};
// column-0 anchored TEXT label; the '\n' prepend counts a
// file-leading label too.
fn hastext(s: str, sym: str) bool = {
return testenv.occurrences(strings.concat("\n", s),
strings.concat("\nTEXT ", sym, ",")) > 0;
};
@test fn nondrop() void = {
let td: str = testenv.fresh();
let fixture: str = strings.concat(testenv.repo(),
"/test/wcc/data/attest_nondrop.ww");
let comps: []str = ["w6c", "w6c", "w6c_ww", "w6c_ww"];
let testmode: []bool = [false, true, false, true];
let names: []str = ["cp", "ct", "wp", "wt"];
let asms: []str = ["", "", "", ""];
let j: i32 = 0;
for (j < 4) {
let outf: str = strings.concat(td, "/", names[j], ".s");
let av: []str = alloc([], 5u64)!;
append(av, testenv.driver(comps[j]));
if (testmode[j]) { append(av, "-T"); };
append(av, fixture);
append(av, "-o");
append(av, outf);
if (runcode(td, names[j], av) != 0) {
fail("nondrop", strings.concat(comps[j],
" nondrop compile failed"));
};
asms[j] = testenv.readfile(outf);
j += 1;
};
let syms: []str = ["data.nondrop_keep", "data.nondrop_test_a",
"data.nondrop_test_b", "data.nondrop_test_c",
"data.nondrop_keep", "data.nondrop_test_a",
"data.nondrop_test_b", "data.nondrop_test_c"];
let rowmode: []bool = [false, false, false, false,
true, true, true, true];
let present: []bool = [true, false, false, false,
true, true, true, true];
let whats: []str = ["plain fn kept non-T", "head @test dropped non-T",
"mid @test dropped non-T", "consecutive @test dropped non-T",
"plain fn kept under -T", "head @test kept under -T",
"mid @test kept under -T", "consecutive @test kept under -T"];
let i: i32 = 0;
for (i < syms.len) {
let s: str = asms[0];
if (rowmode[i]) { s = asms[1]; };
if (hastext(s, syms[i]) != present[i]) {
fail("nondrop", whats[i]);
};
i += 1;
};
if (!testenv.same(asms[0], asms[2])) {
fail("nondrop", "non-T @test-drop asm cs!=ww (rule 10)");
};
if (!testenv.same(asms[1], asms[3])) {
fail("nondrop", "-T @test-keep asm cs!=ww (rule 10)");
};
testenv.clean(td);
};
@test fn undefbody() void = {
let td: str = testenv.fresh();
let fixture: str = strings.concat(testenv.repo(),
"/test/wcc/data/attest_undefbody.ww");
let comps: []str = ["w6c", "w6c_ww"];
let c: i32 = 0;
for (c < 2) {
let av: []str = [testenv.driver(comps[c]), fixture, "-o",
"/dev/null"];
if (runcode(td, strings.concat("reject_", comps[c]), av) == 0) {
fail("undefbody", strings.concat(comps[c],
" (non-T) accepted an undefined symbol in a @test body"));
};
c += 1;
};
testenv.clean(td);
};
@test fn linkfail() void = {
let td: str = testenv.fresh();
let fixture: str = strings.concat(testenv.repo(),
"/test/wcc/data/attest_calldropped.ww");
let rt: str = strings.concat(testenv.repo(), "/out/lib/libwwrt.a");
let comps: []str = ["w6c", "w6c_ww"];
let asmts: []str = ["w6a", "w6a_ww"];
let linkts: []str = ["w6l", "w6l_ww"];
let s: i32 = 0;
for (s < 2) {
let asmf: str = strings.concat(td, "/lf.", comps[s], ".s");
let obj: str = strings.concat(td, "/lf.", comps[s], ".o");
let exe: str = strings.concat(td, "/lf.", comps[s], ".exe");
let cav: []str = [testenv.driver(comps[s]), fixture, "-o", asmf];
if (runcode(td, strings.concat("cc_", comps[s]), cav) != 0) {
fail("linkfail", strings.concat(comps[s],
" non-T calldropped compile failed"));
};
let aav: []str = [testenv.driver(asmts[s]), "-o", obj, asmf];
if (runcode(td, strings.concat("as_", asmts[s]), aav) != 0) {
fail("linkfail", strings.concat(asmts[s],
" calldropped assemble failed"));
};
let lo: testenv.commandout;
let lav: []str = [testenv.driver(linkts[s]), "-o", exe, obj, rt];
testenv.runcommand(td, td, strings.concat("ld_", linkts[s]), lav,
tmo(), &lo);
assert(lo.termination == exec.termination.EXIT);
if (lo.code == 0) {
fail("linkfail", strings.concat(linkts[s],
" linked calldropped (expected undefined-reference to ",
"the dropped @test sym)"));
};
if (!testenv.has(lo.stderr, "calldropped_test")) {
fail("linkfail", strings.concat(linkts[s],
" link failed but not on the dropped @test sym (masked)"));
};
s += 1;
};
testenv.clean(td);
};