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"); }; 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)"); }; }; 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")); }; }; 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")); }; 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); };