Files
ww/test/wcc/997_at_test_ww.c
Hojun-Cho 08a76cf4c8 wcc: drop @test fns from non-T builds, both stages (harec check.c:3941)
Splice @test N_FNDECLs out of the unit after the body-check passes,
mirroring harec's checked-but-not-emitted: a broken @test body still
errors loudly in non-T; @test-free units are emission-unchanged.
910/997 table rows pin keep/test x non-T/-T, head+consecutive unlink,
undef-body reject, and plain-calls-dropped loud link-fail. w6c+wwdump
combined.ww regen. (#6-team)
2026-06-10 22:20:15 +09:00

276 lines
9.0 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], asmf[256], obj[256], exe[256];
char rt[1024], cmd[4096];
snprintf(src, sizeof src, "/tmp/at997_%d.ww", 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 build resolves `import rt` into the combined unit; it exits
* nonzero (no main — that is what -T synthesizes), so we gate on the
* combined ARTIFACT existing, not the exit code. */
snprintf(cmd, sizeof cmd, "%s/ww_ww build %s > /dev/null 2>&1", bin, 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);
return 0;
}
/* byteid — `w6c -T` and `w6c_ww -T` on the same source must agree. */
static int
byteid(const char *bin)
{
int pid = getpid();
char cs[256], ws[256], cmd[4096];
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/w6c -T test/wcc/data/attest_pass.ww -o %s 2>/dev/null",
bin, cs);
if (runwait(cmd) != 0) { fprintf(stderr, "997 FAIL: w6c -T (byteid)\n"); return 1; }
snprintf(cmd, sizeof cmd, "%s/w6c_ww -T test/wcc/data/attest_pass.ww -o %s 2>/dev/null",
bin, 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(cs); unlink(ws);
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], 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(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;
}
snprintf(cmd, sizeof cmd,
"%s/w6c_ww -T test/wcc/data/attest_nondrop.ww -o %s 2>/dev/null", bin, 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);
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;
}
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;
printf("@test -T (ww_ww): run ok + cs/ww byte-id + rejects + "
"non-T @test drop cs/ww byte-id + checked-body (#6)\n");
return 0;
}