test: port the fn/mklabel mangle observers to ww
test/xmod/label_test.ww replaces 706_fnlabel_mangle.c and 750_mklabel_modscoped.c with every assertion preserved (fnlabelmangle fixture run-112 both stages; three mklabel rows with run exits and module-qualified label needles). Strengthened: fnlabel adds the cs==ww sepwork .s byte-id the carrier never asserted.
This commit is contained in:
280
test/xmod/label_test.ww
Normal file
280
test/xmod/label_test.ww
Normal file
@@ -0,0 +1,280 @@
|
||||
package label_test;
|
||||
|
||||
// Module-scoped label-emission observers, both driver stages. Ports of
|
||||
// the retired native carriers test/wcc/706_fnlabel_mangle.c and
|
||||
// test/wcc/750_mklabel_modscoped.c; every assertion preserved.
|
||||
//
|
||||
// fnlabel (#9 + #12) — the on-disk fixture tree
|
||||
// test/wcc/data/fnlabelmangle/ pins all four label-emit sites: two
|
||||
// modules each `export fn ping` + private `fn helper` + `fn fpi`
|
||||
// (LEAQ N_IDENT), root takes `let p = mod.ping` (LEAQ N_DOT) and
|
||||
// calls both directly (CALL N_DOT) and through the ptrs. Build+run
|
||||
// exit 112 (25+31+25+31) on BOTH stages; a link-time label collapse
|
||||
// or a missing TY_FN LEAQ branch lands a wrong body. Strengthened
|
||||
// beyond the carrier: cs==ww byte-id over the sepwork .s concat (the
|
||||
// carrier never asserted it).
|
||||
//
|
||||
// mklabel (#13) — mklabel emits `<module>.<fnname>_<prefix>_<seq>`,
|
||||
// never leaf-only labels that w6a's a_intern collapses across units:
|
||||
// two_modules_same_leaf | mod1.locate/mod2.locate both stamp
|
||||
// | _match_next_1; run exit 0 (mis-jump
|
||||
// | exits 10/20) AND BOTH module-qualified
|
||||
// | labels appear in the .s concat
|
||||
// bytes_strings_contains| the bytes.contains/strings.contains
|
||||
// | latent pair coexists (run 0; 11/12)
|
||||
// same_module_same_leaf | per-fn labelseq keeps same-shape fns in
|
||||
// | ONE module distinct (run 0; 30/31)
|
||||
//
|
||||
// Dropped C machinery, not assertions: the ww_ww-absent skip gate
|
||||
// (the Make target declares both drivers) and the per-path cleanup
|
||||
// ledger (testenv.clean asserts the removal). The .s concat order is
|
||||
// strengthened from the carrier's shell glob to byte-sorted listdir.
|
||||
|
||||
import os;
|
||||
import os.exec;
|
||||
import strings;
|
||||
import testenv;
|
||||
import time;
|
||||
|
||||
fn fail(label: str, why: str) void = {
|
||||
let m: str = strings.concat("label FAIL: ", label, " -- ", why, "\n");
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
assert(false);
|
||||
};
|
||||
|
||||
fn tmo() time.duration = {
|
||||
return (240i64 * (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;
|
||||
};
|
||||
|
||||
// Every per-package .s under `sepdir`, concatenated in byte-sorted
|
||||
// order; "" when the directory is missing or holds no .s.
|
||||
fn catasm(sepdir: str) str = {
|
||||
if (!testenv.isdir(sepdir)) { return ""; };
|
||||
let names: []str = testenv.listdir(sepdir);
|
||||
let out: str = "";
|
||||
let i: i32 = 0;
|
||||
for (i < names.len) {
|
||||
if (strings.hassuffix(names[i], ".s")) {
|
||||
out = strings.concat(out,
|
||||
testenv.readfile(strings.concat(sepdir, "/", names[i])));
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
return out;
|
||||
};
|
||||
|
||||
// ---- fnlabel (#9 + #12) ------------------------------------------------
|
||||
|
||||
@test fn fnlabel() void = {
|
||||
let fixdir: str = strings.concat(testenv.repo(),
|
||||
"/test/wcc/data/fnlabelmangle");
|
||||
let td: str = testenv.fresh();
|
||||
let drvs: []str = ["ww", "ww_ww"];
|
||||
let tags: []str = ["cstage", "wwstage"];
|
||||
let asms: []str = ["", ""];
|
||||
let s: i32 = 0;
|
||||
for (s < 2) {
|
||||
let stem: str = strings.concat(td, "/pos.", tags[s]);
|
||||
let av: []str = [testenv.driver(drvs[s]), "build", "-o", stem,
|
||||
strings.concat(fixdir, "/pos.ww")];
|
||||
if (runcode(td, strings.concat("build_", tags[s]), av) != 0) {
|
||||
fail("fnlabel", strings.concat(tags[s],
|
||||
" pos.ww build failed"));
|
||||
};
|
||||
let rav: []str = [stem];
|
||||
if (runcode(td, strings.concat("run_", tags[s]), rav) != 112) {
|
||||
fail("fnlabel", strings.concat(tags[s], " exit != 112 -- fn ",
|
||||
"labels likely collapsed at link, or LEAQ-of-fn N_DOT ",
|
||||
"branch missing in cgdot"));
|
||||
};
|
||||
asms[s] = catasm(strings.concat(stem, ".sepwork"));
|
||||
s += 1;
|
||||
};
|
||||
if (asms[0].len == 0 || asms[1].len == 0) {
|
||||
fail("fnlabel", "empty .s concat");
|
||||
};
|
||||
if (!testenv.same(asms[0], asms[1])) {
|
||||
fail("fnlabel", "cstage.s != wwstage.s (byte-id break)");
|
||||
};
|
||||
testenv.clean(td);
|
||||
};
|
||||
|
||||
// ---- mklabel (#13) -----------------------------------------------------
|
||||
|
||||
fn mod1locate() str = {
|
||||
return strings.concat(
|
||||
"package mod1;\n",
|
||||
"export fn locate(s: []u8, needle: (u8 | []u8)) (i32 | void) = {\n",
|
||||
" match (needle) {\n",
|
||||
" case let c: u8 => {\n",
|
||||
" let i: i32 = 0;\n",
|
||||
" for (i < s.len) {\n",
|
||||
" if (s[i] == c) { return i; };\n",
|
||||
" i += 1;\n",
|
||||
" };\n",
|
||||
" return;\n",
|
||||
" };\n",
|
||||
" case let sub: []u8 => { return; };\n",
|
||||
" };\n",
|
||||
" return;\n",
|
||||
"};\n");
|
||||
};
|
||||
|
||||
fn mod2locate() str = {
|
||||
return strings.concat(
|
||||
"package mod2;\n",
|
||||
"export fn locate(s: []u8, needle: (u8 | []u8)) (i32 | void) = {\n",
|
||||
" match (needle) {\n",
|
||||
" case let c: u8 => {\n",
|
||||
" let i: i32 = 0;\n",
|
||||
" for (i < s.len) {\n",
|
||||
" if (s[i] == c) { return i + 100; };\n",
|
||||
" i += 1;\n",
|
||||
" };\n",
|
||||
" return;\n",
|
||||
" };\n",
|
||||
" case let sub: []u8 => { return; };\n",
|
||||
" };\n",
|
||||
" return;\n",
|
||||
"};\n");
|
||||
};
|
||||
|
||||
fn locatemain() str = {
|
||||
return strings.concat(
|
||||
"package main;\n",
|
||||
"import mod1;\n",
|
||||
"import mod2;\n",
|
||||
"export fn main() i32 = {\n",
|
||||
" let arr: [3]u8 = [65u8, 66u8, 67u8];\n",
|
||||
" let s: []u8 = arr[0:3];\n",
|
||||
" let n: u8 = 66u8;\n",
|
||||
" let needle: (u8 | []u8) = n;\n",
|
||||
" let a: (i32 | void) = mod1.locate(s, needle);\n",
|
||||
" let b: (i32 | void) = mod2.locate(s, needle);\n",
|
||||
" let av: i32 = match (a) { case let v: i32 => yield v; ",
|
||||
"case void => yield -1; };\n",
|
||||
" let bv: i32 = match (b) { case let v: i32 => yield v; ",
|
||||
"case void => yield -1; };\n",
|
||||
" if (av != 1) { return 10; };\n",
|
||||
" if (bv != 101) { return 20; };\n",
|
||||
" return 0;\n",
|
||||
"};\n");
|
||||
};
|
||||
|
||||
fn containsmain() str = {
|
||||
return strings.concat(
|
||||
"package main;\n",
|
||||
"import bytes;\n",
|
||||
"import strings;\n",
|
||||
"export fn main() i32 = {\n",
|
||||
" let s: str = \"abc\";\n",
|
||||
" let b: []u8 = strings.toutf8(s);\n",
|
||||
" let n: (u8 | []u8) = 98u8;\n",
|
||||
" if (!bytes.contains(b, n)) { return 11; };\n",
|
||||
" if (!strings.contains(s, 'b')) { return 12; };\n",
|
||||
" return 0;\n",
|
||||
"};\n");
|
||||
};
|
||||
|
||||
fn sameleafmain() str = {
|
||||
return strings.concat(
|
||||
"package main;\n",
|
||||
"fn a(needle: (u8 | []u8)) i32 = {\n",
|
||||
" match (needle) {\n",
|
||||
" case let c: u8 => return 1;\n",
|
||||
" case let s: []u8 => return 2;\n",
|
||||
" };\n",
|
||||
" return 0;\n",
|
||||
"};\n",
|
||||
"fn b(needle: (u8 | []u8)) i32 = {\n",
|
||||
" match (needle) {\n",
|
||||
" case let c: u8 => return 3;\n",
|
||||
" case let s: []u8 => return 4;\n",
|
||||
" };\n",
|
||||
" return 0;\n",
|
||||
"};\n",
|
||||
"export fn main() i32 = {\n",
|
||||
" let x: (u8 | []u8) = 7u8;\n",
|
||||
" if (a(x) != 1) { return 30; };\n",
|
||||
" if (b(x) != 3) { return 31; };\n",
|
||||
" return 0;\n",
|
||||
"};\n");
|
||||
};
|
||||
|
||||
// dirs/rels/srcs describe the row's tree; every row builds main.ww,
|
||||
// runs to exit 0 (mis-jump polarities live in the sources), and needs
|
||||
// both required label substrings in the .s concat.
|
||||
fn mkrow(label: str, dirs: []str, rels: []str, srcs: []str,
|
||||
req: []str) void = {
|
||||
let drvs: []str = ["ww", "ww_ww"];
|
||||
let tags: []str = ["cs", "ws"];
|
||||
let s: i32 = 0;
|
||||
for (s < 2) {
|
||||
let td: str = testenv.fresh();
|
||||
let d: i32 = 0;
|
||||
for (d < dirs.len) {
|
||||
assert(os.mkdir(strings.concat(td, "/", dirs[d]), 493) == 0);
|
||||
d += 1;
|
||||
};
|
||||
let f: i32 = 0;
|
||||
for (f < rels.len) {
|
||||
testenv.writefile(strings.concat(td, "/", rels[f]), srcs[f]);
|
||||
f += 1;
|
||||
};
|
||||
let av: []str = [testenv.driver(drvs[s]), "build", "-o", "main",
|
||||
"main.ww"];
|
||||
if (runcode(td, strings.concat("build_", tags[s]), av) != 0) {
|
||||
fail(label, strings.concat(drvs[s], " build failed"));
|
||||
};
|
||||
let rav: []str = [strings.concat(td, "/main")];
|
||||
if (runcode(td, strings.concat("run_", tags[s]), rav) != 0) {
|
||||
fail(label, strings.concat(drvs[s], " run-exit != 0 ",
|
||||
"(collapsed label mis-jump -- #13)"));
|
||||
};
|
||||
let all: str = catasm(strings.concat(td, "/main.sepwork"));
|
||||
if (all.len == 0) { fail(label, "empty .s concat"); };
|
||||
let k: i32 = 0;
|
||||
for (k < req.len) {
|
||||
if (!testenv.has(all, req[k])) {
|
||||
fail(label, strings.concat("label substring `", req[k],
|
||||
"` missing from the emitted .s"));
|
||||
};
|
||||
k += 1;
|
||||
};
|
||||
testenv.clean(td);
|
||||
s += 1;
|
||||
};
|
||||
};
|
||||
|
||||
@test fn two_modules_same_leaf() void = {
|
||||
let dirs: []str = ["mod1", "mod2"];
|
||||
let rels: []str = ["mod1/mod1.ww", "mod2/mod2.ww", "main.ww"];
|
||||
let srcs: []str = [mod1locate(), mod2locate(), locatemain()];
|
||||
let req: []str = ["mod1.locate_match_next_1",
|
||||
"mod2.locate_match_next_1"];
|
||||
mkrow("two_modules_same_leaf", dirs, rels, srcs, req);
|
||||
};
|
||||
|
||||
@test fn bytes_strings_contains() void = {
|
||||
let dirs: []str = [];
|
||||
let rels: []str = ["main.ww"];
|
||||
let srcs: []str = [containsmain()];
|
||||
let req: []str = ["bytes.contains_", "strings.contains_"];
|
||||
mkrow("bytes_strings_contains", dirs, rels, srcs, req);
|
||||
};
|
||||
|
||||
@test fn same_module_same_leaf() void = {
|
||||
let dirs: []str = [];
|
||||
let rels: []str = ["main.ww"];
|
||||
let srcs: []str = [sameleafmain()];
|
||||
let req: []str = ["main.a_match_next_1", "main.b_match_next_1"];
|
||||
mkrow("same_module_same_leaf", dirs, rels, srcs, req);
|
||||
};
|
||||
Reference in New Issue
Block a user