From 7c84c52db49820caa8a6df5be37b6510f799abba Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Sat, 8 Aug 2026 14:07:39 +0900 Subject: [PATCH] test: port the sep import/namespace observers to ww; retire 989_{coloimport,depmain,declns,slttypepref} sepimport_test.ww re-hosts the #98 colo srcd-shadow `ww test` gate, the #99 imported-dep bare-main mangle count, the #23/#30 modfn coexist byte-id (incl. the cross-layout vbu order pin), and the #58/#50 scopelookuptype mod-preference rows over the checked-in and synthesized module trees. slttypepref's ww_ww-absent skip gate is dropped: the Make target declares both drivers (mangle_test precedent). --- Makefile | 2 +- test/sep/sepimport_test.ww | 328 +++++++++++++++++++++++++++++++++ test/wcc/989_coloimport_sep.c | 257 -------------------------- test/wcc/989_declns_sep.c | 308 ------------------------------- test/wcc/989_depmain_sep.c | 289 ----------------------------- test/wcc/989_slttypepref_run.c | 221 ---------------------- 6 files changed, 329 insertions(+), 1076 deletions(-) create mode 100644 test/sep/sepimport_test.ww delete mode 100644 test/wcc/989_coloimport_sep.c delete mode 100644 test/wcc/989_declns_sep.c delete mode 100644 test/wcc/989_depmain_sep.c delete mode 100644 test/wcc/989_slttypepref_run.c diff --git a/Makefile b/Makefile index c5c8fc21..74d1a17c 100644 --- a/Makefile +++ b/Makefile @@ -362,7 +362,7 @@ BYTEID_WW_TARGETS = $(BYTEID_WW_TESTS:%=wwtest/%) # gates (module trees, sepwork artifact layout, composed //ww:module # units, link rejects), NOT byteid suites: they run under # test-compiler beside the surviving residual carriers. -SEP_WW_TESTS = test/sep/sepbuild_test.ww +SEP_WW_TESTS = test/sep/sepbuild_test.ww test/sep/sepimport_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/sepimport_test.ww b/test/sep/sepimport_test.ww new file mode 100644 index 00000000..86a107ff --- /dev/null +++ b/test/sep/sepimport_test.ww @@ -0,0 +1,328 @@ +package sepimport_test; + +// Import/namespace-resolution observers on the sep drivers, both +// stages. Ports of the retired native carriers +// test/wcc/989_coloimport_sep.c, 989_depmain_sep.c, 989_declns_sep.c +// and 989_slttypepref_run.c; every assertion preserved. +// +// coloimport (#98) — a co-located `_test` entry must not shadow the +// dir-package it imports under `ww test`: the two-pass locate_import +// walk resolves `import widget` to the DIR-package on a later -I +// entry, not the same-named sibling FILE on the srcd entry. Exit 0 +// both stages (pre-fix w6c rejects `package widget does not match +// import path gadget`), widget.{unit.ww,wwi} registered in .sepwork, +// and every .s/.wwi byte-id cs vs ww (rule 10). +// +// depmain (#99) — an IMPORTED dir-package's non-exported `fn main` +// mangles on its import path (aa.bb.main / cc.main) instead of +// emitting a bare `TEXT main` that collides with the root entry; +// EXACTLY one column-0 `TEXT main,` across the whole sep build. +// +// declns (#23/#30 modfn leg) — a value-namespace `fn aa` coexists +// with an imported MODULE aa (exit 6 = aa() + aa.helper()); the _vbu +// layout flips decl order and must stay byte-identical on +// {aa.s,aa.wwi,__root.s} (silent order-dependence in the SK_USE/value +// promote is the regression); __root.s carries BOTH +// `CALL aa.helper(SB)` and `CALL main.aa(SB)`. +// +// slttypepref (#58/#50 c1) — scopelookuptype must prefer the current +// module's SK_TYPE when a param shadows a type leaf two modules both +// export: xb's `size(invalid)` resolves xb's !i64 (exit 8), never +// xa's !i8, on both stages, shadow and noshadow rows. The +// two-DIRECTORY tree is load-bearing (a single-file twin risks a +// different scope-install order — E3-C1 retarget note in the +// carrier); each (row,driver) rebuilds a fresh tree. +// +// Dropped C machinery, not assertions: the ww_ww-absent skip gate +// (the Make target declares both drivers) and the per-path +// unlink/rmdir accounting (testenv.clean asserts the removal). + +import os; +import os.exec; +import strings; +import testenv; +import time; + +fn fail(label: str, why: str) void = { + let m: str = strings.concat("sepimport 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; +}; + +fn cmpsepwork(label: str, csdir: str, wwdir: str) void = { + if (!testenv.isdir(csdir)) { fail(label, "no cs sepwork"); }; + let names: []str = testenv.listdir(csdir); + let seen: i32 = 0; + let i: i32 = 0; + for (i < names.len) { + if (strings.hassuffix(names[i], ".s") + || strings.hassuffix(names[i], ".wwi")) { + seen += 1; + if (!testenv.same( + testenv.readfile(strings.concat(csdir, "/", names[i])), + testenv.readfile(strings.concat(wwdir, "/", names[i])))) { + fail(label, strings.concat("cs!=ww for ", names[i], + " (rule 10)")); + }; + }; + i += 1; + }; + // an existing-but-empty sepwork would pass the loop vacuously + if (seen == 0) { fail(label, "no .s/.wwi in cs sepwork"); }; +}; + +// ---- coloimport (#98) -------------------------------------------------- + +@test fn coloimport() void = { + let td: str = testenv.fresh(); + let inc: str = strings.concat(testenv.repo(), "/test/wcc/data/colo98"); + let entry: str = strings.concat(inc, "/widget/widgettest.ww"); + let drvs: []str = ["ww", "ww_ww"]; + let tags: []str = ["cs", "ww"]; + let i: i32 = 0; + for (i < 2) { + let prog: str = strings.concat(td, "/widget.", tags[i], ".bin"); + let av: []str = [testenv.driver(drvs[i]), "test", "-I", inc, + "-o", prog, entry]; + if (runcode(td, strings.concat("colo_", tags[i]), av) != 0) { + fail("coloimport", strings.concat("`ww test` exit != 0 on ", + drvs[i], " (import folded to the sibling file?)")); + }; + i += 1; + }; + let csdir: str = strings.concat(td, "/widget.cs.bin.sepwork"); + let wwdir: str = strings.concat(td, "/widget.ww.bin.sepwork"); + if (!testenv.exists(strings.concat(csdir, "/widget.unit.ww"))) { + fail("coloimport", strings.concat("widget.unit.ww missing (import ", + "folded to the sibling file, not the dir-package)")); + }; + if (!testenv.exists(strings.concat(csdir, "/widget.wwi"))) { + fail("coloimport", "widget.wwi missing"); + }; + cmpsepwork("coloimport", csdir, wwdir); + testenv.clean(td); +}; + +// ---- depmain (#99) ----------------------------------------------------- + +// column-0 anchored `TEXT main,` labels across every .s under `dir`; +// the leading '\n' prepend also counts a file-leading label. +fn countbaremain(dir: str) i32 = { + let names: []str = testenv.listdir(dir); + let n: i32 = 0; + let i: i32 = 0; + for (i < names.len) { + if (strings.hassuffix(names[i], ".s")) { + let body: str = strings.concat("\n", + testenv.readfile(strings.concat(dir, "/", names[i]))); + n += testenv.occurrences(body, "\nTEXT main,"); + }; + i += 1; + }; + return n; +}; + +fn depmainrow(label: str, entry: str, want: i32, deps: str, + mangled: str) void = { + let td: str = testenv.fresh(); + let src: str = strings.concat(testenv.repo(), "/", entry); + let drvs: []str = ["ww", "ww_ww"]; + let tags: []str = ["cs", "ww"]; + let i: i32 = 0; + for (i < 2) { + let prog: str = strings.concat(td, "/", label, ".", tags[i], + ".bin"); + let av: []str = [testenv.driver(drvs[i]), "build", "-o", prog, + src]; + if (runcode(td, strings.concat("build_", tags[i]), av) != 0) { + fail(label, strings.concat(drvs[i], " build failed (bare-main ", + "collision -> w6l duplicate symbol?)")); + }; + let rav: []str = [prog]; + if (runcode(td, strings.concat("run_", tags[i]), rav) != want) { + fail(label, strings.concat(drvs[i], + " run-exit != the dep main's return")); + }; + i += 1; + }; + let csdir: str = strings.concat(td, "/", label, ".cs.bin.sepwork"); + let wwdir: str = strings.concat(td, "/", label, ".ww.bin.sepwork"); + if (!testenv.has(testenv.readfile(strings.concat(csdir, "/", deps)), + mangled)) { + fail(label, strings.concat(deps, " lacks `", mangled, + "` (dep main not mangled)")); + }; + if (countbaremain(csdir) != 1) { + fail(label, strings.concat("bare `TEXT main,` count != 1 ", + "(only the root entry stays bare)")); + }; + cmpsepwork(label, csdir, wwdir); + testenv.clean(td); +}; + +@test fn depmain_aabb() void = { + depmainrow("aabb", "test/wcc/data/depmain99/main.ww", 7, + "aa.bb.s", "TEXT aa.bb.main,"); +}; + +@test fn depmain_cc() void = { + depmainrow("cc", "test/wcc/data/depmain99/main_cc.ww", 9, + "cc.s", "TEXT cc.main,"); +}; + +// ---- declns modfn coexist (#23/#30) ------------------------------------ + +@test fn declns() void = { + let td: str = testenv.fresh(); + let lays: []str = ["modfn_coexist", "modfn_coexist_vbu"]; + let labs: []str = ["coexist", "coexist_vbu"]; + let drvs: []str = ["ww", "ww_ww"]; + let tags: []str = ["cs", "ww"]; + let l: i32 = 0; + for (l < 2) { + let s: i32 = 0; + for (s < 2) { + let prog: str = strings.concat(td, "/", labs[l], ".", + tags[s]); + let src: str = strings.concat(testenv.repo(), + "/test/wcc/data/", lays[l], "/main.ww"); + let av: []str = [testenv.driver(drvs[s]), "build", "-o", + prog, src]; + if (runcode(td, strings.concat("b_", labs[l], "_", tags[s]), + av) != 0) { + fail(labs[l], strings.concat(drvs[s], " build failed")); + }; + let rav: []str = [prog]; + if (runcode(td, strings.concat("r_", labs[l], "_", tags[s]), + rav) != 6) { + fail(labs[l], strings.concat(drvs[s], + " exit != 6 (coexistence mis-resolved)")); + }; + s += 1; + }; + l += 1; + }; + + // the root emits no .wwi post-#69, so its suffix set omits .wwi + let parts: []str = ["aa.s", "aa.wwi", "aa.unit.ww", "__root.s", + "__root.unit.ww"]; + + // cs==ww (rule 10) per layout over the fixed 5-part table + let l2: i32 = 0; + for (l2 < 2) { + let p: i32 = 0; + for (p < parts.len) { + if (!testenv.same( + testenv.readfile(strings.concat(td, "/", labs[l2], + ".cs.sepwork/", parts[p])), + testenv.readfile(strings.concat(td, "/", labs[l2], + ".ww.sepwork/", parts[p])))) { + fail(labs[l2], strings.concat("cs!=ww for ", parts[p], + " (rule 10)")); + }; + p += 1; + }; + l2 += 1; + }; + + // cross-LAYOUT byte-id, same stage: decl-order independence of the + // SK_USE/value promote. `.unit.ww` embeds the differently ORDERED + // source, so the codegen claim is tested on .s/.wwi only. + let oparts: []str = ["aa.s", "aa.wwi", "__root.s"]; + let t: i32 = 0; + for (t < 2) { + let p: i32 = 0; + for (p < oparts.len) { + if (!testenv.same( + testenv.readfile(strings.concat(td, "/coexist.", + tags[t], ".sepwork/", oparts[p])), + testenv.readfile(strings.concat(td, "/coexist_vbu.", + tags[t], ".sepwork/", oparts[p])))) { + fail("declns", strings.concat("vbu!=non-vbu for ", + oparts[p], " (", tags[t], + ") -- silent decl-order dependence")); + }; + p += 1; + }; + t += 1; + }; + + // non-vacuity: the two namespaces resolve to DISTINCT symbols + let t2: i32 = 0; + for (t2 < 2) { + let rs: str = testenv.readfile(strings.concat(td, "/coexist.", + tags[t2], ".sepwork/__root.s")); + if (!testenv.has(rs, "CALL\taa.helper(SB)")) { + fail("declns", strings.concat("__root.s (", tags[t2], + ") lacks module-qualified `CALL aa.helper(SB)`")); + }; + if (!testenv.has(rs, "CALL\tmain.aa(SB)")) { + fail("declns", strings.concat("__root.s (", tags[t2], + ") lacks local-fn `CALL main.aa(SB)`")); + }; + t2 += 1; + }; + testenv.clean(td); +}; + +// ---- slttypepref (#58/#50 c1) ------------------------------------------ + +// fresh two-directory tree per (row,driver); relative build with the +// tree as cwd (canonical dir-package build, binary lands in cwd). +fn sltbuild(drv: str, param: str) i32 = { + let dir: str = testenv.fresh(); + assert(os.mkdir(strings.concat(dir, "/xa"), 493) == 0); + assert(os.mkdir(strings.concat(dir, "/xb"), 493) == 0); + testenv.writefile(strings.concat(dir, "/xa/xa.ww"), strings.concat( + "package xa;\n", + "export type invalid = !i8;\n")); + testenv.writefile(strings.concat(dir, "/xb/xb.ww"), strings.concat( + "package xb;\n", + "import xa;\n", + "export type invalid = !i64;\n", + "export fn f(", param, + ": i32) i64 = { return size(invalid): i64; };\n")); + testenv.writefile(strings.concat(dir, "/root.ww"), strings.concat( + "package main;\n", + "import xb;\n", + "import xa;\n", + "fn main() int = { return xb.f(0): int; };\n")); + let bav: []str = [testenv.driver(drv), "build", "root.ww"]; + let brc: i32 = runcode(dir, "build", bav); + if (brc != 0) { + testenv.clean(dir); + return -1; + }; + let rav: []str = [strings.concat(dir, "/root")]; + let got: i32 = runcode(dir, "run", rav); + testenv.clean(dir); + return got; +}; + +fn sltrow(label: str, param: str, want: i32) void = { + let gc: i32 = sltbuild("ww", param); + if (gc != want) { fail(label, "cstage exit != want (size of the wrong module's type)"); }; + let gw: i32 = sltbuild("ww_ww", param); + if (gw != gc) { + fail(label, strings.concat("cs != ww exit (scopelookuptype ", + "mod-preference divergence -- #58/#50 c1)")); + }; + if (gw != want) { fail(label, "wwstage exit != want"); }; +}; + +@test fn slt_shadow() void = { sltrow("shadow", "invalid", 8); }; + +@test fn slt_noshadow() void = { sltrow("noshadow", "x", 8); }; diff --git a/test/wcc/989_coloimport_sep.c b/test/wcc/989_coloimport_sep.c deleted file mode 100644 index f6abbb96..00000000 --- a/test/wcc/989_coloimport_sep.c +++ /dev/null @@ -1,257 +0,0 @@ -/* - * 989_coloimport_sep — #98 E3 flip-blocker: a co-located `_test` entry must - * not shadow the dir-package it imports under `ww test`. - * - * The driver builds the sep searchpath srcd-first (srcd = the entry file's - * own directory). When the entry is a co-located black-box test - * `lib//test.ww`, srcd = `lib/`, so a transitive `import - * ` (reached from another package in the closure) hits the sibling - * FILE `lib//.ww` (is_dir=0) FIRST — treated as an intra-package - * split, folded INLINE into the importer's unit under that importer's - * module-reset → w6c rejects `package does not match import path - * `. locate_import / locateimport now walk the searchpath in - * TWO passes — pass 1 seeks a DIRECTORY on any entry, pass 2 (only if no - * directory matches anywhere) seeks a FILE — so the dir-package `/` - * on a later entry beats the same-named sibling FILE on the srcd entry, - * while a genuine leaf package with no directory still resolves in the - * file pass. This gate stands for the 7 lib-run tests - * (904/966/967/972/974/976/977) the bug blocked under sep. - * - * Fixture (test/wcc/data/colo98/, the exact srcd-shadow shape, minimised): - * widget/widget.ww package widget — the sibling primary file - * that shadows the dir. - * widget/widgettest.ww package widget_test, import gadget — the - * co-located test ENTRY; - * srcd = .../widget. - * gadget/gadget.ww package gadget, import widget — the NON-root - * importer compiled with -I, - * whose self-named `import - * widget` triggers the fold. - * `-I test/wcc/data/colo98` puts widget's parent on the fallback path, - * mirroring how `lib` holds the real `lib//` dirs. - * - * Asserts (both driver stages, direct builds): - * 1. RUN-EXIT 0 — the @test resolves, builds, links, runs. Pre-fix the - * non-root importer's fold makes w6c reject → non-zero; this is the - * non-vacuity teeth (verified: revert the two-pass walker → reddens - * with `package widget does not match import path gadget`). - * 2. REGISTERED — widget.unit.ww + widget.wwi appear in .sepwork: the - * import resolved to the DIR-package, not the sibling file. Pre-fix - * widget is never registered (folds into the importer), so both are - * absent — the "right module tag" signal. - * 3. cs==ww (rule 10) — per-package .s/.wwi byte-identical across the - * cstage `ww` and wwstage `ww_ww` sep-drivers, proving the `locatein` - * port is symmetric with `locate_import_in`. - * - * Every intermediate is `-o`-redirected to /tmp for isolation. Models - * 989_septest_run conventions; 989 prefix per the sep-gate precedent. - */ -#include -#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; -} - -/* cs==ww over a sep build's per-package output: every .s/.wwi the cstage - * driver produced in `csdir` must be byte-identical to the wwstage - * driver's same-named file in `wwdir`. Returns the count of mismatches. */ -static int -cmp_sepwork(const char *csdir, const char *wwdir, const char *label) -{ - DIR *d = opendir(csdir); - if (!d) { - fprintf(stderr, "coloimport FAIL: %s — no cs sepwork %s\n", label, csdir); - return 1; - } - int bad = 0, seen = 0; - struct dirent *ent; - while ((ent = readdir(d)) != NULL) { - const char *nm = ent->d_name; - size_t nl = strlen(nm); - int is_s = (nl > 2 && strcmp(nm + nl - 2, ".s") == 0); - int is_wwi = (nl > 4 && strcmp(nm + nl - 4, ".wwi") == 0); - if (!is_s && !is_wwi) continue; - seen++; - char a[2048], b[2048]; - snprintf(a, sizeof a, "%s/%s", csdir, nm); - snprintf(b, sizeof b, "%s/%s", wwdir, nm); - if (files_eq(a, b) != 0) { - fprintf(stderr, "coloimport FAIL: %s — cs!=ww for %s (rule 10)\n", - label, nm); - bad++; - } - } - closedir(d); - if (seen == 0) { - fprintf(stderr, "coloimport FAIL: %s — no .s/.wwi in %s\n", label, csdir); - bad++; - } - return bad; -} - -struct tcase { - const char *label; - const char *entry; /* the co-located `_test` build target */ - const char *incdir; /* -I dir (parent that holds the dir-package) */ - const char *regpkg; /* the dir-package that must register its own unit */ -}; - -static struct tcase cases[] = { - /* the transitive self-named-import shadow — the exact lib-run shape. */ - { "widget", "test/wcc/data/colo98/widget/widgettest.ww", - "test/wcc/data/colo98", "widget" }, - { NULL, NULL, NULL, NULL }, -}; - -int -main(void) -{ - const char *bin = absbin(); - if (!bin) return 1; - - char td[64], cmd[8192]; - int fail = 0; - snprintf(td, sizeof td, "/tmp/wwcoloimport_%d", getpid()); - if (mkdir(td, 0755) != 0) { - perror(td); - return 1; - } - - for (int i = 0; cases[i].label; i++) { - struct tcase *t = &cases[i]; - - struct { const char *drv, *tag; int rc; } - stg[] = { { "ww", "cs", -1 }, { "ww_ww", "ww", -1 } }; - for (int s = 0; s < 2; s++) { - char prog[1024]; - snprintf(prog, sizeof prog, "%s/%s.%s.bin", td, t->label, stg[s].tag); - /* Each direct build produces the per-package .s/.wwi artifacts - * this gate inspects. */ - snprintf(cmd, sizeof cmd, - "%s/%s test -I %s -o %s %s " - ">/dev/null 2>&1", - bin, stg[s].drv, t->incdir, prog, - t->entry); - stg[s].rc = runwait(cmd); - } - - /* 1. RUN-EXIT 0 on BOTH stages (pre-fix: w6c rejects the importer). */ - if (stg[0].rc != 0 || stg[1].rc != 0) { - fprintf(stderr, "coloimport FAIL: %s exits cs=%d ww=%d (expected 0)\n", - t->label, stg[0].rc, stg[1].rc); - fail++; - continue; - } - - char csdir[1024], wwdir[1024]; - snprintf(csdir, sizeof csdir, "%s/%s.cs.bin.sepwork", td, t->label); - snprintf(wwdir, sizeof wwdir, "%s/%s.ww.bin.sepwork", td, t->label); - - /* 2. REGISTERED: the dir-package owns its unit + interface (pre-fix - * it folds into the importer and these are absent). */ - char u[2048], w[2048]; - snprintf(u, sizeof u, "%s/%s.unit.ww", csdir, t->regpkg); - snprintf(w, sizeof w, "%s/%s.wwi", csdir, t->regpkg); - if (access(u, 0) != 0) { - fprintf(stderr, "coloimport FAIL: %s — %s missing (import folded " - "to sibling file, not the dir-package)\n", t->label, u); - fail++; - } - if (access(w, 0) != 0) { - fprintf(stderr, "coloimport FAIL: %s — %s missing\n", t->label, w); - fail++; - } - - /* 3. cs==ww (rule 10): per-package .s/.wwi byte-identical. */ - fail += cmp_sepwork(csdir, wwdir, t->label); - } - - for (int i = 0; cases[i].label; i++) { - const char *tags[] = { "cs", "ww" }; - for (int s = 0; s < 2; s++) { - char prog[1024], work[1100]; - snprintf(prog, sizeof prog, "%s/%s.%s.bin", td, - cases[i].label, tags[s]); - if (unlink(prog) != 0 && errno != ENOENT) { - perror(prog); - fail++; - } - snprintf(work, sizeof work, "%s.sepwork", prog); - snprintf(cmd, sizeof cmd, "rm -rf %s", work); - if (runwait(cmd) != 0) { - fprintf(stderr, "coloimport: cleanup failed: %s\n", work); - fail++; - } - } - } - if (rmdir(td) != 0) { - perror(td); - fail++; - } - if (fail) { - fprintf(stderr, "coloimport: %d check(s) failed\n", fail); - return 1; - } - printf("coloimport: co-located `_test` entry resolves `import ` to the " - "dir-package (not the sibling file) under `ww test` on both driver " - "stages + cs==ww per-pkg .s/.wwi\n"); - return 0; -} diff --git a/test/wcc/989_declns_sep.c b/test/wcc/989_declns_sep.c deleted file mode 100644 index a954ffce..00000000 --- a/test/wcc/989_declns_sep.c +++ /dev/null @@ -1,308 +0,0 @@ -/* - * 989_declns_sep — #23/#30 declaration-namespace + module-fn coexistence - * under the directory-package model (task #83, M4 E2-C2c). - * The flat duplicate and builtin runtime rows now belong to r989_declns_* - * (with duplicate-type already owned by runww_dup_type_reject). This - * residual retains only the modfn separate-package artifact leg. - * - * Re-hosts every decl-namespace assertion that once lived ONLY in - * 910/997 onto a gate that survives the M4 flip (910/997 were deleted - * at the flip — they fed the retired amalgamation inputs). Three legs: - * - * dup family — a duplicate top-level fn/type/def/let in one (flat) - * module must build-FAIL on BOTH stages. The fixtures - * are flat single-package (the surviving shape) and feed - * `w6c` / `w6c_ww` DIRECTLY — no driver resolution; the - * dup is an install-pass property, not a build-model one. - * 712 covered the cstage let arm; 997 covered the wwstage - * arm — this gate asserts BOTH stages reject every kind, - * closing the ww arm that dies with 997. - * builtin_redecl— the carve-out: a user redecl of a pre-seeded builtin - * name (`nomem`) is NOT a duplicate. cstage keeps no - * builtins in scope (lookup_builtin wins first), wwstage - * seeds them into c.top so installtop must DROP the redecl - * rather than error. Both stages accept + run to 7. - * modfn coexist — a top-level value-namespace `fn aa` and an imported - * MODULE `aa` coexist: bare `aa()` binds the fn (a module - * is not callable), `aa.helper()` binds through the module. - * RESHAPED from the dying multi-`package`-single-file - * 910/997 fixtures to dir-packages (696 precedent), built - * via `ww build` + run. The _vbu twin flips the decl - * order (`fn aa` BEFORE `import aa`): cstage installs every - * SK_USE in an order-independent first pass, wwstage in - * source order, so vbu exercises a DISTINCT promote path — - * its asm must be byte-identical to the use-before-value - * order, the teeth against silent order-dependence. - * - * dup_xpkg (same leaf in distinct packages = legal) RIDES on 696/697's - * dir-package coverage — no port (spec C2c). - * - * Every modfn intermediate is `-o`-redirected to /tmp for isolation; the dup + - * builtin legs feed w6c directly with `-o /tmp`. Models 989_sepbuild_run + - * 911_attest_drop conventions; 989 prefix per the sep-gate precedent (the - * 7xx cgen/check range is exhausted). - */ -#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; -} - -/* 0 if `needle` occurs in the file at `path`, else 1 (or -1 on read err). */ -static int -file_contains(const char *path, const char *needle) -{ - char *b = NULL; - size_t n = 0; - if (slurp(path, &b, &n) < 0) return -1; - int found = (strstr(b, needle) != NULL); - free(b); - return found ? 0 : 1; -} - -/* - * Duplicate-declaration and builtin-redeclaration runtime ownership moved to - * r989_declns_* fixtures. This wrapper retains only sep/package artifacts. - */ - -/* - * modfn coexist — the two dir-package layouts. Build each via `ww build - * ` + run, both driver stages, expect exit 6 (aa()=1 + aa.helper()=5). - * `ww build` (NOT `ww run`) per spec: run is not sep-wired until the driver - * flip. Returns 0 on success; leaves .sepwork in `td` for the byte-id - * compares below. - */ -struct modfn { const char *lay; const char *fixture; }; -static const struct modfn modfn_lays[] = { - { "coexist", "test/wcc/data/modfn_coexist/main.ww" }, - { "coexist_vbu", "test/wcc/data/modfn_coexist_vbu/main.ww" }, -}; - -/* per-package output the sep driver materializes; the root carries no .wwi - * (post-#69 it is compiled without -I), so its suffix set omits .wwi. */ -static const struct { - const char *pkg; - const char *suf; -} parts[] = { - { "aa", ".s" }, - { "aa", ".wwi" }, - { "aa", ".unit.ww" }, - { "__root", ".s" }, - { "__root", ".unit.ww" }, -}; - -static int -modfn_build(const char *bin, const char *td) -{ - char cmd[4096], prog[1024]; - int rc = 0; - for (size_t l = 0; l < sizeof modfn_lays / sizeof modfn_lays[0]; l++) { - for (size_t s = 0; s < 2; s++) { - const char *drv = (s == 0) ? "ww" : "ww_ww"; - snprintf(prog, sizeof prog, "%s/%s.%s", td, modfn_lays[l].lay, - (s == 0) ? "cs" : "ww"); - snprintf(cmd, sizeof cmd, "%s/%s build -o %s %s 2>/dev/null", - bin, drv, prog, modfn_lays[l].fixture); - if (runwait(cmd) != 0) { - fprintf(stderr, "declns FAIL: %s build %s\n", drv, - modfn_lays[l].lay); - rc = 1; - continue; - } - int got = runwait(prog); - if (got != 6) { - fprintf(stderr, "declns FAIL: modfn %s (%s) exit=%d want=6 " - "(coexistence mis-resolved)\n", modfn_lays[l].lay, drv, got); - rc = 1; - } - } - } - return rc; -} - -/* cs==ww (rule 10): for one layout, every per-package .s/.wwi/.unit.ww the - * cstage and wwstage sep drivers emitted must be byte-identical. */ -static int -modfn_cs_eq_ww(const char *td, const char *lay) -{ - int rc = 0; - for (size_t p = 0; p < sizeof parts / sizeof parts[0]; p++) { - char a[1024], b[1024]; - snprintf(a, sizeof a, "%s/%s.cs.sepwork/%s%s", td, lay, parts[p].pkg, parts[p].suf); - snprintf(b, sizeof b, "%s/%s.ww.sepwork/%s%s", td, lay, parts[p].pkg, parts[p].suf); - if (files_eq(a, b) != 0) { - fprintf(stderr, "declns FAIL: %s cs!=ww for %s%s (rule 10)\n", - lay, parts[p].pkg, parts[p].suf); - rc = 1; - } - } - return rc; -} - -/* cross-order byte-id: the value-before-use layout must emit asm/interface - * byte-identical to the use-before-value layout (same stage). Silent - * order-dependence in the SK_USE/value-sym promote is exactly what regresses; - * a divergence here is the teeth. */ -static int -modfn_order_id(const char *td) -{ - int rc = 0; - const char *tags[] = { "cs", "ww" }; - for (size_t t = 0; t < sizeof tags / sizeof tags[0]; t++) { - for (size_t p = 0; p < sizeof parts / sizeof parts[0]; p++) { - /* `.unit.ww` is the driver's verbatim source-assembly - * intermediate — it embeds the two layouts' differently - * ORDERED source text, so it differs by construction. The - * order-independence claim is about CODEGEN, so it is tested - * on the compiled `.s`/`.wwi` only. */ - if (strcmp(parts[p].suf, ".unit.ww") == 0) continue; - char a[1024], b[1024]; - snprintf(a, sizeof a, "%s/coexist.%s.sepwork/%s%s", - td, tags[t], parts[p].pkg, parts[p].suf); - snprintf(b, sizeof b, "%s/coexist_vbu.%s.sepwork/%s%s", - td, tags[t], parts[p].pkg, parts[p].suf); - if (files_eq(a, b) != 0) { - fprintf(stderr, "declns FAIL: vbu!=non-vbu for %s%s (%s) — " - "silent decl-order dependence\n", parts[p].pkg, - parts[p].suf, tags[t]); - rc = 1; - } - } - } - return rc; -} - -/* non-vacuity: __root.s must carry BOTH calls distinctly — through the - * module (`aa.helper`) and to the local value fn (`main.aa`) — proving the - * two namespaces resolved to different symbols rather than collapsing. */ -static int -modfn_coexist_present(const char *td) -{ - int rc = 0; - const char *tags[] = { "cs", "ww" }; - for (size_t t = 0; t < sizeof tags / sizeof tags[0]; t++) { - char rs[1024]; - snprintf(rs, sizeof rs, "%s/coexist.%s.sepwork/__root.s", td, tags[t]); - if (file_contains(rs, "CALL\taa.helper(SB)") != 0) { - fprintf(stderr, "declns FAIL: __root.s (%s) lacks module-qualified " - "`CALL aa.helper(SB)`\n", tags[t]); - rc = 1; - } - if (file_contains(rs, "CALL\tmain.aa(SB)") != 0) { - fprintf(stderr, "declns FAIL: __root.s (%s) lacks local-fn " - "`CALL main.aa(SB)`\n", tags[t]); - rc = 1; - } - } - return rc; -} - -int -main(void) -{ - const char *bin = absbin(); - if (!bin) { fprintf(stderr, "declns FAIL: getcwd\n"); return 1; } - - int fail = 0; - - char td[64], cmd[256]; - snprintf(td, sizeof td, "/tmp/wwdeclns_%d", getpid()); - if (mkdir(td, 0755) != 0) { - perror(td); - return 1; - } - - fail += modfn_build(bin, td); - fail += modfn_cs_eq_ww(td, "coexist"); - fail += modfn_cs_eq_ww(td, "coexist_vbu"); - fail += modfn_order_id(td); - fail += modfn_coexist_present(td); - - const char *tags[] = { "cs", "ww" }; - for (size_t l = 0; l < sizeof modfn_lays / sizeof modfn_lays[0]; l++) { - for (size_t s = 0; s < sizeof tags / sizeof tags[0]; s++) { - char prog[1024], work[1100]; - snprintf(prog, sizeof prog, "%s/%s.%s", td, - modfn_lays[l].lay, tags[s]); - if (unlink(prog) != 0 && errno != ENOENT) { - perror(prog); - fail++; - } - snprintf(work, sizeof work, "%s.sepwork", prog); - snprintf(cmd, sizeof cmd, "rm -rf %s", work); - if (runwait(cmd) != 0) { - fprintf(stderr, "declns: cleanup failed: %s\n", work); - fail++; - } - } - } - if (rmdir(td) != 0) { - perror(td); - fail++; - } - - if (fail) { - fprintf(stderr, "declns: %d check(s) failed\n", fail); - return 1; - } - printf("declns: modfn dir-pkg coexist→6 (cs==ww, " - "vbu byte-id to use-before-value), both stages (#23/#30)\n"); - return 0; -} diff --git a/test/wcc/989_depmain_sep.c b/test/wcc/989_depmain_sep.c deleted file mode 100644 index 8750b781..00000000 --- a/test/wcc/989_depmain_sep.c +++ /dev/null @@ -1,289 +0,0 @@ -/* - * 989_depmain_sep — #99 E3 flip-blocker: an IMPORTED dir-package's non- - * exported `fn main` must mangle on its import path under `ww build`, - * not emit a bare `TEXT main` that collides with the root unit's entry. - * - * The bare-`main` carve-out (cgen mod_collect + cgfn label, both stages) - * keys on `leaf=="main" && imported==0`. Each package is its own w6c unit, - * composed with a path-carrying `//ww:module-reset ` - * (#57) that mangles decls but leaves imported==0 — so a dep unit's `fn - * main` was imported==0 too, the carve-out fired, and it emitted a bare - * `TEXT main` → `w6l: duplicate symbol main` against the root's real main. - * - * The fix adds a NON-ROOT gate `&& !sep_isdep` to the carve-out. sep_isdep - * is set from `wwiout != NULL`: the producer passes -I (the .wwi output) to - * DEP units ONLY — the root/link-entry unit's .wwi is stripped (#69) — so - * `wwiout==NULL` is the exact "this is the root/link-entry unit" oracle. A - * dep's main now mangles on its path; only the root's stays bare. - * - * Fixtures (test/wcc/data/depmain99/, srcd-relative dir-package resolution): - * main.ww package main, import aa.bb — root; calls bb.run() → 7. - * aa/bb/bb.ww package bb, `fn main`→7 + `export fn run` calls it — the - * DOTTED-path imported dep whose main must mangle aa.bb.main. - * main_cc.ww package main, import cc — root; calls cc.run() → 9. - * cc/cc.ww package cc, `fn main`→9 + `export fn run` — the SINGLE- - * component imported dep whose main must mangle cc.main. - * - * Asserts (both driver stages, direct builds): - * 1. SEP build+link+run exit == the dep's main's return — pre-fix the - * bare-main collision makes w6l reject. This is the non-vacuity teeth - * (verified: revert `&& !sep_isdep` → `w6l: duplicate symbol main`). - * 2. MANGLED — the dep's .s shows `TEXT .main`; exactly ONE bare - * `TEXT main,` across the whole sep build (the root's __root.s). - * 3. cs==ww (rule 10) — per-package .s/.wwi byte-identical across the - * cstage `ww` and wwstage `ww_ww` sep-drivers. - * - * Every intermediate is `-o`-redirected to /tmp for isolation. Models 989_coloimport_sep - * conventions; 989 prefix per the sep-gate precedent. - */ -#include -#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 -2; -} - -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; -} - -/* cs==ww over a sep build's per-package output: every .s/.wwi the cstage - * driver produced in `csdir` must be byte-identical to the wwstage driver's - * same-named file in `wwdir`. Returns the count of mismatches. */ -static int -cmp_sepwork(const char *csdir, const char *wwdir, const char *label) -{ - DIR *d = opendir(csdir); - if (!d) { - fprintf(stderr, "depmain FAIL: %s — no cs sepwork %s\n", label, csdir); - return 1; - } - int bad = 0, seen = 0; - struct dirent *ent; - while ((ent = readdir(d)) != NULL) { - const char *nm = ent->d_name; - size_t nl = strlen(nm); - int is_s = (nl > 2 && strcmp(nm + nl - 2, ".s") == 0); - int is_wwi = (nl > 4 && strcmp(nm + nl - 4, ".wwi") == 0); - if (!is_s && !is_wwi) continue; - seen++; - char a[2048], b[2048]; - snprintf(a, sizeof a, "%s/%s", csdir, nm); - snprintf(b, sizeof b, "%s/%s", wwdir, nm); - if (files_eq(a, b) != 0) { - fprintf(stderr, "depmain FAIL: %s — cs!=ww for %s (rule 10)\n", - label, nm); - bad++; - } - } - closedir(d); - if (seen == 0) { - fprintf(stderr, "depmain FAIL: %s — no .s/.wwi in %s\n", label, csdir); - bad++; - } - return bad; -} - -/* count_bare_main — number of bare `TEXT main,` labels across every .s in - * `dir`. Exactly 1 is correct (the root entry); the bug produces 2 (the - * dep's mis-bared main collides). */ -static int -count_bare_main(const char *dir) -{ - DIR *d = opendir(dir); - if (!d) return -1; - int n = 0; - struct dirent *ent; - while ((ent = readdir(d)) != NULL) { - const char *nm = ent->d_name; - size_t nl = strlen(nm); - if (!(nl > 2 && strcmp(nm + nl - 2, ".s") == 0)) continue; - char p[2048]; - snprintf(p, sizeof p, "%s/%s", dir, nm); - char *b = NULL; - size_t bn = 0; - if (slurp(p, &b, &bn) < 0) continue; - /* a bare label is `TEXT main,` at column 0 of any line. */ - for (char *s = b; (s = strstr(s, "TEXT main,")) != NULL; s++) - if (s == b || s[-1] == '\n') n++; - free(b); - } - closedir(d); - return n; -} - -/* file_has — 0 if `needle` occurs in `path`, 1 if absent, -1 on read err. */ -static int -file_has(const char *path, const char *needle) -{ - char *b = NULL; - size_t bn = 0; - if (slurp(path, &b, &bn) < 0) return -1; - int found = (strstr(b, needle) != NULL); - free(b); - return found ? 0 : 1; -} - -struct tcase { - const char *label; - const char *entry; /* root build target */ - int exit; /* the dep main's return, == the program exit */ - const char *deps; /* the dep's .s basename in .sepwork */ - const char *mangled; /* the mangled TEXT label the dep must emit */ -}; - -static struct tcase cases[] = { - /* dotted-path import: dep main mangles on the full path. */ - { "aabb", "test/wcc/data/depmain99/main.ww", 7, - "aa.bb.s", "TEXT aa.bb.main," }, - /* single-component import: dep main mangles on the leaf package. */ - { "cc", "test/wcc/data/depmain99/main_cc.ww", 9, - "cc.s", "TEXT cc.main," }, - { NULL, NULL, 0, NULL, NULL }, -}; - -int -main(void) -{ - const char *bin = absbin(); - if (!bin) return 1; - - char td[64], cmd[8192]; - int fail = 0; - snprintf(td, sizeof td, "/tmp/wwdepmain_%d", getpid()); - if (mkdir(td, 0755) != 0) { - perror(td); - return 1; - } - - for (int i = 0; cases[i].label; i++) { - struct tcase *t = &cases[i]; - - struct { const char *drv, *tag; int rc; } - stg[] = { { "ww", "cs", -1 }, { "ww_ww", "ww", -1 } }; - - /* 1. SEP build+link+run exit == the dep main's return, both stages. */ - for (int s = 0; s < 2; s++) { - char prog[1024]; - snprintf(prog, sizeof prog, "%s/%s.%s.bin", td, t->label, stg[s].tag); - snprintf(cmd, sizeof cmd, - "%s/%s build -o %s %s " - ">/dev/null 2>&1 && %s", - bin, stg[s].drv, prog, t->entry, prog); - stg[s].rc = runwait(cmd); - if (stg[s].rc != t->exit) { - fprintf(stderr, "depmain FAIL: %s sep %s exit=%d (expected %d)\n", - t->label, stg[s].tag, stg[s].rc, t->exit); - fail++; - } - } - - char csdir[1024], wwdir[1024]; - snprintf(csdir, sizeof csdir, "%s/%s.cs.bin.sepwork", td, t->label); - snprintf(wwdir, sizeof wwdir, "%s/%s.ww.bin.sepwork", td, t->label); - - /* 2. MANGLED: the dep's .s emits `TEXT .main`; exactly one bare - * `TEXT main,` across the whole sep build (the root's __root.s). */ - char deps[2048]; - snprintf(deps, sizeof deps, "%s/%s", csdir, t->deps); - if (file_has(deps, t->mangled) != 0) { - fprintf(stderr, "depmain FAIL: %s — %s lacks `%s` (dep main not " - "mangled)\n", t->label, deps, t->mangled); - fail++; - } - int nbare = count_bare_main(csdir); - if (nbare != 1) { - fprintf(stderr, "depmain FAIL: %s — %d bare `TEXT main,` in %s " - "(expected 1, the root entry)\n", t->label, nbare, csdir); - fail++; - } - - /* 3. cs==ww (rule 10): per-package .s/.wwi byte-identical. */ - fail += cmp_sepwork(csdir, wwdir, t->label); - - } - - for (int i = 0; cases[i].label; i++) { - const char *tags[] = { "cs", "ww" }; - for (int s = 0; s < 2; s++) { - char prog[1024], work[1100]; - snprintf(prog, sizeof prog, "%s/%s.%s.bin", td, - cases[i].label, tags[s]); - if (unlink(prog) != 0 && errno != ENOENT) { - perror(prog); - fail++; - } - snprintf(work, sizeof work, "%s.sepwork", prog); - snprintf(cmd, sizeof cmd, "rm -rf %s", work); - if (runwait(cmd) != 0) { - fprintf(stderr, "depmain: cleanup failed: %s\n", work); - fail++; - } - } - } - if (rmdir(td) != 0) { - perror(td); - fail++; - } - if (fail) { - fprintf(stderr, "depmain: %d check(s) failed\n", fail); - return 1; - } - printf("depmain: imported dir-package `fn main` mangles on its import path " - "under `ww build` (only the root entry stays bare) on both driver " - "stages + cs==ww per-pkg .s/.wwi\n"); - return 0; -} diff --git a/test/wcc/989_slttypepref_run.c b/test/wcc/989_slttypepref_run.c deleted file mode 100644 index b8ebe81c..00000000 --- a/test/wcc/989_slttypepref_run.c +++ /dev/null @@ -1,221 +0,0 @@ -/* - * 989_slttypepref_run (#58/#50, c1) — scopelookuptype must prefer the - * current module's SK_TYPE when a value binding shadows a type leaf that - * two modules both export. - * - * THE BUG (wwstage only, cat-A silent wrong-binary under rc=0): the - * aliassym fallback (check.ww ~908) composes scopelookupprefer-then- - * scopelookuptype; lib/ww/sym.ww scopelookuptype had NO mod parameter and - * returned the first (newest-installed) SK_TYPE in the bucket chain. - * scopedefineinmodule PREPENDS, so chain-first = last-registered. When a - * param/local shadows the type leaf (scopelookupprefer lands on the value, - * not the SK_TYPE) and two modules export the same type leaf, wwstage - * silently resolved whichever module installed last — install-order - * dependent — while cstage passes c->cur_mod to scope_lookup_type - * (sym.c:131) and resolves the current module's type deterministically. - * THE FIX: give scopelookuptype a `mod` param and prefer the curmod- - * matching SK_TYPE (Pass-1 mod-match, Pass-2 chain-first fallback); - * the sole caller passes c.curmod. Mirror of cstage scope_lookup_type. - * - * row | shape | exit (cs==ww) - * ---------+-----------------------------------------------+-------------- - * shadow | xb.f param `invalid` shadows type `invalid`; | 8 (was ww 1) - * | xb !i64, xa !i8; size(invalid) in xb.f | - * noshadow | param renamed (no shadow), aliassym not hit | 8 (control) - * - * The shadow row was RED pre-c1 (ww resolved xa's !i8 -> exit 1, the wrong - * module's type under rc=0). The noshadow control pins the non-shadow path - * (scopelookupprefer lands on the SK_TYPE directly) so the fix can't - * perturb it. - * - * E3-C1 retarget: the single-file multi-package source the original used - * is the amalgamator shape, deleted with the flip (ww is now strictly - * dir=module). Re-expressed as a dir-package tree: xb/ and xa/ both export - * `type invalid`, and xb IMPORTS xa so both `invalid` leaves are in scope - * during xb's sep-compile — the exact two-module same-leaf collision the - * scopelookuptype mod-preference fix governs. A regression flips to xa's - * !i8 -> exit 1. - */ -#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; -} - -/* Only xb's param name varies between rows; xa and the root are shared. - * `xb_param` is the parameter spelling: "invalid" shadows the type leaf, - * "x" does not. */ -struct row { const char *label; const char *xb_param; int want_exit; }; - -static const struct row rows[] = { - { "shadow", "invalid", 8 }, - { "noshadow", "x", 8 }, -}; - -static int -write_file(const char *path, const char *content) -{ - FILE *f = fopen(path, "wb"); - if (!f) return -1; - if (fputs(content, f) == EOF) { fclose(f); return -1; } - return fclose(f); -} - -static int -run_build(const char *driver, const struct row *r, int *cleanup_failed) -{ - char dir[] = "/tmp/sltp_XXXXXX"; - char sub[128], path[256], xbsrc[512], cmd[1024]; - int brc = -1, got = -2, cleanup_fail = 0; - int have_xa = 0, have_xb = 0; - *cleanup_failed = 0; - if (mkdtemp(dir) == NULL) return -2; - - snprintf(path, sizeof path, "%s/xa", dir); - if (mkdir(path, 0755) != 0) goto out; - have_xa = 1; - snprintf(path, sizeof path, "%s/xb", dir); - if (mkdir(path, 0755) != 0) goto out; - have_xb = 1; - - snprintf(path, sizeof path, "%s/xa/xa.ww", dir); - if (write_file(path, "package xa;\nexport type invalid = !i8;\n")) - goto out; - - snprintf(sub, sizeof sub, "%s/xb/xb.ww", dir); - snprintf(xbsrc, sizeof xbsrc, - "package xb;\n" - "import xa;\n" - "export type invalid = !i64;\n" - "export fn f(%s: i32) i64 = { return size(invalid): i64; };\n", - r->xb_param); - if (write_file(sub, xbsrc)) goto out; - - snprintf(path, sizeof path, "%s/root.ww", dir); - if (write_file(path, - "package main;\n" - "import xb;\n" - "import xa;\n" - "fn main() int = { return xb.f(0): int; };\n")) - goto out; - - /* canonical dir-package build: cd into the package root, build the - * root unit; the binary lands in cwd (#22 dir=module). */ - snprintf(cmd, sizeof cmd, "cd %s && %s build root.ww 2>/dev/null", - dir, driver); - brc = runwait(cmd); - - char outbin[128]; - snprintf(outbin, sizeof outbin, "%s/root", dir); - got = -1; - if (brc == 0) got = runwait(outbin); - -out: - snprintf(cmd, sizeof cmd, "rm -rf -- '%s/root.sepwork'", dir); - if (runwait(cmd) != 0) cleanup_fail = 1; - snprintf(path, sizeof path, "%s/root", dir); - if (unlink(path) != 0 && errno != ENOENT) cleanup_fail = 1; - snprintf(path, sizeof path, "%s/root.ww", dir); - if (unlink(path) != 0 && errno != ENOENT) cleanup_fail = 1; - if (have_xb) { - snprintf(path, sizeof path, "%s/xb/xb.ww", dir); - if (unlink(path) != 0 && errno != ENOENT) cleanup_fail = 1; - snprintf(path, sizeof path, "%s/xb", dir); - if (rmdir(path) != 0 && errno != ENOENT) cleanup_fail = 1; - } - if (have_xa) { - snprintf(path, sizeof path, "%s/xa/xa.ww", dir); - if (unlink(path) != 0 && errno != ENOENT) cleanup_fail = 1; - snprintf(path, sizeof path, "%s/xa", dir); - if (rmdir(path) != 0 && errno != ENOENT) cleanup_fail = 1; - } - if (rmdir(dir) != 0) cleanup_fail = 1; - *cleanup_failed = cleanup_fail; - return brc == 0 ? got : -1; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - char absbin[1024]; - if (bin[0] != '/') { - char cwd[1024]; - if (getcwd(cwd, sizeof cwd) == NULL) return 1; - snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); - bin = absbin; - } - - char cdrv[1024], wdrv[1024]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - int have_ww = (access(wdrv, X_OK) == 0); - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - - for (int i = 0; i < n; i++) { - total++; - int cleanup_failed = 0; - int gc = run_build(cdrv, &rows[i], &cleanup_failed); - if (cleanup_failed) { - fprintf(stderr, - "slttypepref[cstage][%s]: temporary cleanup failed\n", - rows[i].label); - fail++; - } - if (gc < 0) { - fprintf(stderr, "slttypepref[cstage][%s]: build/run failed " - "(got %d)\n", rows[i].label, gc); - fail++; - continue; - } - if (gc != rows[i].want_exit) { - fprintf(stderr, "slttypepref[cstage][%s]: exit=%d want=%d\n", - rows[i].label, gc, rows[i].want_exit); - fail++; - } - if (!have_ww) { - fprintf(stderr, "slttypepref: skip wwstage (no %s)\n", wdrv); - continue; - } - cleanup_failed = 0; - int gw = run_build(wdrv, &rows[i], &cleanup_failed); - if (cleanup_failed) { - fprintf(stderr, - "slttypepref[wwstage][%s]: temporary cleanup failed\n", - rows[i].label); - fail++; - } - if (gw != gc) { - fprintf(stderr, "slttypepref[%s]: cs=%d != ww=%d " - "(scopelookuptype mod-preference divergence — #58/#50 c1)\n", - rows[i].label, gc, gw); - fail++; - } - if (gw != rows[i].want_exit) { - fprintf(stderr, "slttypepref[wwstage][%s]: exit=%d want=%d\n", - rows[i].label, gw, rows[i].want_exit); - fail++; - } - } - - if (fail) { - fprintf(stderr, "slttypepref_run: %d/%d checks failed\n", - fail, total); - return 1; - } - printf("slttypepref_run: %d/%d ok\n", total, total); - return 0; -}