test: port the M3 .wwi-consumer keystone to ww; retire 989_m3sep
m3sep_test.ww drives the raw w6c/w6a/w6l toolchains (+_ww twins) over hand-composed //ww:module units splicing produced .wwi bytes: the bodies==.wwi keystone on both stages per package (mid/root/smid) with determinism, rule-10 and the value-global DATA guard; the end-to-end i32 chain (exit 59) with final-exe byte-id; and the #49 str-label / #53 export-leaf link legs (exits 160/140) with their .s needle proofs. Each @test re-produces its .wwi cold instead of sharing the carrier's single scratch dir (same claims, sharper isolation).
This commit is contained in:
2
Makefile
2
Makefile
@@ -364,7 +364,7 @@ BYTEID_WW_TARGETS = $(BYTEID_WW_TESTS:%=wwtest/%)
|
||||
# test-compiler beside the surviving residual carriers.
|
||||
SEP_WW_TESTS = test/sep/sepbuild_test.ww test/sep/sepimport_test.ww \
|
||||
test/sep/seplink_test.ww test/sep/sepscratch_test.ww \
|
||||
test/sep/septest_test.ww
|
||||
test/sep/septest_test.ww test/sep/m3sep_test.ww
|
||||
SEP_WW_TARGETS = $(SEP_WW_TESTS:%=wwtest/%)
|
||||
BOOTSTRAP_WRAPPER_SOURCES = test/wcc/950_selfcheck.c \
|
||||
test/wcc/991_w6a_ww.c \
|
||||
|
||||
491
test/sep/m3sep_test.ww
Normal file
491
test/sep/m3sep_test.ww
Normal file
@@ -0,0 +1,491 @@
|
||||
package m3sep_test;
|
||||
|
||||
// M3 `.wwi` separate-compilation CONSUMER gate (task #22 arc). Port of
|
||||
// the retired native carrier test/wcc/989_m3sep_run.c; every assertion
|
||||
// preserved. Drives the raw w6c/w6a/w6l toolchains (+_ww twins), never
|
||||
// the ww driver: the composed //ww:module units splice PRODUCED .wwi
|
||||
// bytes between compile steps — an inter-stage artifact flow no
|
||||
// driver invocation or fixture can express.
|
||||
//
|
||||
// keystone (rob §4.1, the load-bearing claim) — compiling package P
|
||||
// under `-c` with deps as `.wwi` emits byte-identical codegen to deps
|
||||
// as full BODIES, holding the -c filter constant and varying only the
|
||||
// dep-source form; proven per package (mid, root, smid) on BOTH
|
||||
// stages (a wwstage-only guard regression cannot hide behind the
|
||||
// .wwi-sourced sep unit), plus cs==ww on the sep unit (rule 10),
|
||||
// sep-path determinism (same input twice -> identical), and the
|
||||
// value-global guard: an imported `export let` must never re-emit its
|
||||
// DATA[W] definition (link collision). The smid row exercises the
|
||||
// strlit-intern guard: neutralize it and sleaf.banner interns ahead
|
||||
// of smid.label in the bodies-unit, desyncing `_S_` -> keystone RED.
|
||||
//
|
||||
// chain — end-to-end: leaf/mid/root compiled -c, assembled, linked
|
||||
// with libwwrt.a and RUN through both full toolchains -> exit 59 (the
|
||||
// real cross-boundary computation over all four fact-classes: fn
|
||||
// signature, struct layout, def const, export-let global); the two
|
||||
// final executables byte-identical (rule 10 end-to-end).
|
||||
//
|
||||
// str49 (#49) — two str-bearing packages sep-LINK without an `_S_`
|
||||
// label collision (per-unit prefix): exit 160 reads a distinguishing
|
||||
// byte THROUGH each str's .ptr; cstage .s carries `sleaf._S_` /
|
||||
// `smid._S_` and no bare `DATA _S_0(SB)`.
|
||||
//
|
||||
// leaf53 (#53) — two packages exporting the SAME-named non-fn leaves
|
||||
// (`let v`, `def K`) sep-LINK because every exported decl
|
||||
// path-qualifies (cea.v/ceb.v, cea.K/ceb.K, no bare v/K DATA): exit
|
||||
// 140 proves each getter reads its OWN module's data.
|
||||
//
|
||||
// Dropped C machinery, not assertions: the ~70-entry remove_child
|
||||
// cleanup ledger (testenv.clean asserts the recursive removal).
|
||||
|
||||
import os;
|
||||
import os.exec;
|
||||
import strings;
|
||||
import testenv;
|
||||
import time;
|
||||
|
||||
fn fail(label: str, why: str) void = {
|
||||
let m: str = strings.concat("m3sep FAIL: ", label, " -- ", why, "\n");
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
assert(false);
|
||||
};
|
||||
|
||||
fn tmo() time.duration = {
|
||||
return (180i64 * (time.second: i64)): time.duration;
|
||||
};
|
||||
|
||||
// -1 encodes an abnormal (non-EXIT) termination, never a valid code.
|
||||
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;
|
||||
};
|
||||
|
||||
// one //ww:module section: directive line, body bytes, closing newline
|
||||
// (the exact append_section shape the carrier composed).
|
||||
fn sec(directive: str, body: str) str = {
|
||||
return strings.concat(directive, "\n", body, "\n");
|
||||
};
|
||||
|
||||
// The fixture: leaf -> mid -> root carries all FOUR cross-boundary
|
||||
// fact-classes (fn signature, struct layout, def const, export-let
|
||||
// global). root exit = combine(mkpoint(3,4)) + add(1,2) = 56 + 3 = 59.
|
||||
fn leafsrc() str = {
|
||||
return strings.concat(
|
||||
"package leaf;\n",
|
||||
"export type Point = struct { x: i32, y: i32 };\n",
|
||||
"export def SCALE: i32 = 7;\n",
|
||||
"export let origin_tag: i32 = 42;\n",
|
||||
"export fn add(a: i32, b: i32) i32 = { return a + b; };\n",
|
||||
"export fn mkpoint(x: i32, y: i32) Point = { return Point { x = x, y = y }; };\n",
|
||||
"fn leafpriv(z: i32) i32 = { return z * SCALE; };\n");
|
||||
};
|
||||
|
||||
fn midsrc() str = {
|
||||
return strings.concat(
|
||||
"package mid;\n",
|
||||
"import leaf;\n",
|
||||
"export fn combine(p: leaf.Point) i32 = {\n",
|
||||
" return leaf.add(p.x, p.y) + leaf.SCALE + leaf.origin_tag;\n",
|
||||
"};\n");
|
||||
};
|
||||
|
||||
fn rootsrc() str = {
|
||||
return strings.concat(
|
||||
"package main;\n",
|
||||
"import leaf;\n",
|
||||
"import mid;\n",
|
||||
"fn main() i32 = {\n",
|
||||
" let p = leaf.mkpoint(3, 4);\n",
|
||||
" let r = mid.combine(p);\n",
|
||||
" r = r + leaf.add(1, 2);\n",
|
||||
" return r;\n",
|
||||
"};\n");
|
||||
};
|
||||
|
||||
// str sub-fixture: sleaf's export-let interns a strlit in sleaf's own
|
||||
// compile but is stripped from sleaf.wwi — the strlit-intern guard's
|
||||
// non-vacuity source (see keystone smid row).
|
||||
fn sleafsrc() str = {
|
||||
return strings.concat(
|
||||
"package sleaf;\n",
|
||||
"export let banner: str = \"sleaf-banner\";\n",
|
||||
"export fn tag(a: i32) i32 = { return a + 1; };\n",
|
||||
"export fn getbanner() str = { return banner; };\n");
|
||||
};
|
||||
|
||||
fn smidsrc() str = {
|
||||
return strings.concat(
|
||||
"package smid;\n",
|
||||
"import sleaf;\n",
|
||||
"export let label: str = \"smid-label\";\n",
|
||||
"export fn use(a: i32) i32 = { return sleaf.tag(a); };\n",
|
||||
"export fn getlabel() str = { return label; };\n");
|
||||
};
|
||||
|
||||
// getbanner()[0] = 's'(115), getlabel()[4] = '-'(45) -> 160; a collided
|
||||
// `_S_0` resolving to the wrong package's bytes corrupts the exit.
|
||||
fn srootsrc() str = {
|
||||
return strings.concat(
|
||||
"package main;\n",
|
||||
"import sleaf;\n",
|
||||
"import smid;\n",
|
||||
"fn main() i32 = {\n",
|
||||
" let a: str = sleaf.getbanner();\n",
|
||||
" let b: str = smid.getlabel();\n",
|
||||
" return (a[0]: i32) + (b[4]: i32);\n",
|
||||
"};\n");
|
||||
};
|
||||
|
||||
// cea.val()=43, ceb.val()=97 -> 140; a collided bare v/K resolving to
|
||||
// the wrong package's data corrupts the exit.
|
||||
fn ceasrc() str = {
|
||||
return strings.concat(
|
||||
"package cea;\n",
|
||||
"export let v: i64 = 40;\n",
|
||||
"export def K: i64 = 3;\n",
|
||||
"export fn val() i64 = { return v + K; };\n");
|
||||
};
|
||||
|
||||
fn cebsrc() str = {
|
||||
return strings.concat(
|
||||
"package ceb;\n",
|
||||
"export let v: i64 = 90;\n",
|
||||
"export def K: i64 = 7;\n",
|
||||
"export fn val() i64 = { return v + K; };\n");
|
||||
};
|
||||
|
||||
fn cxrootsrc() str = {
|
||||
return strings.concat(
|
||||
"package main;\n",
|
||||
"import cea;\n",
|
||||
"import ceb;\n",
|
||||
"fn main() i32 = { return (cea.val(): i32) + (ceb.val(): i32); };\n");
|
||||
};
|
||||
|
||||
// produce a package's .wwi (w6c -I, .s discarded); the production
|
||||
// itself must succeed.
|
||||
fn produce(td: str, name: str, unit: str, wwi: str) void = {
|
||||
let av: []str = [testenv.driver("w6c"), "-I", wwi, "-o", "/dev/null",
|
||||
unit];
|
||||
if (runcode(td, strings.concat("prod_", name), av) != 0) {
|
||||
fail(name, ".wwi produce failed");
|
||||
};
|
||||
};
|
||||
|
||||
fn compilestep(td: str, label: str, step: str, comp: str, outs: str,
|
||||
unit: str) void = {
|
||||
let av: []str = [testenv.driver(comp), "-c", "-o", outs, unit];
|
||||
if (runcode(td, strings.concat(label, "_", step), av) != 0) {
|
||||
fail(label, strings.concat(comp, " -c failed (", step, ")"));
|
||||
};
|
||||
};
|
||||
|
||||
// the per-package keystone: 5 compiles over the bodies/sep unit pair,
|
||||
// then bodies==sep (both stages), cs==ww, determinism, and the
|
||||
// imported-value-global DATA guard.
|
||||
fn keystone(td: str, label: str, bodies: str, sepu: str) void = {
|
||||
let bf: str = strings.concat(td, "/", label, ".bodies.ww");
|
||||
testenv.writefile(bf, bodies);
|
||||
let sf: str = strings.concat(td, "/", label, ".sep.ww");
|
||||
testenv.writefile(sf, sepu);
|
||||
let csb: str = strings.concat(td, "/", label, ".bodies.cs.s");
|
||||
let css: str = strings.concat(td, "/", label, ".sep.cs.s");
|
||||
let wws: str = strings.concat(td, "/", label, ".sep.ww.s");
|
||||
let css2: str = strings.concat(td, "/", label, ".sep.cs2.s");
|
||||
let wwb: str = strings.concat(td, "/", label, ".bodies.ww.s");
|
||||
compilestep(td, label, "bcs", "w6c", csb, bf);
|
||||
compilestep(td, label, "scs", "w6c", css, sf);
|
||||
compilestep(td, label, "sww", "w6c_ww", wws, sf);
|
||||
compilestep(td, label, "scs2", "w6c", css2, sf);
|
||||
compilestep(td, label, "bww", "w6c_ww", wwb, bf);
|
||||
if (!testenv.same(testenv.readfile(csb), testenv.readfile(css))) {
|
||||
fail(label, strings.concat("bodies.s != sep.s (the .wwi does not ",
|
||||
"convey the dep facts P's codegen needs)"));
|
||||
};
|
||||
if (!testenv.same(testenv.readfile(wwb), testenv.readfile(wws))) {
|
||||
fail(label, "ww bodies.s != ww sep.s (wwstage keystone)");
|
||||
};
|
||||
if (!testenv.same(testenv.readfile(css), testenv.readfile(wws))) {
|
||||
fail(label, "cs sep.s != ww sep.s (rule 10)");
|
||||
};
|
||||
if (!testenv.same(testenv.readfile(css), testenv.readfile(css2))) {
|
||||
fail(label, "sep codegen non-deterministic");
|
||||
};
|
||||
// definition guard only: a READ (LEAQ origin_tag) is legitimate in a
|
||||
// dependent; the DATA[W] definition belongs to leaf's compile alone.
|
||||
let s: str = testenv.readfile(css);
|
||||
if (testenv.has(s, "DATAW origin_tag(SB)")
|
||||
|| testenv.has(s, "DATA origin_tag(SB)")) {
|
||||
fail(label, "imported value-global re-emitted (link collision)");
|
||||
};
|
||||
};
|
||||
|
||||
// compile (-c) + assemble one package unit through one toolchain.
|
||||
fn buildpkg(td: str, name: str, comp: str, asmtool: str, unit: str,
|
||||
sf: str, of: str) void = {
|
||||
let cav: []str = [testenv.driver(comp), "-c", "-o", sf, unit];
|
||||
if (runcode(td, strings.concat("c_", name), cav) != 0) {
|
||||
fail(name, strings.concat(comp, " -c failed"));
|
||||
};
|
||||
let aav: []str = [testenv.driver(asmtool), "-o", of, sf];
|
||||
if (runcode(td, strings.concat("a_", name), aav) != 0) {
|
||||
fail(name, strings.concat(asmtool, " failed"));
|
||||
};
|
||||
};
|
||||
|
||||
// materialize leaf.ww + leaf.wwi + mid.prod.ww + mid.wwi under td.
|
||||
fn producecore(td: str) void = {
|
||||
let leafww: str = strings.concat(td, "/leaf.ww");
|
||||
testenv.writefile(leafww, leafsrc());
|
||||
produce(td, "leaf", leafww, strings.concat(td, "/leaf.wwi"));
|
||||
let prod: str = strings.concat(td, "/mid.prod.ww");
|
||||
testenv.writefile(prod, strings.concat(
|
||||
sec("//ww:module leaf",
|
||||
testenv.readfile(strings.concat(td, "/leaf.wwi"))),
|
||||
sec("//ww:module-reset", midsrc())));
|
||||
produce(td, "mid", prod, strings.concat(td, "/mid.wwi"));
|
||||
};
|
||||
|
||||
// materialize sleaf.ww + sleaf.wwi (+ optionally smid.wwi) under td.
|
||||
fn producestr(td: str, withsmid: bool) void = {
|
||||
let sleafww: str = strings.concat(td, "/sleaf.ww");
|
||||
testenv.writefile(sleafww, sleafsrc());
|
||||
produce(td, "sleaf", sleafww, strings.concat(td, "/sleaf.wwi"));
|
||||
if (!withsmid) { return; };
|
||||
let prod: str = strings.concat(td, "/smid.prod.ww");
|
||||
testenv.writefile(prod, strings.concat(
|
||||
sec("//ww:module sleaf",
|
||||
testenv.readfile(strings.concat(td, "/sleaf.wwi"))),
|
||||
sec("//ww:module-reset", smidsrc())));
|
||||
produce(td, "smid", prod, strings.concat(td, "/smid.wwi"));
|
||||
};
|
||||
|
||||
@test fn m3keystone() void = {
|
||||
let td: str = testenv.fresh();
|
||||
producecore(td);
|
||||
producestr(td, false);
|
||||
let rl: str = testenv.readfile(strings.concat(td, "/leaf.wwi"));
|
||||
let rm: str = testenv.readfile(strings.concat(td, "/mid.wwi"));
|
||||
let rsl: str = testenv.readfile(strings.concat(td, "/sleaf.wwi"));
|
||||
keystone(td, "mid",
|
||||
strings.concat(sec("//ww:module leaf", leafsrc()),
|
||||
sec("//ww:module-reset", midsrc())),
|
||||
strings.concat(sec("//ww:module leaf", rl),
|
||||
sec("//ww:module-reset", midsrc())));
|
||||
keystone(td, "root",
|
||||
strings.concat(sec("//ww:module leaf", leafsrc()),
|
||||
sec("//ww:module mid", midsrc()),
|
||||
sec("//ww:module-reset", rootsrc())),
|
||||
strings.concat(sec("//ww:module leaf", rl),
|
||||
sec("//ww:module mid", rm),
|
||||
sec("//ww:module-reset", rootsrc())));
|
||||
keystone(td, "smid",
|
||||
strings.concat(sec("//ww:module sleaf", sleafsrc()),
|
||||
sec("//ww:module-reset", smidsrc())),
|
||||
strings.concat(sec("//ww:module sleaf", rsl),
|
||||
sec("//ww:module-reset", smidsrc())));
|
||||
testenv.clean(td);
|
||||
};
|
||||
|
||||
@test fn m3chain() void = {
|
||||
let td: str = testenv.fresh();
|
||||
producecore(td);
|
||||
let rl: str = testenv.readfile(strings.concat(td, "/leaf.wwi"));
|
||||
let rm: str = testenv.readfile(strings.concat(td, "/mid.wwi"));
|
||||
let midsep: str = strings.concat(td, "/mid.sep.ww");
|
||||
testenv.writefile(midsep, strings.concat(
|
||||
sec("//ww:module leaf", rl),
|
||||
sec("//ww:module-reset", midsrc())));
|
||||
let rootsep: str = strings.concat(td, "/root.sep.ww");
|
||||
testenv.writefile(rootsep, strings.concat(
|
||||
sec("//ww:module leaf", rl),
|
||||
sec("//ww:module mid", rm),
|
||||
sec("//ww:module-reset", rootsrc())));
|
||||
|
||||
let comps: []str = ["w6c", "w6c_ww"];
|
||||
let asms: []str = ["w6a", "w6a_ww"];
|
||||
let lnks: []str = ["w6l", "w6l_ww"];
|
||||
let tags: []str = ["cs", "ww"];
|
||||
let rt: str = strings.concat(testenv.repo(), "/out/lib/libwwrt.a");
|
||||
let exes: []str = ["", ""];
|
||||
let t: i32 = 0;
|
||||
for (t < 2) {
|
||||
let lo: str = strings.concat(td, "/leaf.", tags[t], ".o");
|
||||
let mo: str = strings.concat(td, "/mid.", tags[t], ".o");
|
||||
let ro: str = strings.concat(td, "/root.", tags[t], ".o");
|
||||
buildpkg(td, strings.concat("leaf_", tags[t]), comps[t], asms[t],
|
||||
strings.concat(td, "/leaf.ww"),
|
||||
strings.concat(td, "/leaf.", tags[t], ".s"), lo);
|
||||
buildpkg(td, strings.concat("mid_", tags[t]), comps[t], asms[t],
|
||||
midsep, strings.concat(td, "/mid.", tags[t], ".s"), mo);
|
||||
buildpkg(td, strings.concat("root_", tags[t]), comps[t], asms[t],
|
||||
rootsep, strings.concat(td, "/root.", tags[t], ".s"), ro);
|
||||
let exe: str = strings.concat(td, "/prog.", tags[t]);
|
||||
let lav: []str = [testenv.driver(lnks[t]), "-o", exe, ro, mo, lo,
|
||||
rt];
|
||||
if (runcode(td, strings.concat("l_", tags[t]), lav) != 0) {
|
||||
fail("chain", strings.concat(lnks[t], " link failed"));
|
||||
};
|
||||
let rav: []str = [exe];
|
||||
if (runcode(td, strings.concat("run_", tags[t]), rav) != 59) {
|
||||
fail("chain", strings.concat(tags[t], " sep program exit != 59 ",
|
||||
"(cross-boundary behavior wrong)"));
|
||||
};
|
||||
exes[t] = exe;
|
||||
t += 1;
|
||||
};
|
||||
// symbol order is identical (same object set, same order); the .s
|
||||
// are byte-id, so the linked images must match too.
|
||||
if (!testenv.same(testenv.readfile(exes[0]),
|
||||
testenv.readfile(exes[1]))) {
|
||||
fail("chain", "cs exe != ww exe (rule 10, final image)");
|
||||
};
|
||||
testenv.clean(td);
|
||||
};
|
||||
|
||||
@test fn m3str49() void = {
|
||||
let td: str = testenv.fresh();
|
||||
producestr(td, true);
|
||||
let rsl: str = testenv.readfile(strings.concat(td, "/sleaf.wwi"));
|
||||
let rsm: str = testenv.readfile(strings.concat(td, "/smid.wwi"));
|
||||
let smidsep: str = strings.concat(td, "/smid.sep.ww");
|
||||
testenv.writefile(smidsep, strings.concat(
|
||||
sec("//ww:module sleaf", rsl),
|
||||
sec("//ww:module-reset", smidsrc())));
|
||||
let srootsep: str = strings.concat(td, "/sroot.sep.ww");
|
||||
testenv.writefile(srootsep, strings.concat(
|
||||
sec("//ww:module sleaf", rsl),
|
||||
sec("//ww:module smid", rsm),
|
||||
sec("//ww:module-reset", srootsrc())));
|
||||
|
||||
let comps: []str = ["w6c", "w6c_ww"];
|
||||
let asms: []str = ["w6a", "w6a_ww"];
|
||||
let lnks: []str = ["w6l", "w6l_ww"];
|
||||
let tags: []str = ["cs", "ww"];
|
||||
let rt: str = strings.concat(testenv.repo(), "/out/lib/libwwrt.a");
|
||||
let exes: []str = ["", ""];
|
||||
let t: i32 = 0;
|
||||
for (t < 2) {
|
||||
let ls: str = strings.concat(td, "/sleaf.", tags[t], ".s");
|
||||
let ms: str = strings.concat(td, "/smid.", tags[t], ".s");
|
||||
let lo: str = strings.concat(td, "/sleaf.", tags[t], ".o");
|
||||
let mo: str = strings.concat(td, "/smid.", tags[t], ".o");
|
||||
let ro: str = strings.concat(td, "/sroot.", tags[t], ".o");
|
||||
buildpkg(td, strings.concat("sleaf_", tags[t]), comps[t], asms[t],
|
||||
strings.concat(td, "/sleaf.ww"), ls, lo);
|
||||
buildpkg(td, strings.concat("smid_", tags[t]), comps[t], asms[t],
|
||||
smidsep, ms, mo);
|
||||
buildpkg(td, strings.concat("sroot_", tags[t]), comps[t], asms[t],
|
||||
srootsep, strings.concat(td, "/sroot.", tags[t], ".s"), ro);
|
||||
// #49 structural proof (cstage .s once): module-prefixed strlit
|
||||
// labels, never a shared bare _S_0
|
||||
if (t == 0) {
|
||||
let sl: str = testenv.readfile(ls);
|
||||
let sm: str = testenv.readfile(ms);
|
||||
if (!testenv.has(sl, "sleaf._S_")
|
||||
|| !testenv.has(sm, "smid._S_")) {
|
||||
fail("str49", "strlit label not module-prefixed");
|
||||
};
|
||||
if (testenv.has(sl, "DATA _S_0(SB)")
|
||||
|| testenv.has(sm, "DATA _S_0(SB)")) {
|
||||
fail("str49",
|
||||
"bare _S_0 survives (cross-unit link collision)");
|
||||
};
|
||||
};
|
||||
let exe: str = strings.concat(td, "/sprog.", tags[t]);
|
||||
let lav: []str = [testenv.driver(lnks[t]), "-o", exe, ro, mo, lo,
|
||||
rt];
|
||||
if (runcode(td, strings.concat("l_", tags[t]), lav) != 0) {
|
||||
fail("str49", strings.concat(lnks[t],
|
||||
" str-pkg sep link failed (#49)"));
|
||||
};
|
||||
let rav: []str = [exe];
|
||||
if (runcode(td, strings.concat("run_", tags[t]), rav) != 160) {
|
||||
fail("str49", strings.concat(tags[t], " str-link exit != 160 ",
|
||||
"(#49 _S_ collision corrupts the linked strings)"));
|
||||
};
|
||||
exes[t] = exe;
|
||||
t += 1;
|
||||
};
|
||||
if (!testenv.same(testenv.readfile(exes[0]),
|
||||
testenv.readfile(exes[1]))) {
|
||||
fail("str49", "cs str-exe != ww str-exe (rule 10, #49)");
|
||||
};
|
||||
testenv.clean(td);
|
||||
};
|
||||
|
||||
@test fn m3leaf53() void = {
|
||||
let td: str = testenv.fresh();
|
||||
let ceaww: str = strings.concat(td, "/cea.ww");
|
||||
testenv.writefile(ceaww, ceasrc());
|
||||
let cebww: str = strings.concat(td, "/ceb.ww");
|
||||
testenv.writefile(cebww, cebsrc());
|
||||
produce(td, "cea", ceaww, strings.concat(td, "/cea.wwi"));
|
||||
produce(td, "ceb", cebww, strings.concat(td, "/ceb.wwi"));
|
||||
let cxsep: str = strings.concat(td, "/cxroot.sep.ww");
|
||||
testenv.writefile(cxsep, strings.concat(
|
||||
sec("//ww:module cea",
|
||||
testenv.readfile(strings.concat(td, "/cea.wwi"))),
|
||||
sec("//ww:module ceb",
|
||||
testenv.readfile(strings.concat(td, "/ceb.wwi"))),
|
||||
sec("//ww:module-reset", cxrootsrc())));
|
||||
|
||||
let comps: []str = ["w6c", "w6c_ww"];
|
||||
let asms: []str = ["w6a", "w6a_ww"];
|
||||
let lnks: []str = ["w6l", "w6l_ww"];
|
||||
let tags: []str = ["cs", "ww"];
|
||||
let rt: str = strings.concat(testenv.repo(), "/out/lib/libwwrt.a");
|
||||
let exes: []str = ["", ""];
|
||||
let t: i32 = 0;
|
||||
for (t < 2) {
|
||||
let as_: str = strings.concat(td, "/cea.", tags[t], ".s");
|
||||
let bs: str = strings.concat(td, "/ceb.", tags[t], ".s");
|
||||
let ao: str = strings.concat(td, "/cea.", tags[t], ".o");
|
||||
let bo: str = strings.concat(td, "/ceb.", tags[t], ".o");
|
||||
let ro: str = strings.concat(td, "/cxroot.", tags[t], ".o");
|
||||
buildpkg(td, strings.concat("cea_", tags[t]), comps[t], asms[t],
|
||||
ceaww, as_, ao);
|
||||
buildpkg(td, strings.concat("ceb_", tags[t]), comps[t], asms[t],
|
||||
cebww, bs, bo);
|
||||
buildpkg(td, strings.concat("cxroot_", tags[t]), comps[t], asms[t],
|
||||
cxsep, strings.concat(td, "/cxroot.", tags[t], ".s"), ro);
|
||||
// #53 structural proof (cstage .s once): module-prefixed exported
|
||||
// non-fn leaves, never a shared bare v/K
|
||||
if (t == 0) {
|
||||
let sa: str = testenv.readfile(as_);
|
||||
let sb: str = testenv.readfile(bs);
|
||||
if (!testenv.has(sa, "cea.v(SB)") || !testenv.has(sa, "cea.K(SB)")
|
||||
|| !testenv.has(sb, "ceb.v(SB)")
|
||||
|| !testenv.has(sb, "ceb.K(SB)")) {
|
||||
fail("leaf53", "exported non-fn leaf not module-prefixed");
|
||||
};
|
||||
if (testenv.has(sa, "DATAW v(SB)") || testenv.has(sa, "DATA K(SB)")
|
||||
|| testenv.has(sb, "DATAW v(SB)")
|
||||
|| testenv.has(sb, "DATA K(SB)")) {
|
||||
fail("leaf53",
|
||||
"bare exported leaf survives (cross-unit link collision)");
|
||||
};
|
||||
};
|
||||
let exe: str = strings.concat(td, "/cxprog.", tags[t]);
|
||||
let lav: []str = [testenv.driver(lnks[t]), "-o", exe, ro, ao, bo,
|
||||
rt];
|
||||
if (runcode(td, strings.concat("l_", tags[t]), lav) != 0) {
|
||||
fail("leaf53", strings.concat(lnks[t],
|
||||
" export-leaf sep link failed (#53)"));
|
||||
};
|
||||
let rav: []str = [exe];
|
||||
if (runcode(td, strings.concat("run_", tags[t]), rav) != 140) {
|
||||
fail("leaf53", strings.concat(tags[t], " export-leaf exit != 140 ",
|
||||
"(#53 same-leaf collision corrupts the linked data)"));
|
||||
};
|
||||
exes[t] = exe;
|
||||
t += 1;
|
||||
};
|
||||
if (!testenv.same(testenv.readfile(exes[0]),
|
||||
testenv.readfile(exes[1]))) {
|
||||
fail("leaf53", "cs export-leaf exe != ww exe (rule 10, #53)");
|
||||
};
|
||||
testenv.clean(td);
|
||||
};
|
||||
@@ -1,793 +0,0 @@
|
||||
/*
|
||||
* 989_m3sep_run — M3 `.wwi` separate-compilation CONSUMER gate (task #22 arc).
|
||||
*
|
||||
* M3 = w6c `-c` (separate-compile / primary-only codegen) reads each imported
|
||||
* package's `.wwi` prototypes (M2's producer output) as import scope INSTEAD of
|
||||
* re-checking dep bodies. Since the sep flip this IS the sole compile
|
||||
* path. This gate certifies the consumer.
|
||||
*
|
||||
* Fixture: a leaf -> mid -> root chain carrying ALL FOUR cross-boundary fact-
|
||||
* classes — fn SIGNATURE (leaf.add), struct/type LAYOUT (leaf.Point, used as a
|
||||
* by-value param + returned), `def` const VALUE (leaf.SCALE, const-folded into
|
||||
* mid), and `export let` value-global (leaf.origin_tag, read across the
|
||||
* boundary). mid imports leaf; root imports both.
|
||||
*
|
||||
* THE LOAD-BEARING CLAIM (rob §4.1): compiling package P in sep mode (deps as
|
||||
* `.wwi`) produces the SAME codegen for P's own symbols as compiling P with
|
||||
* deps as full BODIES — proven by holding the `-c` codegen FILTER constant and
|
||||
* varying ONLY the dep-source form:
|
||||
* Pbodies.s = w6c -c (deps as //ww:module body sections + P)
|
||||
* Psep.s = w6c -c (deps as //ww:module .wwi sections + P)
|
||||
* GATE: cmp Pbodies.s Psep.s → byte-identical (per-package, sharp blame).
|
||||
* Both emit ONLY P's symbols (the `-c` filter), so the cmp proves the `.wwi`
|
||||
* conveys exactly the type/layout/const facts the dep bodies did, for P's code.
|
||||
*
|
||||
* Plus, per package: the keystone on BOTH stages (cstage bodies==sep and
|
||||
* wwstage bodies==sep — so a wwstage-only guard regression can't hide behind
|
||||
* the `.wwi`-sourced sep unit), cs==ww at the `.s` level (rule 10 — w6c vs
|
||||
* w6c_ww on the identical sep-unit), sep-path determinism (compile twice ->
|
||||
* identical), and the value-global guard (an imported `export let` must NOT
|
||||
* re-emit DATA — that would duplicate the dep's own definition -> link
|
||||
* collision). A str sub-fixture (sleaf -> smid -> sroot) makes the strlit-
|
||||
* intern guard non-vacuous AND, post-#49 (per-unit `_S_` prefix), LINKS the
|
||||
* two str-bearing packages without a label collision — see its note below.
|
||||
* End-to-end:
|
||||
* compile each package's .o under `-c`, link with w6l, run -> exit code is the
|
||||
* real cross-boundary computation; the SAME with the wwstage tools (w6c_ww/
|
||||
* w6a_ww/w6l_ww) -> cs==ww at the final-exe level + behavioral identity.
|
||||
*
|
||||
* COLD: a fresh getpid-keyed /tmp dir, `.wwi` materialized from scratch every
|
||||
* run (no warm cache — a stale `.wwi` is a #110-class freshness hazard). It
|
||||
* writes nothing to the source tree. 9xx is full; shares the 989
|
||||
* prefix per the 989_lib_byteid / 989_m2wwi precedent (the short name keys the
|
||||
* binary). Models 989_m2wwi_run.c conventions.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char *
|
||||
absbin(void)
|
||||
{
|
||||
const char *b = getenv("BIN");
|
||||
if (!b) b = "out/bin";
|
||||
if (b[0] == '/') return b;
|
||||
static char buf[2048];
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return NULL;
|
||||
snprintf(buf, sizeof buf, "%s/%s", cwd, b);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int
|
||||
slurp(const char *path, char **outbuf, size_t *outlen)
|
||||
{
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) return -1;
|
||||
fseek(f, 0, SEEK_END);
|
||||
long n = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
if (n < 0) { fclose(f); return -1; }
|
||||
char *b = malloc((size_t)n + 1);
|
||||
if (!b) { fclose(f); return -1; }
|
||||
if (fread(b, 1, (size_t)n, f) != (size_t)n) { free(b); fclose(f); return -1; }
|
||||
b[n] = '\0';
|
||||
fclose(f);
|
||||
*outbuf = b;
|
||||
*outlen = (size_t)n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
files_eq(const char *a, const char *b)
|
||||
{
|
||||
char *ba = NULL, *bb = NULL;
|
||||
size_t na = 0, nb = 0;
|
||||
if (slurp(a, &ba, &na) < 0 || slurp(b, &bb, &nb) < 0) {
|
||||
free(ba); free(bb);
|
||||
return -1;
|
||||
}
|
||||
int eq = (na == nb && memcmp(ba, bb, na) == 0);
|
||||
free(ba); free(bb);
|
||||
return eq ? 0 : 1;
|
||||
}
|
||||
|
||||
static int
|
||||
write_file(const char *path, const char *body)
|
||||
{
|
||||
FILE *f = fopen(path, "wb");
|
||||
if (!f) return -1;
|
||||
fputs(body, f);
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Remove one exact invocation-created child, accepting an uncreated path. */
|
||||
static int
|
||||
remove_child(const char *dir, const char *name)
|
||||
{
|
||||
char path[1024];
|
||||
snprintf(path, sizeof path, "%s/%s", dir, name);
|
||||
if (unlink(path) == 0 || errno == ENOENT) return 0;
|
||||
perror(path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* concat src files (verbatim bodies, each under a //ww:module directive or the
|
||||
* //ww:module-reset root boundary) into one sep-unit. `pre` is the directive
|
||||
* line (without trailing newline) to emit before `src`'s bytes; chained calls
|
||||
* build the full unit. Returns 0 on success. */
|
||||
static int
|
||||
append_section(FILE *out, const char *directive, const char *srcpath)
|
||||
{
|
||||
char *b = NULL;
|
||||
size_t n = 0;
|
||||
if (slurp(srcpath, &b, &n) < 0) return -1;
|
||||
fprintf(out, "%s\n", directive);
|
||||
fwrite(b, 1, n, out);
|
||||
fputc('\n', out);
|
||||
free(b);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The fixture. leaf is a leaf package (no imports); mid imports leaf; root
|
||||
* imports both. All FOUR fact-classes cross the leaf boundary. root's exit code
|
||||
* is the real cross-boundary computation:
|
||||
* combine(p) = add(p.x,p.y) + SCALE + origin_tag = add(3,4)+7+42 = 7+7+42 = 56
|
||||
* main = combine(mkpoint(3,4)) + add(1,2) = 56 + 3 = 59
|
||||
*/
|
||||
#define EXPECT_EXIT 59
|
||||
static const char *leaf_src =
|
||||
"package leaf;\n"
|
||||
"export type Point = struct { x: i32, y: i32 };\n"
|
||||
"export def SCALE: i32 = 7;\n"
|
||||
"export let origin_tag: i32 = 42;\n"
|
||||
"export fn add(a: i32, b: i32) i32 = { return a + b; };\n"
|
||||
"export fn mkpoint(x: i32, y: i32) Point = { return Point { x = x, y = y }; };\n"
|
||||
"fn leafpriv(z: i32) i32 = { return z * SCALE; };\n";
|
||||
static const char *mid_src =
|
||||
"package mid;\n"
|
||||
"import leaf;\n"
|
||||
"export fn combine(p: leaf.Point) i32 = {\n"
|
||||
" return leaf.add(p.x, p.y) + leaf.SCALE + leaf.origin_tag;\n"
|
||||
"};\n";
|
||||
static const char *root_src =
|
||||
"package main;\n"
|
||||
"import leaf;\n"
|
||||
"import mid;\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let p = leaf.mkpoint(3, 4);\n"
|
||||
" let r = mid.combine(p);\n"
|
||||
" r = r + leaf.add(1, 2);\n"
|
||||
" return r;\n"
|
||||
"};\n";
|
||||
|
||||
/* Str-literal sub-fixture (sleaf -> smid). Two roles: (1) the per-package
|
||||
* bodies-vs-.wwi keystone cmp, and (2) post-#49 the cross-unit LINK leg below
|
||||
* (the per-unit `_S_` prefix removed the global-counter label collision that
|
||||
* used to block linking str-bearing packages). Its job is to make the strlit-
|
||||
* intern guard (let_pre_
|
||||
* intern / letpreintern) non-vacuous: sleaf carries a top-level str `export
|
||||
* let` whose initializer interns a strlit in sleaf's OWN compile but is
|
||||
* stripped from sleaf's `.wwi`. With the guard live, a dependent must NOT re-
|
||||
* intern it, so smid's own strlit stays `_S_0` in both the bodies and `.wwi`
|
||||
* units -> keystone byte-id. Drop the guard and sleaf.banner interns ahead of
|
||||
* smid.label in the bodies-unit, shifting it to `_S_1` -> keystone goes RED.
|
||||
* The i32-only leaf->mid->root fixture has no strlit and is blind to this. */
|
||||
static const char *sleaf_src =
|
||||
"package sleaf;\n"
|
||||
"export let banner: str = \"sleaf-banner\";\n"
|
||||
"export fn tag(a: i32) i32 = { return a + 1; };\n"
|
||||
"export fn getbanner() str = { return banner; };\n";
|
||||
static const char *smid_src =
|
||||
"package smid;\n"
|
||||
"import sleaf;\n"
|
||||
"export let label: str = \"smid-label\";\n"
|
||||
"export fn use(a: i32) i32 = { return sleaf.tag(a); };\n"
|
||||
"export fn getlabel() str = { return label; };\n";
|
||||
|
||||
/* #49 LINK leg: a root over the str sub-fixture. Two str-bearing packages
|
||||
* (sleaf, smid) compiled `-c` separately now LINK without an `_S_` label
|
||||
* collision — the M3-core str sub-fixture was keystone-only (never linked)
|
||||
* precisely because the global `_S_<n>` counter made both emit `_S_0`. With
|
||||
* the per-unit prefix (sleaf._S_0 / smid._S_0) the link is clean. The exit
|
||||
* reads a DISTINGUISHING byte THROUGH each string's `.ptr` (so a collided
|
||||
* `_S_0` resolving to the wrong package's bytes would corrupt the result):
|
||||
* getbanner()[0] = 's'(115) of "sleaf-banner"
|
||||
* getlabel()[4] = '-'(45) of "smid-label"
|
||||
* => 115 + 45 = 160. */
|
||||
#define EXPECT_SLINK 160
|
||||
static const char *sroot_src =
|
||||
"package main;\n"
|
||||
"import sleaf;\n"
|
||||
"import smid;\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let a: str = sleaf.getbanner();\n"
|
||||
" let b: str = smid.getlabel();\n"
|
||||
" return (a[0]: i32) + (b[4]: i32);\n"
|
||||
"};\n";
|
||||
|
||||
/* #53 LINK leg: two packages each EXPORT the SAME-named non-fn leaf (`let v`,
|
||||
* `def K`). Pre-#53 exported non-fn decls skipped path-qualification and
|
||||
* emitted a BARE symbol, so linking cea+ceb clashed (w6l: duplicate symbol
|
||||
* v/K) — the rest of the suite never caught it because no two in-tree packages
|
||||
* export the same non-fn leaf. With §7-A every exported decl path-qualifies
|
||||
* (cea.v/ceb.v, cea.K/ceb.K) so the link is clean and each getter reads its
|
||||
* OWN module's data. A collided leaf resolving to the wrong package's data
|
||||
* would corrupt the exit:
|
||||
* cea.val() = 40 + 3 = 43
|
||||
* ceb.val() = 90 + 7 = 97
|
||||
* => 43 + 97 = 140. */
|
||||
#define EXPECT_CXLINK 140
|
||||
static const char *cea_src =
|
||||
"package cea;\n"
|
||||
"export let v: i64 = 40;\n"
|
||||
"export def K: i64 = 3;\n"
|
||||
"export fn val() i64 = { return v + K; };\n";
|
||||
static const char *ceb_src =
|
||||
"package ceb;\n"
|
||||
"export let v: i64 = 90;\n"
|
||||
"export def K: i64 = 7;\n"
|
||||
"export fn val() i64 = { return v + K; };\n";
|
||||
static const char *cxroot_src =
|
||||
"package main;\n"
|
||||
"import cea;\n"
|
||||
"import ceb;\n"
|
||||
"fn main() i32 = { return (cea.val(): i32) + (ceb.val(): i32); };\n";
|
||||
|
||||
/* A package's sep-unit + bodies-unit composition. `name` is the dotted package
|
||||
* path; deps are spliced ahead under //ww:module <deppath>, then the target's
|
||||
* own source under //ww:module-reset. */
|
||||
struct pkg {
|
||||
const char *name; /* dotted path / //ww:module directive */
|
||||
const char *src; /* the package's own source */
|
||||
const char *file; /* on-disk .ww basename */
|
||||
const char *wwi; /* produced .wwi basename (NULL if not produced) */
|
||||
};
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = absbin();
|
||||
if (!bin) return 1;
|
||||
char td[64], cmd[8192], p[1024];
|
||||
int fail = 0;
|
||||
|
||||
snprintf(td, sizeof td, "/tmp/wwm3_%d", getpid());
|
||||
if (mkdir(td, 0755) != 0) {
|
||||
perror(td);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* materialize the fixture cold */
|
||||
snprintf(p, sizeof p, "%s/leaf.ww", td); if (write_file(p, leaf_src)) { fail++; goto out; }
|
||||
snprintf(p, sizeof p, "%s/mid.ww", td); if (write_file(p, mid_src)) { fail++; goto out; }
|
||||
snprintf(p, sizeof p, "%s/root.ww", td); if (write_file(p, root_src)) { fail++; goto out; }
|
||||
snprintf(p, sizeof p, "%s/sleaf.ww", td); if (write_file(p, sleaf_src)) { fail++; goto out; }
|
||||
snprintf(p, sizeof p, "%s/smid.ww", td); if (write_file(p, smid_src)) { fail++; goto out; }
|
||||
snprintf(p, sizeof p, "%s/sroot.ww", td); if (write_file(p, sroot_src)) { fail++; goto out; }
|
||||
|
||||
char leafww[1024], midww[1024], rootww[1024];
|
||||
char leafwwi[1024], midwwi[1024];
|
||||
char sleafww[1024], smidww[1024], sleafwwi[1024];
|
||||
char smidwwi[1024], srootww[1024];
|
||||
snprintf(leafww, sizeof leafww, "%s/leaf.ww", td);
|
||||
snprintf(midww, sizeof midww, "%s/mid.ww", td);
|
||||
snprintf(rootww, sizeof rootww, "%s/root.ww", td);
|
||||
snprintf(leafwwi, sizeof leafwwi, "%s/leaf.wwi", td);
|
||||
snprintf(midwwi, sizeof midwwi, "%s/mid.wwi", td);
|
||||
snprintf(sleafww, sizeof sleafww, "%s/sleaf.ww", td);
|
||||
snprintf(smidww, sizeof smidww, "%s/smid.ww", td);
|
||||
snprintf(sleafwwi, sizeof sleafwwi, "%s/sleaf.wwi", td);
|
||||
snprintf(smidwwi, sizeof smidwwi, "%s/smid.wwi", td);
|
||||
snprintf(srootww, sizeof srootww, "%s/sroot.ww", td);
|
||||
|
||||
/* ---- produce dep .wwi COLD, in dependency order -------------------- */
|
||||
/* leaf has no deps: produce directly. */
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"timeout 180 %s/w6c -I %s -o /dev/null %s >/dev/null 2>&1",
|
||||
bin, leafwwi, leafww);
|
||||
if (runwait(cmd) != 0) { fprintf(stderr, "m3sep FAIL: leaf.wwi produce\n"); fail++; goto out; }
|
||||
/* mid deps leaf: produce against leaf.wwi (mid primary). */
|
||||
{
|
||||
char unit[1024]; snprintf(unit, sizeof unit, "%s/mid.prod.ww", td);
|
||||
FILE *u = fopen(unit, "wb");
|
||||
if (!u) { fail++; goto out; }
|
||||
if (append_section(u, "//ww:module leaf", leafwwi) ||
|
||||
append_section(u, "//ww:module-reset", midww)) { fclose(u); fail++; goto out; }
|
||||
fclose(u);
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"timeout 180 %s/w6c -I %s -o /dev/null %s >/dev/null 2>&1",
|
||||
bin, midwwi, unit);
|
||||
if (runwait(cmd) != 0) { fprintf(stderr, "m3sep FAIL: mid.wwi produce\n"); fail++; goto out; }
|
||||
}
|
||||
/* sleaf has no deps: produce directly (str sub-fixture). */
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"timeout 180 %s/w6c -I %s -o /dev/null %s >/dev/null 2>&1",
|
||||
bin, sleafwwi, sleafww);
|
||||
if (runwait(cmd) != 0) { fprintf(stderr, "m3sep FAIL: sleaf.wwi produce\n"); fail++; goto out; }
|
||||
/* smid deps sleaf: produce smid.wwi (smid primary, sleaf.wwi scope) for
|
||||
* the #49 link leg's sroot scope. */
|
||||
{
|
||||
char unit[1024]; snprintf(unit, sizeof unit, "%s/smid.prod.ww", td);
|
||||
FILE *u = fopen(unit, "wb");
|
||||
if (!u) { fail++; goto out; }
|
||||
if (append_section(u, "//ww:module sleaf", sleafwwi) ||
|
||||
append_section(u, "//ww:module-reset", smidww)) { fclose(u); fail++; goto out; }
|
||||
fclose(u);
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"timeout 180 %s/w6c -I %s -o /dev/null %s >/dev/null 2>&1",
|
||||
bin, smidwwi, unit);
|
||||
if (runwait(cmd) != 0) { fprintf(stderr, "m3sep FAIL: smid.wwi produce\n"); fail++; goto out; }
|
||||
}
|
||||
|
||||
/* ---- per-package .s gate: keystone + cs==ww + determinism ---------- */
|
||||
/* compose mid's bodies-unit and sep-unit, then root's. */
|
||||
/* [8] (not [6]) so a 6-entry initializer leaves a NULL terminator in the
|
||||
* trailing slots — the k+=2 loop stops at the first NULL. */
|
||||
struct { const char *label; const char *combine_bodies[8]; const char *combine_sep[8]; }
|
||||
units[] = {
|
||||
{ "mid",
|
||||
/* bodies: leaf body, then mid */
|
||||
{ "//ww:module leaf", leafww, "//ww:module-reset", midww, NULL, NULL },
|
||||
/* sep: leaf.wwi, then mid */
|
||||
{ "//ww:module leaf", leafwwi, "//ww:module-reset", midww, NULL, NULL } },
|
||||
{ "root",
|
||||
{ "//ww:module leaf", leafww, "//ww:module mid", midww, "//ww:module-reset", rootww },
|
||||
{ "//ww:module leaf", leafwwi, "//ww:module mid", midwwi, "//ww:module-reset", rootww } },
|
||||
/* str sub-fixture: keystone-only (this loop never links), so the
|
||||
* cross-unit `_S_` collision is moot here. Exercises the strlit-
|
||||
* intern guard — neutralize it and sleaf.banner interns ahead of
|
||||
* smid.label in the bodies-unit, desyncing `_S_` -> keystone RED. */
|
||||
{ "smid",
|
||||
{ "//ww:module sleaf", sleafww, "//ww:module-reset", smidww, NULL, NULL },
|
||||
{ "//ww:module sleaf", sleafwwi, "//ww:module-reset", smidww, NULL, NULL } },
|
||||
};
|
||||
|
||||
for (int i = 0; i < (int)(sizeof units / sizeof units[0]); i++) {
|
||||
char bodies[1024], sep[1024];
|
||||
snprintf(bodies, sizeof bodies, "%s/%s.bodies.ww", td, units[i].label);
|
||||
snprintf(sep, sizeof sep, "%s/%s.sep.ww", td, units[i].label);
|
||||
FILE *bf = fopen(bodies, "wb"), *sf = fopen(sep, "wb");
|
||||
if (!bf || !sf) { if (bf) fclose(bf); if (sf) fclose(sf); fail++; goto out; }
|
||||
int berr = 0, serr = 0;
|
||||
for (int k = 0; units[i].combine_bodies[k]; k += 2)
|
||||
berr |= append_section(bf, units[i].combine_bodies[k], units[i].combine_bodies[k+1]);
|
||||
for (int k = 0; units[i].combine_sep[k]; k += 2)
|
||||
serr |= append_section(sf, units[i].combine_sep[k], units[i].combine_sep[k+1]);
|
||||
fclose(bf); fclose(sf);
|
||||
if (berr || serr) { fail++; goto out; }
|
||||
|
||||
char csb[1024], css[1024], wws[1024], css2[1024], wwb[1024];
|
||||
snprintf(csb, sizeof csb, "%s/%s.bodies.cs.s", td, units[i].label);
|
||||
snprintf(css, sizeof css, "%s/%s.sep.cs.s", td, units[i].label);
|
||||
snprintf(wws, sizeof wws, "%s/%s.sep.ww.s", td, units[i].label);
|
||||
snprintf(css2, sizeof css2, "%s/%s.sep.cs2.s", td, units[i].label);
|
||||
snprintf(wwb, sizeof wwb, "%s/%s.bodies.ww.s", td, units[i].label);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "timeout 180 %s/w6c -c -o %s %s >/dev/null 2>&1", bin, csb, bodies);
|
||||
if (runwait(cmd) != 0) { fprintf(stderr, "m3sep FAIL: %s bodies -c\n", units[i].label); fail++; continue; }
|
||||
snprintf(cmd, sizeof cmd, "timeout 180 %s/w6c -c -o %s %s >/dev/null 2>&1", bin, css, sep);
|
||||
if (runwait(cmd) != 0) { fprintf(stderr, "m3sep FAIL: %s sep -c (cstage)\n", units[i].label); fail++; continue; }
|
||||
snprintf(cmd, sizeof cmd, "timeout 180 %s/w6c_ww -c -o %s %s >/dev/null 2>&1", bin, wws, sep);
|
||||
if (runwait(cmd) != 0) { fprintf(stderr, "m3sep FAIL: %s sep -c (wwstage)\n", units[i].label); fail++; continue; }
|
||||
snprintf(cmd, sizeof cmd, "timeout 180 %s/w6c -c -o %s %s >/dev/null 2>&1", bin, css2, sep);
|
||||
if (runwait(cmd) != 0) { fprintf(stderr, "m3sep FAIL: %s sep -c (determinism re-run)\n", units[i].label); fail++; continue; }
|
||||
snprintf(cmd, sizeof cmd, "timeout 180 %s/w6c_ww -c -o %s %s >/dev/null 2>&1", bin, wwb, bodies);
|
||||
if (runwait(cmd) != 0) { fprintf(stderr, "m3sep FAIL: %s bodies -c (wwstage)\n", units[i].label); fail++; continue; }
|
||||
|
||||
/* KEYSTONE: deps-as-bodies == deps-as-.wwi (holding -c). */
|
||||
if (files_eq(csb, css) != 0) {
|
||||
fprintf(stderr, "m3sep FAIL: %s — bodies.s != sep.s (the .wwi does "
|
||||
"not convey the dep facts P's codegen needs)\n", units[i].label);
|
||||
fail++;
|
||||
}
|
||||
/* wwstage keystone: the cstage cmp above compiles BODIES with the C
|
||||
* stage, so a wwstage-only guard regression (e.g. letpreintern) would
|
||||
* not shift the .wwi-sourced sep unit and would slip. Compile the
|
||||
* bodies unit with w6c_ww too and hold the same bodies==sep invariant
|
||||
* on the wwstage side. */
|
||||
if (files_eq(wwb, wws) != 0) {
|
||||
fprintf(stderr, "m3sep FAIL: %s — ww bodies.s != ww sep.s "
|
||||
"(wwstage keystone)\n", units[i].label);
|
||||
fail++;
|
||||
}
|
||||
/* rule 10: cstage sep.s == wwstage sep.s. */
|
||||
if (files_eq(css, wws) != 0) {
|
||||
fprintf(stderr, "m3sep FAIL: %s — cs sep.s != ww sep.s (rule 10)\n", units[i].label);
|
||||
fail++;
|
||||
}
|
||||
/* determinism: same input twice -> identical. */
|
||||
if (files_eq(css, css2) != 0) {
|
||||
fprintf(stderr, "m3sep FAIL: %s — sep codegen non-deterministic\n", units[i].label);
|
||||
fail++;
|
||||
}
|
||||
/* value-global guard: an imported `export let` must NOT re-emit. The
|
||||
* `mid`/`root` sep-units import leaf.origin_tag; its DATAW must appear
|
||||
* ONLY in leaf's own compile, never in a dependent's. */
|
||||
{
|
||||
char *s = NULL; size_t n = 0;
|
||||
if (slurp(css, &s, &n) == 0) {
|
||||
/* the DEFINITION is `DATAW origin_tag(SB),...`; a READ is
|
||||
* `LEAQ origin_tag(SB),...` (legitimately present — the
|
||||
* dependent loads the imported global). Gate on the DATA[W]
|
||||
* definition only. */
|
||||
if (strstr(s, "DATAW origin_tag(SB)") != NULL
|
||||
|| strstr(s, "DATA origin_tag(SB)") != NULL) {
|
||||
fprintf(stderr, "m3sep FAIL: %s — imported value-global "
|
||||
"origin_tag re-emitted (link collision)\n", units[i].label);
|
||||
fail++;
|
||||
}
|
||||
free(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- behavioral + final-exe cs==ww --------------------------------- */
|
||||
/* Build each package's .o under -c with BOTH toolchains, link, run.
|
||||
* leaf is its own primary (no deps); mid/root use their sep-units. */
|
||||
{
|
||||
char leafsep_cs[1024], midsep[1024], rootsep[1024];
|
||||
snprintf(midsep, sizeof midsep, "%s/mid.sep.ww", td);
|
||||
snprintf(rootsep, sizeof rootsep, "%s/root.sep.ww", td);
|
||||
(void)leafsep_cs;
|
||||
|
||||
struct { const char *tool_c, *tool_a, *tool_l; const char *tag; } tc[] = {
|
||||
{ "w6c", "w6a", "w6l", "cs" },
|
||||
{ "w6c_ww", "w6a_ww", "w6l_ww", "ww" },
|
||||
};
|
||||
char exe[2][1024];
|
||||
for (int t = 0; t < 2; t++) {
|
||||
char ls[1024], lo[1024], ms[1024], mo[1024], rs[1024], ro[1024];
|
||||
snprintf(ls, sizeof ls, "%s/leaf.%s.s", td, tc[t].tag);
|
||||
snprintf(lo, sizeof lo, "%s/leaf.%s.o", td, tc[t].tag);
|
||||
snprintf(ms, sizeof ms, "%s/mid.%s.s", td, tc[t].tag);
|
||||
snprintf(mo, sizeof mo, "%s/mid.%s.o", td, tc[t].tag);
|
||||
snprintf(rs, sizeof rs, "%s/root.%s.s", td, tc[t].tag);
|
||||
snprintf(ro, sizeof ro, "%s/root.%s.o", td, tc[t].tag);
|
||||
snprintf(exe[t], sizeof exe[t], "%s/prog.%s", td, tc[t].tag);
|
||||
|
||||
int ok = 1;
|
||||
snprintf(cmd, sizeof cmd, "timeout 180 %s/%s -c -o %s %s >/dev/null 2>&1 && timeout 180 %s/%s -o %s %s >/dev/null 2>&1",
|
||||
bin, tc[t].tool_c, ls, leafww, bin, tc[t].tool_a, lo, ls);
|
||||
if (runwait(cmd) != 0) ok = 0;
|
||||
snprintf(cmd, sizeof cmd, "timeout 180 %s/%s -c -o %s %s >/dev/null 2>&1 && timeout 180 %s/%s -o %s %s >/dev/null 2>&1",
|
||||
bin, tc[t].tool_c, ms, midsep, bin, tc[t].tool_a, mo, ms);
|
||||
if (runwait(cmd) != 0) ok = 0;
|
||||
snprintf(cmd, sizeof cmd, "timeout 180 %s/%s -c -o %s %s >/dev/null 2>&1 && timeout 180 %s/%s -o %s %s >/dev/null 2>&1",
|
||||
bin, tc[t].tool_c, rs, rootsep, bin, tc[t].tool_a, ro, rs);
|
||||
if (runwait(cmd) != 0) ok = 0;
|
||||
snprintf(cmd, sizeof cmd, "timeout 180 %s/%s -o %s %s %s %s %s/../lib/libwwrt.a >/dev/null 2>&1",
|
||||
bin, tc[t].tool_l, exe[t], ro, mo, lo, bin);
|
||||
if (runwait(cmd) != 0) ok = 0;
|
||||
if (!ok) {
|
||||
fprintf(stderr, "m3sep FAIL: %s toolchain sep build/link failed\n", tc[t].tag);
|
||||
fail++;
|
||||
exe[t][0] = '\0';
|
||||
continue;
|
||||
}
|
||||
int rc = runwait(exe[t]);
|
||||
if (rc != EXPECT_EXIT) {
|
||||
fprintf(stderr, "m3sep FAIL: %s sep program exit=%d, expected %d "
|
||||
"(cross-boundary behavior wrong)\n", tc[t].tag, rc, EXPECT_EXIT);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
/* cs==ww at the final-exe level (rule 10 end-to-end). Symbol order
|
||||
* is identical because both toolchains link the SAME object set in
|
||||
* the SAME order; only the compiler differs, and it is byte-id at
|
||||
* the .s level (proven above), so the linked images match too. */
|
||||
if (exe[0][0] && exe[1][0] && files_eq(exe[0], exe[1]) != 0) {
|
||||
fprintf(stderr, "m3sep FAIL: cs exe != ww exe (rule 10, final image)\n");
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- #49 LINK leg: two str-bearing packages link without _S_ clash --- */
|
||||
/* sleaf and smid each carry a top-level str global -> each emits a strlit
|
||||
* label. Under the OLD global `_S_<n>` counter both would emit `_S_0`, so
|
||||
* linking them was impossible (the M3-core str sub-fixture is keystone-
|
||||
* ONLY for exactly this reason). With the #49 per-unit prefix the labels
|
||||
* are sleaf._S_0 / smid._S_0 -> the link is clean. sroot reads a
|
||||
* distinguishing byte THROUGH each string's `.ptr`, so a collided label
|
||||
* resolving to the wrong package's bytes would corrupt the exit code. */
|
||||
{
|
||||
char smidsep[1024], srootsep[1024];
|
||||
snprintf(smidsep, sizeof smidsep, "%s/smid.sep.ww", td);
|
||||
snprintf(srootsep, sizeof srootsep, "%s/sroot.sep.ww", td);
|
||||
/* sroot imports sleaf + smid: scope = both .wwi, then sroot's body.
|
||||
* (smid.sep.ww was already composed by the keystone loop above.) */
|
||||
{
|
||||
FILE *u = fopen(srootsep, "wb");
|
||||
if (!u) { fail++; goto out; }
|
||||
if (append_section(u, "//ww:module sleaf", sleafwwi) ||
|
||||
append_section(u, "//ww:module smid", smidwwi) ||
|
||||
append_section(u, "//ww:module-reset", srootww)) { fclose(u); fail++; goto out; }
|
||||
fclose(u);
|
||||
}
|
||||
|
||||
struct { const char *tool_c, *tool_a, *tool_l; const char *tag; } tc[] = {
|
||||
{ "w6c", "w6a", "w6l", "cs" },
|
||||
{ "w6c_ww", "w6a_ww", "w6l_ww", "ww" },
|
||||
};
|
||||
char sexe[2][1024];
|
||||
for (int t = 0; t < 2; t++) {
|
||||
char ls[1024], lo[1024], ms[1024], mo[1024], rs[1024], ro[1024];
|
||||
snprintf(ls, sizeof ls, "%s/sleaf.%s.s", td, tc[t].tag);
|
||||
snprintf(lo, sizeof lo, "%s/sleaf.%s.o", td, tc[t].tag);
|
||||
snprintf(ms, sizeof ms, "%s/smid.%s.s", td, tc[t].tag);
|
||||
snprintf(mo, sizeof mo, "%s/smid.%s.o", td, tc[t].tag);
|
||||
snprintf(rs, sizeof rs, "%s/sroot.%s.s", td, tc[t].tag);
|
||||
snprintf(ro, sizeof ro, "%s/sroot.%s.o", td, tc[t].tag);
|
||||
snprintf(sexe[t], sizeof sexe[t], "%s/sprog.%s", td, tc[t].tag);
|
||||
|
||||
int ok = 1;
|
||||
snprintf(cmd, sizeof cmd, "timeout 180 %s/%s -c -o %s %s >/dev/null 2>&1 && timeout 180 %s/%s -o %s %s >/dev/null 2>&1",
|
||||
bin, tc[t].tool_c, ls, sleafww, bin, tc[t].tool_a, lo, ls);
|
||||
if (runwait(cmd) != 0) ok = 0;
|
||||
snprintf(cmd, sizeof cmd, "timeout 180 %s/%s -c -o %s %s >/dev/null 2>&1 && timeout 180 %s/%s -o %s %s >/dev/null 2>&1",
|
||||
bin, tc[t].tool_c, ms, smidsep, bin, tc[t].tool_a, mo, ms);
|
||||
if (runwait(cmd) != 0) ok = 0;
|
||||
snprintf(cmd, sizeof cmd, "timeout 180 %s/%s -c -o %s %s >/dev/null 2>&1 && timeout 180 %s/%s -o %s %s >/dev/null 2>&1",
|
||||
bin, tc[t].tool_c, rs, srootsep, bin, tc[t].tool_a, ro, rs);
|
||||
if (runwait(cmd) != 0) ok = 0;
|
||||
/* #49 structural proof (cstage .s once): each str package's strlit
|
||||
* label is MODULE-PREFIXED, never a shared bare `_S_0`. */
|
||||
if (t == 0) {
|
||||
char *sl = NULL, *sm = NULL; size_t nl = 0, nm = 0;
|
||||
if (slurp(ls, &sl, &nl) == 0 && slurp(ms, &sm, &nm) == 0) {
|
||||
if (strstr(sl, "sleaf._S_") == NULL || strstr(sm, "smid._S_") == NULL) {
|
||||
fprintf(stderr, "m3sep FAIL: #49 strlit label not module-prefixed\n");
|
||||
fail++;
|
||||
}
|
||||
if (strstr(sl, "DATA _S_0(SB)") != NULL || strstr(sm, "DATA _S_0(SB)") != NULL) {
|
||||
fprintf(stderr, "m3sep FAIL: #49 bare _S_0 survives (cross-unit link collision)\n");
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
free(sl); free(sm);
|
||||
}
|
||||
/* link sroot + smid + sleaf + runtime: clean iff labels are unique. */
|
||||
snprintf(cmd, sizeof cmd, "timeout 180 %s/%s -o %s %s %s %s %s/../lib/libwwrt.a >/dev/null 2>&1",
|
||||
bin, tc[t].tool_l, sexe[t], ro, mo, lo, bin);
|
||||
if (runwait(cmd) != 0) ok = 0;
|
||||
if (!ok) {
|
||||
fprintf(stderr, "m3sep FAIL: %s str-pkg sep build/link failed (#49)\n", tc[t].tag);
|
||||
fail++;
|
||||
sexe[t][0] = '\0';
|
||||
continue;
|
||||
}
|
||||
int rc = runwait(sexe[t]);
|
||||
if (rc != EXPECT_SLINK) {
|
||||
fprintf(stderr, "m3sep FAIL: %s str-link program exit=%d, expected %d "
|
||||
"(#49 _S_ collision corrupts the linked strings)\n", tc[t].tag, rc, EXPECT_SLINK);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
if (sexe[0][0] && sexe[1][0] && files_eq(sexe[0], sexe[1]) != 0) {
|
||||
fprintf(stderr, "m3sep FAIL: cs str-exe != ww str-exe (rule 10, #49)\n");
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- #53 LINK leg: same-leaf exported non-fn decls path-qualify ----- */
|
||||
/* cea and ceb each `export let v` + `export def K` (see cea_src note).
|
||||
* Pre-#53 both emitted BARE v/K -> w6l duplicate-symbol clash; with §7-A
|
||||
* path-qualification (cea.v/ceb.v, cea.K/ceb.K) the link is clean and each
|
||||
* getter reads its OWN module's data. */
|
||||
{
|
||||
char ceaww[1024], cebww[1024], cxrootww[1024];
|
||||
char ceawwi[1024], cebwwi[1024], cxrootsep[1024];
|
||||
snprintf(ceaww, sizeof ceaww, "%s/cea.ww", td);
|
||||
snprintf(cebww, sizeof cebww, "%s/ceb.ww", td);
|
||||
snprintf(cxrootww, sizeof cxrootww, "%s/cxroot.ww", td);
|
||||
snprintf(ceawwi, sizeof ceawwi, "%s/cea.wwi", td);
|
||||
snprintf(cebwwi, sizeof cebwwi, "%s/ceb.wwi", td);
|
||||
snprintf(cxrootsep, sizeof cxrootsep, "%s/cxroot.sep.ww", td);
|
||||
if (write_file(ceaww, cea_src) || write_file(cebww, ceb_src) ||
|
||||
write_file(cxrootww, cxroot_src)) { fail++; goto out; }
|
||||
/* cea, ceb have no deps: produce their .wwi directly. */
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"timeout 180 %s/w6c -I %s -o /dev/null %s >/dev/null 2>&1", bin, ceawwi, ceaww);
|
||||
if (runwait(cmd) != 0) { fprintf(stderr, "m3sep FAIL: cea.wwi produce (#53)\n"); fail++; goto out; }
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"timeout 180 %s/w6c -I %s -o /dev/null %s >/dev/null 2>&1", bin, cebwwi, cebww);
|
||||
if (runwait(cmd) != 0) { fprintf(stderr, "m3sep FAIL: ceb.wwi produce (#53)\n"); fail++; goto out; }
|
||||
/* cxroot imports cea + ceb: scope = both .wwi, then cxroot's body. */
|
||||
{
|
||||
FILE *u = fopen(cxrootsep, "wb");
|
||||
if (!u) { fail++; goto out; }
|
||||
if (append_section(u, "//ww:module cea", ceawwi) ||
|
||||
append_section(u, "//ww:module ceb", cebwwi) ||
|
||||
append_section(u, "//ww:module-reset", cxrootww)) { fclose(u); fail++; goto out; }
|
||||
fclose(u);
|
||||
}
|
||||
|
||||
struct { const char *tool_c, *tool_a, *tool_l; const char *tag; } tc[] = {
|
||||
{ "w6c", "w6a", "w6l", "cs" },
|
||||
{ "w6c_ww", "w6a_ww", "w6l_ww", "ww" },
|
||||
};
|
||||
char cxexe[2][1024];
|
||||
for (int t = 0; t < 2; t++) {
|
||||
char as[1024], ao[1024], bs[1024], bo[1024], rs[1024], ro[1024];
|
||||
snprintf(as, sizeof as, "%s/cea.%s.s", td, tc[t].tag);
|
||||
snprintf(ao, sizeof ao, "%s/cea.%s.o", td, tc[t].tag);
|
||||
snprintf(bs, sizeof bs, "%s/ceb.%s.s", td, tc[t].tag);
|
||||
snprintf(bo, sizeof bo, "%s/ceb.%s.o", td, tc[t].tag);
|
||||
snprintf(rs, sizeof rs, "%s/cxroot.%s.s", td, tc[t].tag);
|
||||
snprintf(ro, sizeof ro, "%s/cxroot.%s.o", td, tc[t].tag);
|
||||
snprintf(cxexe[t], sizeof cxexe[t], "%s/cxprog.%s", td, tc[t].tag);
|
||||
|
||||
int ok = 1;
|
||||
snprintf(cmd, sizeof cmd, "timeout 180 %s/%s -c -o %s %s >/dev/null 2>&1 && timeout 180 %s/%s -o %s %s >/dev/null 2>&1",
|
||||
bin, tc[t].tool_c, as, ceaww, bin, tc[t].tool_a, ao, as);
|
||||
if (runwait(cmd) != 0) ok = 0;
|
||||
snprintf(cmd, sizeof cmd, "timeout 180 %s/%s -c -o %s %s >/dev/null 2>&1 && timeout 180 %s/%s -o %s %s >/dev/null 2>&1",
|
||||
bin, tc[t].tool_c, bs, cebww, bin, tc[t].tool_a, bo, bs);
|
||||
if (runwait(cmd) != 0) ok = 0;
|
||||
snprintf(cmd, sizeof cmd, "timeout 180 %s/%s -c -o %s %s >/dev/null 2>&1 && timeout 180 %s/%s -o %s %s >/dev/null 2>&1",
|
||||
bin, tc[t].tool_c, rs, cxrootsep, bin, tc[t].tool_a, ro, rs);
|
||||
if (runwait(cmd) != 0) ok = 0;
|
||||
/* #53 structural proof (cstage .s once): each package's exported
|
||||
* non-fn leaf is MODULE-PREFIXED, never a shared bare v/K. */
|
||||
if (t == 0) {
|
||||
char *sa = NULL, *sb = NULL; size_t na = 0, nb = 0;
|
||||
if (slurp(as, &sa, &na) == 0 && slurp(bs, &sb, &nb) == 0) {
|
||||
if (strstr(sa, "cea.v(SB)") == NULL || strstr(sa, "cea.K(SB)") == NULL ||
|
||||
strstr(sb, "ceb.v(SB)") == NULL || strstr(sb, "ceb.K(SB)") == NULL) {
|
||||
fprintf(stderr, "m3sep FAIL: #53 exported non-fn leaf not module-prefixed\n");
|
||||
fail++;
|
||||
}
|
||||
if (strstr(sa, "DATAW v(SB)") != NULL || strstr(sa, "DATA K(SB)") != NULL ||
|
||||
strstr(sb, "DATAW v(SB)") != NULL || strstr(sb, "DATA K(SB)") != NULL) {
|
||||
fprintf(stderr, "m3sep FAIL: #53 bare exported leaf survives (cross-unit link collision)\n");
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
free(sa); free(sb);
|
||||
}
|
||||
/* link cxroot + cea + ceb + runtime: clean iff leaves are unique. */
|
||||
snprintf(cmd, sizeof cmd, "timeout 180 %s/%s -o %s %s %s %s %s/../lib/libwwrt.a >/dev/null 2>&1",
|
||||
bin, tc[t].tool_l, cxexe[t], ro, ao, bo, bin);
|
||||
if (runwait(cmd) != 0) ok = 0;
|
||||
if (!ok) {
|
||||
fprintf(stderr, "m3sep FAIL: %s export-leaf sep build/link failed (#53)\n", tc[t].tag);
|
||||
fail++;
|
||||
cxexe[t][0] = '\0';
|
||||
continue;
|
||||
}
|
||||
int rc = runwait(cxexe[t]);
|
||||
if (rc != EXPECT_CXLINK) {
|
||||
fprintf(stderr, "m3sep FAIL: %s export-leaf link program exit=%d, expected %d "
|
||||
"(#53 same-leaf export collision corrupts the linked data)\n", tc[t].tag, rc, EXPECT_CXLINK);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
if (cxexe[0][0] && cxexe[1][0] && files_eq(cxexe[0], cxexe[1]) != 0) {
|
||||
fprintf(stderr, "m3sep FAIL: cs export-leaf exe != ww exe (rule 10, #53)\n");
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
{
|
||||
int cleanfail = 0;
|
||||
cleanfail |= remove_child(td, "leaf.ww");
|
||||
cleanfail |= remove_child(td, "mid.ww");
|
||||
cleanfail |= remove_child(td, "root.ww");
|
||||
cleanfail |= remove_child(td, "sleaf.ww");
|
||||
cleanfail |= remove_child(td, "smid.ww");
|
||||
cleanfail |= remove_child(td, "sroot.ww");
|
||||
cleanfail |= remove_child(td, "leaf.wwi");
|
||||
cleanfail |= remove_child(td, "mid.wwi");
|
||||
cleanfail |= remove_child(td, "mid.prod.ww");
|
||||
cleanfail |= remove_child(td, "sleaf.wwi");
|
||||
cleanfail |= remove_child(td, "smid.wwi");
|
||||
cleanfail |= remove_child(td, "smid.prod.ww");
|
||||
|
||||
cleanfail |= remove_child(td, "mid.bodies.ww");
|
||||
cleanfail |= remove_child(td, "mid.sep.ww");
|
||||
cleanfail |= remove_child(td, "mid.bodies.cs.s");
|
||||
cleanfail |= remove_child(td, "mid.sep.cs.s");
|
||||
cleanfail |= remove_child(td, "mid.sep.ww.s");
|
||||
cleanfail |= remove_child(td, "mid.sep.cs2.s");
|
||||
cleanfail |= remove_child(td, "mid.bodies.ww.s");
|
||||
cleanfail |= remove_child(td, "root.bodies.ww");
|
||||
cleanfail |= remove_child(td, "root.sep.ww");
|
||||
cleanfail |= remove_child(td, "root.bodies.cs.s");
|
||||
cleanfail |= remove_child(td, "root.sep.cs.s");
|
||||
cleanfail |= remove_child(td, "root.sep.ww.s");
|
||||
cleanfail |= remove_child(td, "root.sep.cs2.s");
|
||||
cleanfail |= remove_child(td, "root.bodies.ww.s");
|
||||
cleanfail |= remove_child(td, "smid.bodies.ww");
|
||||
cleanfail |= remove_child(td, "smid.sep.ww");
|
||||
cleanfail |= remove_child(td, "smid.bodies.cs.s");
|
||||
cleanfail |= remove_child(td, "smid.sep.cs.s");
|
||||
cleanfail |= remove_child(td, "smid.sep.ww.s");
|
||||
cleanfail |= remove_child(td, "smid.sep.cs2.s");
|
||||
cleanfail |= remove_child(td, "smid.bodies.ww.s");
|
||||
|
||||
cleanfail |= remove_child(td, "leaf.cs.s");
|
||||
cleanfail |= remove_child(td, "leaf.cs.o");
|
||||
cleanfail |= remove_child(td, "mid.cs.s");
|
||||
cleanfail |= remove_child(td, "mid.cs.o");
|
||||
cleanfail |= remove_child(td, "root.cs.s");
|
||||
cleanfail |= remove_child(td, "root.cs.o");
|
||||
cleanfail |= remove_child(td, "prog.cs");
|
||||
cleanfail |= remove_child(td, "leaf.ww.s");
|
||||
cleanfail |= remove_child(td, "leaf.ww.o");
|
||||
cleanfail |= remove_child(td, "mid.ww.s");
|
||||
cleanfail |= remove_child(td, "mid.ww.o");
|
||||
cleanfail |= remove_child(td, "root.ww.s");
|
||||
cleanfail |= remove_child(td, "root.ww.o");
|
||||
cleanfail |= remove_child(td, "prog.ww");
|
||||
|
||||
cleanfail |= remove_child(td, "sroot.sep.ww");
|
||||
cleanfail |= remove_child(td, "sleaf.cs.s");
|
||||
cleanfail |= remove_child(td, "sleaf.cs.o");
|
||||
cleanfail |= remove_child(td, "smid.cs.s");
|
||||
cleanfail |= remove_child(td, "smid.cs.o");
|
||||
cleanfail |= remove_child(td, "sroot.cs.s");
|
||||
cleanfail |= remove_child(td, "sroot.cs.o");
|
||||
cleanfail |= remove_child(td, "sprog.cs");
|
||||
cleanfail |= remove_child(td, "sleaf.ww.s");
|
||||
cleanfail |= remove_child(td, "sleaf.ww.o");
|
||||
cleanfail |= remove_child(td, "smid.ww.s");
|
||||
cleanfail |= remove_child(td, "smid.ww.o");
|
||||
cleanfail |= remove_child(td, "sroot.ww.s");
|
||||
cleanfail |= remove_child(td, "sroot.ww.o");
|
||||
cleanfail |= remove_child(td, "sprog.ww");
|
||||
|
||||
cleanfail |= remove_child(td, "cea.ww");
|
||||
cleanfail |= remove_child(td, "ceb.ww");
|
||||
cleanfail |= remove_child(td, "cxroot.ww");
|
||||
cleanfail |= remove_child(td, "cea.wwi");
|
||||
cleanfail |= remove_child(td, "ceb.wwi");
|
||||
cleanfail |= remove_child(td, "cxroot.sep.ww");
|
||||
cleanfail |= remove_child(td, "cea.cs.s");
|
||||
cleanfail |= remove_child(td, "cea.cs.o");
|
||||
cleanfail |= remove_child(td, "ceb.cs.s");
|
||||
cleanfail |= remove_child(td, "ceb.cs.o");
|
||||
cleanfail |= remove_child(td, "cxroot.cs.s");
|
||||
cleanfail |= remove_child(td, "cxroot.cs.o");
|
||||
cleanfail |= remove_child(td, "cxprog.cs");
|
||||
cleanfail |= remove_child(td, "cea.ww.s");
|
||||
cleanfail |= remove_child(td, "cea.ww.o");
|
||||
cleanfail |= remove_child(td, "ceb.ww.s");
|
||||
cleanfail |= remove_child(td, "ceb.ww.o");
|
||||
cleanfail |= remove_child(td, "cxroot.ww.s");
|
||||
cleanfail |= remove_child(td, "cxroot.ww.o");
|
||||
cleanfail |= remove_child(td, "cxprog.ww");
|
||||
if (rmdir(td) != 0) {
|
||||
perror(td);
|
||||
cleanfail = 1;
|
||||
}
|
||||
if (cleanfail) {
|
||||
fprintf(stderr, "m3sep FAIL: temporary cleanup failed\n");
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
if (fail) {
|
||||
fprintf(stderr, "m3sep: %d check(s) failed\n", fail);
|
||||
return 1;
|
||||
}
|
||||
printf("m3sep: synth leaf->mid->root — per-pkg bodies==.wwi (-c) + cs==ww "
|
||||
".s/exe + determinism + value-global guard + behavioral (exit %d) + "
|
||||
"#49 str-pkg sep-LINK (sleaf+smid, exit 160) + "
|
||||
"#53 export-leaf sep-LINK (cea+ceb same-leaf v/K, exit 140)\n",
|
||||
EXPECT_EXIT);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user