The -T harness synthesized `use test;` after name-binding, so the lib/test runner run keyed the bare scope and collided with a user-defined bare fn run — a spurious "duplicate fn run" reject (the E1 tolerance seam). Prepending the synth use before binding keys the runner as test.run in the test module namespace, distinct from the user bare run; the two coexist. Hare-faithful: the runner is its own test module (ref/hare/test/+test.ha:97). Inverts attest_userrun.ww from the #23-mandated reject to a coexist fixture; gate asserts exactly 1 TEXT run + 1 TEXT test.run on the -T asm (distinct symbols, not a dead-dup). Closes #80.
524 lines
19 KiB
C
524 lines
19 KiB
C
/*
|
|
* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/wait.h>
|
|
|
|
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 <stem>` (compile-only) resolves imports AND auto-
|
|
* bundles lib/test (#17) into <stem>.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 <fixture>` (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: `<fixture>` 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;
|
|
}
|
|
|
|
/* count_text — number of `TEXT <sym>,` directive lines in an asm file
|
|
* (Plan-9 `TEXT name,$frame`), -1 on open failure. A COUNT (not presence)
|
|
* is what distinguishes the #84 dead-dup: the bug yields 0x `TEXT run` +
|
|
* 2x `TEXT test.run`, which a mere presence check would miss. */
|
|
static int
|
|
count_text(const char *path, const char *sym)
|
|
{
|
|
char want[128];
|
|
snprintf(want, sizeof want, "TEXT %s,", sym);
|
|
size_t wlen = strlen(want);
|
|
FILE *f = fopen(path, "r");
|
|
if (f == NULL) return -1;
|
|
char line[8192];
|
|
int n = 0;
|
|
while (fgets(line, sizeof line, f) != NULL)
|
|
if (strncmp(line, want, wlen) == 0) n++;
|
|
fclose(f);
|
|
return n;
|
|
}
|
|
|
|
/* coexist_run — a @test unit that ALSO defines a user `fn run` must
|
|
* COEXIST with lib/test's bound runner, not collide. Post-#80 the synth
|
|
* `use test;` is prepended before binding, so lib/test's `run` keys under
|
|
* "test" (not "") and no longer duplicate-collides with the user's bare
|
|
* `run` (which mangles bare via #84). `w6c_ww -T` of the `ww_ww test -c`
|
|
* combined now ACCEPTS; the -T asm carries EXACTLY 1 `TEXT run` (user,
|
|
* bare) + 1 `TEXT test.run` (lib runner) and is byte-identical to w6c's
|
|
* (cs==ww) — the static-label proof that the user run is distinct in the
|
|
* real @test/-T path where #84 lives; then, assembled, linked and run, the
|
|
* synth entry exits 0 (the user `run` did not hijack the runner and the
|
|
* @test passes). Pre-#80 this loud-rejected "duplicate fn run". */
|
|
static int
|
|
coexist_run(const char *bin)
|
|
{
|
|
int pid = getpid();
|
|
char stem[256], comb[300], asmf[320], csasm[320], obj[320], exe[320];
|
|
char rt[1024], cmd[4096];
|
|
snprintf(stem, sizeof stem, "/tmp/at997cr_%d", pid);
|
|
snprintf(comb, sizeof comb, "%s.combined.ww", stem);
|
|
snprintf(asmf, sizeof asmf, "%s.run.s", stem);
|
|
snprintf(csasm, sizeof csasm, "%s.cs.s", stem);
|
|
snprintf(obj, sizeof obj, "%s.run.o", stem);
|
|
snprintf(exe, sizeof exe, "%s.exe", stem);
|
|
snprintf(rt, sizeof rt, "%s/../lib/libwwrt.a", bin);
|
|
snprintf(cmd, sizeof cmd,
|
|
"%s/ww_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_ww build produced no %s\n", comb);
|
|
return 1;
|
|
}
|
|
int rc = 0;
|
|
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 rejected user `fn run` coexist "
|
|
"(expected accept post-#80)\n");
|
|
rc = 1;
|
|
}
|
|
if (rc == 0) {
|
|
int nr = count_text(asmf, "run");
|
|
int nt = count_text(asmf, "test.run");
|
|
if (nr != 1 || nt != 1) {
|
|
fprintf(stderr, "997 FAIL: w6c_ww -T asm label count run=%d "
|
|
"test.run=%d (want 1/1) — user `fn run` not distinct from "
|
|
"lib runner (a #84 dead-dup gives run=0/test.run=2)\n",
|
|
nr, nt);
|
|
rc = 1;
|
|
}
|
|
}
|
|
if (rc == 0) {
|
|
snprintf(cmd, sizeof cmd, "%s/w6c -T %s -o %s 2>/dev/null",
|
|
bin, comb, csasm);
|
|
if (runwait(cmd) != 0) {
|
|
fprintf(stderr, "997 FAIL: w6c -T (coexist cs==ww)\n");
|
|
rc = 1;
|
|
}
|
|
}
|
|
if (rc == 0) {
|
|
char *bc = NULL, *bw = NULL;
|
|
size_t nc = 0, nw = 0;
|
|
if (slurp(csasm, &bc, &nc) < 0 || slurp(asmf, &bw, &nw) < 0) {
|
|
fprintf(stderr, "997 FAIL: slurp coexist asm\n");
|
|
rc = 1;
|
|
} else if (nc != nw || memcmp(bc, bw, nc) != 0) {
|
|
fprintf(stderr, "997 FAIL: coexist -T asm cs!=ww "
|
|
"(cs %zu, ww %zu)\n", nc, nw);
|
|
rc = 1;
|
|
}
|
|
free(bc); free(bw);
|
|
}
|
|
if (rc == 0) {
|
|
snprintf(cmd, sizeof cmd, "%s/w6a_ww -o %s %s 2>/dev/null", bin, obj, asmf);
|
|
if (runwait(cmd) != 0) { fprintf(stderr, "997 FAIL: coexist w6a_ww\n"); rc = 1; }
|
|
}
|
|
if (rc == 0) {
|
|
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: coexist w6l_ww\n"); rc = 1; }
|
|
}
|
|
if (rc == 0 && runwait(exe) != 0) {
|
|
fprintf(stderr, "997 FAIL: coexist synth entry nonzero — user "
|
|
"`fn run` hijacked the runner or the @test failed\n");
|
|
rc = 1;
|
|
}
|
|
unlink(comb); unlink(asmf); unlink(csasm); unlink(obj); unlink(exe);
|
|
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 <fixture>` 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 (coexist_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 coexist (#80)\n");
|
|
return 0;
|
|
}
|