From ff7bf08d818294b5a8469b6b24f2308aad230f63 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Sat, 8 Aug 2026 14:08:45 +0900 Subject: [PATCH] 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). --- Makefile | 2 +- test/sep/m3sep_test.ww | 491 ++++++++++++++++++++++++ test/wcc/989_m3sep_run.c | 793 --------------------------------------- 3 files changed, 492 insertions(+), 794 deletions(-) create mode 100644 test/sep/m3sep_test.ww delete mode 100644 test/wcc/989_m3sep_run.c diff --git a/Makefile b/Makefile index 1904c2f5..07ce1dd8 100644 --- a/Makefile +++ b/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 \ diff --git a/test/sep/m3sep_test.ww b/test/sep/m3sep_test.ww new file mode 100644 index 00000000..9641da05 --- /dev/null +++ b/test/sep/m3sep_test.ww @@ -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); +}; diff --git a/test/wcc/989_m3sep_run.c b/test/wcc/989_m3sep_run.c deleted file mode 100644 index 982af698..00000000 --- a/test/wcc/989_m3sep_run.c +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include - -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_` 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 , 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_` 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; -}