/* * 997_at_test_ww — the wwstage twin of 910_at_test, driving the `-T` * @test mode through `w6c_ww` (the ww-built compiler) instead of `w6c` * (the C bootstrap). Task #15. * * Probes: * 1. RUN — `w6c_ww -T` the @test-only fixture (import-resolved by * `ww_ww build` into a combined unit), assemble, link, run; the * synthesized entry must exit 0. * 2. BYTE-ID (rule 10) — `w6c -T` and `w6c_ww -T` must emit * byte-identical asm for the same source. The synthesized entry * rides cgfn at the gated choke point, so the @test set + the * collection-order calls are identical by construction. This is * the new entry-synth byte-id gate. * 3. REJECT — `-T` of a user-main fixture exits nonzero. * 4. GUARD — `-T` of a non-`fn() void` @test exits nonzero. */ #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 run_fixture(const char *bin) { int pid = getpid(); char src[256], comb[256], binout[256], asmf[256], obj[256], exe[256]; char rt[1024], cmd[4096]; snprintf(src, sizeof src, "/tmp/at997_%d.ww", pid); snprintf(binout, sizeof binout, "/tmp/at997_%d", pid); snprintf(comb, sizeof comb, "/tmp/at997_%d.combined.ww", pid); snprintf(asmf, sizeof asmf, "/tmp/at997_%d.s", pid); snprintf(obj, sizeof obj, "/tmp/at997_%d.o", pid); snprintf(exe, sizeof exe, "/tmp/at997_%d.exe", pid); snprintf(rt, sizeof rt, "%s/../lib/libwwrt.a", bin); snprintf(cmd, sizeof cmd, "cp test/wcc/data/attest_pass.ww %s", src); if (runwait(cmd) != 0) { fprintf(stderr, "997 FAIL: cp\n"); return 1; } /* `ww_ww test -c -o ` (compile-only) resolves imports AND auto- * bundles lib/test (#17) into .combined.ww — the synth's run() * callee must link below — WITHOUT running the harness. We gate on the * combined ARTIFACT existing; `-o` keeps the binary off the CWD. */ snprintf(cmd, sizeof cmd, "%s/ww_ww test -c -o %s %s > /dev/null 2>&1", bin, binout, src); runwait(cmd); if (access(comb, 0) != 0) { fprintf(stderr, "997 FAIL: ww_ww build produced no %s\n", comb); return 1; } snprintf(cmd, sizeof cmd, "%s/w6c_ww -T %s -o %s 2>/dev/null", bin, comb, asmf); if (runwait(cmd) != 0) { fprintf(stderr, "997 FAIL: w6c_ww -T errored\n"); return 1; } snprintf(cmd, sizeof cmd, "%s/w6a_ww -o %s %s 2>/dev/null", bin, obj, asmf); if (runwait(cmd) != 0) { fprintf(stderr, "997 FAIL: w6a_ww\n"); return 1; } snprintf(cmd, sizeof cmd, "%s/w6l_ww -o %s %s %s 2>/dev/null", bin, exe, obj, rt); if (runwait(cmd) != 0) { fprintf(stderr, "997 FAIL: w6l_ww\n"); return 1; } int rc = runwait(exe); if (rc != 0) { fprintf(stderr, "997 FAIL: synth entry exited %d (a @test failed)\n", rc); return 1; } unlink(src); unlink(comb); unlink(asmf); unlink(obj); unlink(exe); unlink(binout); char tmp[300]; snprintf(tmp, sizeof tmp, "%s.s", binout); unlink(tmp); snprintf(tmp, sizeof tmp, "%s.o", binout); unlink(tmp); return 0; } /* byteid — `w6c -T` and `w6c_ww -T` on the same unit must agree. The #17 * synth's run() callee only resolves against the auto-bundled lib/test, so * we byte-compare the lib/test-INCLUSIVE combined (raw -T of the fixture * would loud-reject the undefined run); `ww test -c` writes that combined. */ static int byteid(const char *bin) { int pid = getpid(); char stem[256], comb[256], cs[256], ws[256], cmd[4096]; snprintf(stem, sizeof stem, "/tmp/at997_bid_%d", pid); snprintf(comb, sizeof comb, "%s.combined.ww", stem); snprintf(cs, sizeof cs, "/tmp/at997_bid_c_%d.s", pid); snprintf(ws, sizeof ws, "/tmp/at997_bid_w_%d.s", pid); snprintf(cmd, sizeof cmd, "%s/ww test -c -o %s test/wcc/data/attest_pass.ww > /dev/null 2>&1", bin, stem); runwait(cmd); if (access(comb, 0) != 0) { fprintf(stderr, "997 FAIL: ww test -c produced no %s\n", comb); return 1; } snprintf(cmd, sizeof cmd, "%s/w6c -T %s -o %s 2>/dev/null", bin, comb, cs); if (runwait(cmd) != 0) { fprintf(stderr, "997 FAIL: w6c -T (byteid)\n"); return 1; } snprintf(cmd, sizeof cmd, "%s/w6c_ww -T %s -o %s 2>/dev/null", bin, comb, ws); if (runwait(cmd) != 0) { fprintf(stderr, "997 FAIL: w6c_ww -T (byteid)\n"); return 1; } char *bc = NULL, *bw = NULL; size_t nc = 0, nw = 0; int rc = 0; if (slurp(cs, &bc, &nc) < 0 || slurp(ws, &bw, &nw) < 0) { fprintf(stderr, "997 FAIL: slurp byteid asm\n"); rc = 1; } else if (nc != nw || memcmp(bc, bw, nc) != 0) { fprintf(stderr, "997 FAIL: -T asm differs (cs %zu, ww %zu)\n", nc, nw); rc = 1; } free(bc); free(bw); unlink(comb); unlink(cs); unlink(ws); char tmp[300]; snprintf(tmp, sizeof tmp, "%s.s", stem); unlink(tmp); snprintf(tmp, sizeof tmp, "%s.o", stem); unlink(tmp); unlink(stem); return rc; } static int reject(const char *bin, const char *fixture, const char *what) { char cmd[4096]; snprintf(cmd, sizeof cmd, "%s/w6c_ww -T %s -o /dev/null 2>/dev/null", bin, fixture); if (runwait(cmd) == 0) { fprintf(stderr, "997 FAIL: w6c_ww -T accepted %s (expected reject)\n", what); return 1; } return 0; } /* Each row: a fixture `w6c_ww -T` must loud-reject, and why. */ static const struct { const char *fixture; const char *what; } rejects[] = { { "test/wcc/data/attest_userman.ww", "user main" }, { "test/wcc/data/attest_badsig.ww", "non-void @test" }, }; /* #6: TEXT-def present/absent rows for attest_nondrop.ww (mirror 910). */ static const struct { const char *sym; int testmode; /* 1 => -T, 0 => plain build */ int present; const char *what; } nondrop_rows[] = { { "data.nondrop_keep", 0, 1, "plain fn kept non-T" }, { "data.nondrop_test_a", 0, 0, "head @test dropped non-T" }, { "data.nondrop_test_b", 0, 0, "mid @test dropped non-T" }, { "data.nondrop_test_c", 0, 0, "consecutive @test dropped non-T" }, { "data.nondrop_keep", 1, 1, "plain fn kept under -T" }, { "data.nondrop_test_a", 1, 1, "head @test kept under -T" }, { "data.nondrop_test_b", 1, 1, "mid @test kept under -T" }, { "data.nondrop_test_c", 1, 1, "consecutive @test kept under -T" }, }; /* nondrop — drive the #6 splice through w6c_ww (rows) AND assert the * non-T asm is cs/ww byte-identical (rule 10: the drop must be symmetric, * not just cstage-side — 997's byteid above only covered -T mode). */ static int nondrop(const char *bin) { int pid = getpid(); char wp[256], wt[256], cp[256], stem[256], comb[300], cmd[4096]; snprintf(wp, sizeof wp, "/tmp/at997nd_wp_%d.s", pid); snprintf(wt, sizeof wt, "/tmp/at997nd_wt_%d.s", pid); snprintf(cp, sizeof cp, "/tmp/at997nd_cp_%d.s", pid); snprintf(stem, sizeof stem, "/tmp/at997nd_c_%d", pid); snprintf(comb, sizeof comb, "%s.combined.ww", stem); snprintf(cmd, sizeof cmd, "%s/w6c_ww test/wcc/data/attest_nondrop.ww -o %s 2>/dev/null", bin, wp); if (runwait(cmd) != 0) { fprintf(stderr, "997 FAIL: w6c_ww non-T nondrop compile\n"); return 1; } /* the -T compile synthesizes a main calling lib/test's run(), so it * needs the lib/test-inclusive combined `ww test -c` writes. */ snprintf(cmd, sizeof cmd, "%s/ww test -c -o %s test/wcc/data/attest_nondrop.ww > /dev/null 2>&1", bin, stem); runwait(cmd); snprintf(cmd, sizeof cmd, "%s/w6c_ww -T %s -o %s 2>/dev/null", bin, comb, wt); if (runwait(cmd) != 0) { fprintf(stderr, "997 FAIL: w6c_ww -T nondrop compile\n"); return 1; } snprintf(cmd, sizeof cmd, "%s/w6c test/wcc/data/attest_nondrop.ww -o %s 2>/dev/null", bin, cp); if (runwait(cmd) != 0) { fprintf(stderr, "997 FAIL: w6c non-T nondrop compile\n"); return 1; } int rc = 0; for (size_t i = 0; i < sizeof nondrop_rows / sizeof nondrop_rows[0]; i++) { const char *f = nondrop_rows[i].testmode ? wt : wp; snprintf(cmd, sizeof cmd, "grep -q '^TEXT %s,' %s", nondrop_rows[i].sym, f); int found = runwait(cmd) == 0; if (found != nondrop_rows[i].present) { fprintf(stderr, "997 FAIL: w6c_ww — %s: %s %s (expected %s)\n", nondrop_rows[i].what, nondrop_rows[i].sym, found ? "present" : "absent", nondrop_rows[i].present ? "present" : "absent"); rc = 1; break; } } if (rc == 0) { char *bc = NULL, *bw = NULL; size_t nc = 0, nw = 0; if (slurp(cp, &bc, &nc) < 0 || slurp(wp, &bw, &nw) < 0) { fprintf(stderr, "997 FAIL: slurp non-T nondrop asm\n"); rc = 1; } else if (nc != nw || memcmp(bc, bw, nc) != 0) { fprintf(stderr, "997 FAIL: non-T @test-drop asm differs " "(cs %zu, ww %zu)\n", nc, nw); rc = 1; } free(bc); free(bw); } unlink(wp); unlink(wt); unlink(cp); unlink(comb); char tmp[300]; snprintf(tmp, sizeof tmp, "%s.s", stem); unlink(tmp); snprintf(tmp, sizeof tmp, "%s.o", stem); unlink(tmp); unlink(stem); return rc; } /* reject_plain — `w6c_ww ` (NON-T) must exit nonzero. Pins (as * 910 does for cstage) that wwstage type-checks a @test body before the * #6 splice drops it: an undefined symbol is caught loud even though the * fn never reaches codegen (checked at check.ww pass 2/3, dropped after). */ static int reject_plain(const char *bin, const char *fixture, const char *what) { char cmd[4096]; snprintf(cmd, sizeof cmd, "%s/w6c_ww %s -o /dev/null 2>/dev/null", bin, fixture); if (runwait(cmd) == 0) { fprintf(stderr, "997 FAIL: w6c_ww (non-T) accepted %s " "(expected reject)\n", what); return 1; } return 0; } /* accept_byteid — a #23 legal-accept control: `` must COMPILE on * BOTH stages (no false dup reject) AND be cs/ww byte-identical (rule 10). * Used for the cross-package same-leaf control (keying is (name, mod), so * distinct-package leaves coexist) and the builtin-redecl carve-out (the * one structural point where wwstage seeds builtins and cstage does not — * byte-id proves the drop-the-redecl mirror is exact). */ static int accept_byteid(const char *bin, const char *fixture, const char *what) { int pid = getpid(); char cs[256], ws[256], cmd[4096]; snprintf(cs, sizeof cs, "/tmp/at997ab_c_%d.s", pid); snprintf(ws, sizeof ws, "/tmp/at997ab_w_%d.s", pid); snprintf(cmd, sizeof cmd, "%s/w6c %s -o %s 2>/dev/null", bin, fixture, cs); if (runwait(cmd) != 0) { fprintf(stderr, "997 FAIL: w6c rejected %s\n", what); return 1; } snprintf(cmd, sizeof cmd, "%s/w6c_ww %s -o %s 2>/dev/null", bin, fixture, ws); if (runwait(cmd) != 0) { fprintf(stderr, "997 FAIL: w6c_ww rejected %s\n", what); return 1; } char *bc = NULL, *bw = NULL; size_t nc = 0, nw = 0; int rc = 0; if (slurp(cs, &bc, &nc) < 0 || slurp(ws, &bw, &nw) < 0) { fprintf(stderr, "997 FAIL: slurp %s asm\n", what); rc = 1; } else if (nc != nw || memcmp(bc, bw, nc) != 0) { fprintf(stderr, "997 FAIL: %s asm differs (cs %zu, ww %zu)\n", what, nc, nw); rc = 1; } free(bc); free(bw); unlink(cs); unlink(ws); return rc; } /* collide_run — a @test unit defining a user `fn run` collides with * lib/test's bound runner once `ww test -c` bundles it; `w6c_ww -T` of the * combined must loud-reject the duplicate (#23). Pre-fix wwstage built a * binary that called the user run and silently skipped every @test. */ static int collide_run(const char *bin) { int pid = getpid(); char stem[256], comb[300], cmd[4096]; snprintf(stem, sizeof stem, "/tmp/at997cr_%d", pid); snprintf(comb, sizeof comb, "%s.combined.ww", stem); snprintf(cmd, sizeof cmd, "%s/ww test -c -o %s test/wcc/data/attest_userrun.ww > /dev/null 2>&1", bin, stem); runwait(cmd); if (access(comb, 0) != 0) { fprintf(stderr, "997 FAIL: ww build produced no %s\n", comb); return 1; } snprintf(cmd, sizeof cmd, "%s/w6c_ww -T %s -o /dev/null 2>/dev/null", bin, comb); int rc = 0; if (runwait(cmd) == 0) { fprintf(stderr, "997 FAIL: w6c_ww -T accepted user `fn run` collision " "(expected duplicate-fn reject)\n"); rc = 1; } unlink(comb); char tmp[320]; snprintf(tmp, sizeof tmp, "%s.s", stem); unlink(tmp); snprintf(tmp, sizeof tmp, "%s.o", stem); unlink(tmp); unlink(stem); return rc; } /* modfn_run — `ww_ww run ` and assert exit 6 (#30): the wwstage * runtime twin of 910's modfn_run. The fixture's main returns `aa() + * aa.helper()` = 1 + 5; the bare fn must resolve to 1 and the qualified * module member to 5, so a mis-resolution that still compiled runs to a * different code. Pins the resolved TARGETS on the wwstage driver. */ static int modfn_run(const char *bin, const char *fixture, const char *what) { char cmd[4096]; snprintf(cmd, sizeof cmd, "%s/ww_ww run %s > /dev/null 2>&1", bin, fixture); int rc = runwait(cmd); if (rc != 6) { fprintf(stderr, "997 FAIL: ww_ww run %s exited %d (expected 6 — bare " "fn + qualified module resolution)\n", what, rc); return 1; } return 0; } int main(void) { const char *bin = absbin(); if (!bin) { fprintf(stderr, "997 FAIL: getcwd\n"); return 1; } if (run_fixture(bin) != 0) return 1; if (byteid(bin) != 0) return 1; for (size_t i = 0; i < sizeof rejects / sizeof rejects[0]; i++) if (reject(bin, rejects[i].fixture, rejects[i].what) != 0) return 1; if (nondrop(bin) != 0) return 1; if (reject_plain(bin, "test/wcc/data/attest_undefbody.ww", "undefined symbol in @test body") != 0) return 1; /* #23 — wwstage top-level duplicate-decl reject (one row per kind: * all four route through installtop) + legal cross-package control * (byte-id) + the builtin-redecl carve-out (byte-id) + the -T * `fn run` collision parity row. */ if (reject_plain(bin, "test/wcc/data/dup_fn.ww", "same-module duplicate fn") != 0) return 1; if (reject_plain(bin, "test/wcc/data/dup_type.ww", "same-module duplicate type") != 0) return 1; if (reject_plain(bin, "test/wcc/data/dup_def.ww", "same-module duplicate def") != 0) return 1; if (reject_plain(bin, "test/wcc/data/dup_let.ww", "same-module duplicate let") != 0) return 1; if (accept_byteid(bin, "test/wcc/data/dup_xpkg_ok.ww", "cross-package same-name") != 0) return 1; if (accept_byteid(bin, "test/wcc/data/builtin_redecl_ok.ww", "builtin nomem redecl (carve-out)") != 0) return 1; /* #30 — a top-level fn whose leaf matches an imported module name * coexists with the module bareword. wwstage now PROMOTES the same-leaf * collision in place (use_alias=1), mirroring cstage exactly, so byte-id * proves the resolved call targets match. Both decl orders are pinned: * use-before-value promotes via installtop's value-arm, value-before-use * via installdecl's N_USE arm — both reach the identical single-sym end * state, so cstage and wwstage agree on every source order. The bare * `aa()` and qualified `aa.helper()` both resolve, else the compile * fails. */ if (accept_byteid(bin, "test/wcc/data/modfn_coexist_ok.ww", "fn named like imported module (use-before-value)") != 0) return 1; if (accept_byteid(bin, "test/wcc/data/modfn_coexist_vbu_ok.ww", "fn named like imported module (value-before-use)") != 0) return 1; /* runtime-resolve: both orders run to exit 6 on the wwstage driver, * pinning that the promoted sym resolves bare fn=1 + module member=5. */ if (modfn_run(bin, "test/wcc/data/modfn_coexist_ok.ww", "use-before-value") != 0) return 1; if (modfn_run(bin, "test/wcc/data/modfn_coexist_vbu_ok.ww", "value-before-use") != 0) return 1; if (collide_run(bin) != 0) return 1; printf("@test -T (ww_ww): run ok + cs/ww byte-id + rejects + " "non-T @test drop cs/ww byte-id + checked-body (#6) + " "dup fn/type/def/let reject + xpkg/builtin-redecl byte-id + " "modfn coexist byte-id (#30) + fn-run collision (#23)\n"); return 0; }