test/wcc: carrier ownership repair and driver-contract adaptation

Every surviving carrier now owns its artifacts: checked mkdir/mkdtemp/
fopen acquisition, one all-exit cleanup funnel per carrier, ENOENT-
tolerant checked unlinks, exact-path deletion (rm -rf only for an
owned pid-keyed dir or a .sepwork beneath one), and cleanup failure
fails a passing carrier without overwriting its diagnostic. In the
same pass the carriers adapt to the driver contract this branch lands:
--sep and WW_PKGCACHE are gone, -S and the /tmp/ww_run_<pid> scratch
contract are asserted, and rows whose runtime or reject coverage moved
to test/wcc/data fixtures or test/lang @test owners are trimmed to the
byte/artifact/diagnostic observations only they can make.

Repair and adaptation ride together because most files interleave both
in the same hunks; splitting would manufacture intermediate carrier
states that never existed and cannot run against either driver.
This commit is contained in:
2026-08-07 23:02:47 +09:00
parent b2899dd8d3
commit a95a7a316b
132 changed files with 7573 additions and 6172 deletions

View File

@@ -17,6 +17,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -37,31 +38,45 @@ runwait(const char *cmd)
static int
run_roundtrip(const char *driver, int id)
{
char dir[80], wrdir[112], wrp[176], mainp[160], cmd[1024], outbin[160];
char dir[80], wrdir[112], wrp[176], mainp[160], cmd[1024];
char outbin[160], scratch[176];
snprintf(dir, sizeof dir, "/tmp/wwue_rt_%d_%d", getpid(), id);
snprintf(wrdir, sizeof wrdir, "%s/wr", dir);
mkdir(dir, 0755);
mkdir(wrdir, 0755);
snprintf(wrp, sizeof wrp, "%s/wr.ww", wrdir);
snprintf(mainp, sizeof mainp, "%s/main.ww", dir);
snprintf(outbin, sizeof outbin, "%s/main", dir);
snprintf(scratch, sizeof scratch, "%s/main.sepwork", dir);
if (mkdir(dir, 0755) != 0) {
perror(dir);
return -1;
}
int got = -1;
int wr_owned = 0;
if (mkdir(wrdir, 0755) != 0) {
perror(wrdir);
goto cleanup;
}
wr_owned = 1;
FILE *f = fopen(wrp, "wb");
if (!f) {
snprintf(cmd, sizeof cmd, "rm -rf %s", dir);
(void)system(cmd);
return -1;
perror(wrp);
goto cleanup;
}
/* one def per serializer branch: \x (<=0xFF), \u (<=0xFFFF), \U. */
fputs("package wr;\n"
"export def EACUTE: rune = '\\u00e9';\n"
"export def EURO: rune = '\\u20ac';\n"
"export def SMILEY: rune = '\\U0001F600';\n", f);
fclose(f);
if (fclose(f) != 0) {
perror(wrp);
goto cleanup;
}
f = fopen(mainp, "wb");
if (!f) {
snprintf(cmd, sizeof cmd, "rm -rf %s", dir);
(void)system(cmd);
return -1;
perror(mainp);
goto cleanup;
}
fputs("package main;\n"
"import wr;\n"
@@ -74,19 +89,46 @@ run_roundtrip(const char *driver, int id)
" if ((c: u32) != 128512u32) { return 3; };\n"
" return 42;\n"
"};\n", f);
fclose(f);
if (fclose(f) != 0) {
perror(mainp);
goto cleanup;
}
snprintf(cmd, sizeof cmd, "cd %s && %s build main.ww", dir, driver);
if (runwait(cmd) != 0) {
fprintf(stderr, "roundtrip: build via %s failed\n", driver);
snprintf(cmd, sizeof cmd, "rm -rf %s", dir);
(void)system(cmd);
return -1;
goto cleanup;
}
snprintf(outbin, sizeof outbin, "%s/main", dir);
int got = runwait(outbin);
snprintf(cmd, sizeof cmd, "rm -rf %s", dir);
(void)system(cmd);
got = runwait(outbin);
cleanup:
/* `ww build` retains this exact caller-owned scratch tree. */
snprintf(cmd, sizeof cmd, "rm -rf %s", scratch);
int cleanfail = runwait(cmd) != 0;
if (unlink(outbin) != 0 && errno != ENOENT) {
perror(outbin);
cleanfail = 1;
}
if (unlink(mainp) != 0 && errno != ENOENT) {
perror(mainp);
cleanfail = 1;
}
if (wr_owned) {
if (unlink(wrp) != 0 && errno != ENOENT) {
perror(wrp);
cleanfail = 1;
}
if (rmdir(wrdir) != 0) {
perror(wrdir);
cleanfail = 1;
}
}
if (rmdir(dir) != 0) {
perror(dir);
cleanfail = 1;
}
if (cleanfail && got == 42)
got = -1;
return got;
}

View File

@@ -32,30 +32,37 @@ main(void)
char dir[64];
snprintf(dir, sizeof dir, "/tmp/wwarch_%d", getpid());
mkdir(dir, 0755);
if (mkdir(dir, 0755) != 0) {
perror(dir);
return 1;
}
int result = 1;
/* lib_good.ww — defines f1() (→ main.f1 under strict-package #24a) */
char path[256];
snprintf(path, sizeof path, "%s/lib_good.ww", dir);
FILE *f = fopen(path, "wb");
if (!f) { perror(path); goto cleanup; }
wwtest_fputs("export fn f1() i32 = { return 7; };", f);
fclose(f);
if (fclose(f) != 0) { perror(path); goto cleanup; }
/* lib_bad.ww — defines f2() but also references an undefined extern */
snprintf(path, sizeof path, "%s/lib_bad.ww", dir);
f = fopen(path, "wb");
if (!f) { perror(path); goto cleanup; }
wwtest_fputs("@symbol(\"this_symbol_does_not_exist\") fn bogus() i32;\n"
"export fn f2() i32 = { return bogus(); };", f);
fclose(f);
if (fclose(f) != 0) { perror(path); goto cleanup; }
/* main.ww — calls f1, NOT f2. The plain prototype declares f1 in the
* same (main) module, so the call resolves to main.f1 — matching
* lib_good's moduled definition (no @symbol bare-pin needed). */
snprintf(path, sizeof path, "%s/m.ww", dir);
f = fopen(path, "wb");
if (!f) { perror(path); goto cleanup; }
wwtest_fputs("fn f1() i32;\n"
"fn main() i32 = { return f1(); };", f);
fclose(f);
if (fclose(f) != 0) { perror(path); goto cleanup; }
char cmd[2048];
snprintf(cmd, sizeof cmd,
@@ -65,7 +72,10 @@ main(void)
"ar rcs libfoo.a lib_good.o lib_bad.o && "
"%s/w6c -o m.s m.ww && %s/w6a -o m.o m.s",
dir, bin, bin, bin, bin, bin, bin);
if (run(cmd) != 0) { fprintf(stderr, "build failed\n"); return 1; }
if (run(cmd) != 0) {
fprintf(stderr, "build failed\n");
goto cleanup;
}
/* Find start.o for the runtime */
char startobj[256];
@@ -75,8 +85,6 @@ main(void)
"%s/w6l -o %s/m %s/m.o %s %s/libfoo.a 2>%s/link.err",
bin, dir, dir, startobj, dir, dir);
if (run(cmd) != 0) {
FILE *ef = fopen("/dev/null", "r");
(void)ef;
char errpath[300];
snprintf(errpath, sizeof errpath, "%s/link.err", dir);
FILE *e = fopen(errpath, "r");
@@ -84,16 +92,36 @@ main(void)
char buf[512]; size_t r = fread(buf, 1, sizeof buf - 1, e); buf[r]='\0'; fclose(e);
fprintf(stderr, "link failed:\n%s\n", buf);
}
return 1;
goto cleanup;
}
char exepath[256];
snprintf(exepath, sizeof exepath, "%s/m", dir);
int rc = run(exepath);
if (WIFEXITED(rc) && WEXITSTATUS(rc) == 7) {
printf("arch: ok (only needed archive member pulled)\n");
return 0;
result = 0;
} else {
fprintf(stderr, "arch: unexpected exit\n");
}
fprintf(stderr, "arch: unexpected exit\n");
return 1;
cleanup:
snprintf(path, sizeof path, "%s/lib_good.ww", dir); unlink(path);
snprintf(path, sizeof path, "%s/lib_bad.ww", dir); unlink(path);
snprintf(path, sizeof path, "%s/m.ww", dir); unlink(path);
snprintf(path, sizeof path, "%s/lib_good.s", dir); unlink(path);
snprintf(path, sizeof path, "%s/lib_bad.s", dir); unlink(path);
snprintf(path, sizeof path, "%s/lib_good.o", dir); unlink(path);
snprintf(path, sizeof path, "%s/lib_bad.o", dir); unlink(path);
snprintf(path, sizeof path, "%s/libfoo.a", dir); unlink(path);
snprintf(path, sizeof path, "%s/m.s", dir); unlink(path);
snprintf(path, sizeof path, "%s/m.o", dir); unlink(path);
snprintf(path, sizeof path, "%s/m", dir); unlink(path);
snprintf(path, sizeof path, "%s/link.err", dir); unlink(path);
if (rmdir(dir) != 0) {
perror(dir);
result = 1;
}
if (result == 0)
printf("arch: ok (only needed archive member pulled)\n");
return result;
}

View File

@@ -33,14 +33,14 @@
* - unary-plus-i32: `def X: i32 = +5;` — TK_PLUS noop, completes
* the unary whitelist coverage.
*
* Per-fixture: compile+link+run via the `ww` driver (cstage) and
* via `ww_ww` (wwstage) if it exists. Asm byte-identity diff between
* cstage's w6c and wwstage's w6c_ww closes the symmetry corner.
* Runtime ownership moved to the r56_def_* runww fixtures. This wrapper
* retains only the source-to-assembly byte-identity comparison between
* cstage's w6c and wwstage's w6c_ww.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include "wwtestpkg.h"
@@ -53,7 +53,7 @@ runwait(const char *cmd)
return -1;
}
struct row { const char *label; const char *src; int want; };
struct row { const char *label; const char *src; };
static const struct row rows[] = {
/* The headline #24 case: N_UN(TK_MINUS, N_INTLIT) rhs.
@@ -65,8 +65,7 @@ static const struct row rows[] = {
"\tlet x: i32 = NEG;\n"
"\tif (x == -100) { return 42; };\n"
"\treturn 1;\n"
"};\n",
42 },
"};\n" },
/* Regression check: an unwrapped N_INTLIT rhs must still
* emit a DATA row. If the fold-gate rewrite accidentally
@@ -77,8 +76,7 @@ static const struct row rows[] = {
"\tlet x: i32 = POS;\n"
"\tif (x == 100) { return 42; };\n"
"\treturn 1;\n"
"};\n",
42 },
"};\n" },
/* Unary tilde over N_INTLIT — the other arithmetic op in
* the unary whitelist. ~0 is -1 in i32 two's complement;
@@ -90,8 +88,7 @@ static const struct row rows[] = {
"\tlet x: i32 = NTIL;\n"
"\tif (x == -1) { return 42; };\n"
"\treturn 1;\n"
"};\n",
42 },
"};\n" },
/* 8-byte slot via the same fold path. Catches any width-
* specific bug in the DATA-row emit (DATA always writes
@@ -103,8 +100,7 @@ static const struct row rows[] = {
"\tlet x: i64 = NEG;\n"
"\tif (x == -1234567890i64) { return 42; };\n"
"\treturn 1;\n"
"};\n",
42 },
"};\n" },
/* Unsigned (u32) slot. `~0u32` is all-ones; reading it
* back through a u32 local and comparing against the
@@ -116,8 +112,7 @@ static const struct row rows[] = {
"\tlet x: u32 = UMAX;\n"
"\tif (x == 4294967295u32) { return 42; };\n"
"\treturn 1;\n"
"};\n",
42 },
"};\n" },
/* Unary plus — TK_PLUS noop in the fold. Completes the
* unary whitelist coverage. */
@@ -127,40 +122,9 @@ static const struct row rows[] = {
"\tlet x: i32 = P;\n"
"\tif (x == 5) { return 42; };\n"
"\treturn 1;\n"
"};\n",
42 },
"};\n" },
};
static int
run_driver(const char *driver, const struct row *r, int i)
{
char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/dng_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/dng_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/dng_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
wwtest_fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
runwait(rmcmd);
return -1;
}
int got = runwait(outbin);
runwait(rmcmd);
return got;
}
/* asm_byte_identical — generate .s via cstage's w6c and wwstage's
* w6c_ww and diff. Pins the symmetric-emit contract: if either
* stage's fold helper drifts (e.g. one accepts N_NIL the other
@@ -178,23 +142,22 @@ asm_byte_identical(const char *bin, const struct row *r, int i)
wwtest_fputs(r->src, f);
fclose(f);
int rc = -1;
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
unlink(src);
return -1;
goto cleanup;
}
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
bin, ws, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
unlink(src); unlink(cs);
return -1;
goto cleanup;
}
FILE *fc = fopen(cs, "rb");
FILE *fw = fopen(ws, "rb");
int rc = 0;
rc = 0;
if (!fc || !fw) {
rc = -1;
} else {
@@ -210,7 +173,25 @@ asm_byte_identical(const char *bin, const struct row *r, int i)
if (rc != 0)
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
r->label);
unlink(src); unlink(cs); unlink(ws);
cleanup:;
/* a failing compile can still leave a partial .s — ENOENT is the
* only tolerable unlink error on the never-created legs. */
int cleanfail = 0;
if (unlink(src) != 0 && errno != ENOENT) {
perror(src);
cleanfail = 1;
}
if (unlink(cs) != 0 && errno != ENOENT) {
perror(cs);
cleanfail = 1;
}
if (unlink(ws) != 0 && errno != ENOENT) {
perror(ws);
cleanfail = 1;
}
if (cleanfail && rc == 0)
rc = -1;
return rc;
}
@@ -227,41 +208,14 @@ main(void)
bin = absbin;
}
char cdrv[1024];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
/* gate on the binary the rows invoke (w6c_ww), not the ww_ww
* driver — probing the wrong one skips or fails all rows. */
char wdrv[1024];
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
struct { const char *name; const char *path; int gated_on_existence; }
drivers[] = {
{ "cstage", cdrv, 0 },
{ "wwstage", wdrv, 1 },
{ NULL, NULL, 0 },
};
snprintf(wdrv, sizeof wdrv, "%s/w6c_ww", bin);
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
for (int d = 0; drivers[d].name; d++) {
if (drivers[d].gated_on_existence
&& access(drivers[d].path, X_OK) != 0) {
fprintf(stderr, "def_neg_global: skip %s (no %s)\n",
drivers[d].name, drivers[d].path);
continue;
}
for (int i = 0; i < n; i++) {
int got = run_driver(drivers[d].path, &rows[i], i);
total++;
if (got != rows[i].want) {
fprintf(stderr,
"def_neg_global[%s][%s]: exit=%d want=%d\n",
drivers[d].name, rows[i].label,
got, rows[i].want);
fail++;
}
}
}
/* Asm byte-identity diff, only when wwstage is built. */
if (access(wdrv, X_OK) == 0) {
for (int i = 0; i < n; i++) {

View File

@@ -16,7 +16,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include "wwtestpkg.h"
@@ -49,8 +51,8 @@ runwait(const char *cmd)
static int
linkwith(const char *lnk, const char *obj, const char *libdir, int nflags)
{
char exe[64];
snprintf(exe, sizeof exe, "/tmp/wwfc_%d_x", getpid());
char exe[128];
snprintf(exe, sizeof exe, "%s/wwfc_x", libdir);
char junk[8192];
size_t off = 0;
@@ -63,19 +65,30 @@ linkwith(const char *lnk, const char *obj, const char *libdir, int nflags)
"%s -o %s %s%s -L%s -lfoo 2>/dev/null",
lnk, exe, obj, junk, libdir);
int rc = runwait(cmd);
if (rc != 0) { unlink(exe); return rc; }
if (rc != 0) goto cleanup;
FILE *f = fopen(exe, "rb");
if (!f) return -1;
if (!f) {
rc = -1;
goto cleanup;
}
unsigned char hdr[20];
int ok = fread(hdr, 1, sizeof hdr, f) == sizeof hdr;
fclose(f);
unlink(exe);
if (!ok || memcmp(hdr, "\x7f""ELF", 4) != 0) return -1;
if (!ok || memcmp(hdr, "\x7f""ELF", 4) != 0) {
rc = -1;
goto cleanup;
}
unsigned short etype = (unsigned short)hdr[16]
| ((unsigned short)hdr[17] << 8);
if (etype != 2) return -1; /* ET_EXEC */
return 0;
if (etype != 2) rc = -1; /* ET_EXEC */
cleanup:
if (unlink(exe) != 0 && errno != ENOENT) {
perror(exe);
if (rc == 0) rc = -1;
}
return rc;
}
int
@@ -84,37 +97,44 @@ main(void)
const char *bin = absbin();
if (!bin) return 1;
char src[64], asmf[64], obj[64];
char fsrc[64], fasm[64], fobj[64];
char src[128], asmf[128], obj[128];
char fsrc[128], fasm[128], fobj[128];
char libdir[64], lib[128], cmd[1024];
int pid = getpid();
snprintf(src, sizeof src, "/tmp/wwfc_%d_m.ww", pid);
snprintf(asmf, sizeof asmf, "/tmp/wwfc_%d_m.s", pid);
snprintf(obj, sizeof obj, "/tmp/wwfc_%d_m.o", pid);
snprintf(fsrc, sizeof fsrc, "/tmp/wwfc_%d_f.ww", pid);
snprintf(fasm, sizeof fasm, "/tmp/wwfc_%d_f.s", pid);
snprintf(fobj, sizeof fobj, "/tmp/wwfc_%d_f.o", pid);
snprintf(libdir, sizeof libdir, "/tmp/wwfc_%d_lib", pid);
if (mkdir(libdir, 0755) != 0) {
perror(libdir);
return 1;
}
int result = 1;
snprintf(src, sizeof src, "%s/m.ww", libdir);
snprintf(asmf, sizeof asmf, "%s/m.s", libdir);
snprintf(obj, sizeof obj, "%s/m.o", libdir);
snprintf(fsrc, sizeof fsrc, "%s/f.ww", libdir);
snprintf(fasm, sizeof fasm, "%s/f.s", libdir);
snprintf(fobj, sizeof fobj, "%s/f.o", libdir);
snprintf(lib, sizeof lib, "%s/libfoo.a", libdir);
/* a standalone main.o (no undefs) + an unrelated archived foo.o. */
FILE *f = fopen(src, "wb");
if (!f) { perror(src); goto cleanup; }
wwtest_fputs("fn main() i32 = { return 42; };", f);
fclose(f);
if (fclose(f) != 0) { perror(src); goto cleanup; }
f = fopen(fsrc, "wb");
if (!f) { perror(fsrc); goto cleanup; }
wwtest_fputs("export fn foo() i32 = { return 7; };", f);
fclose(f);
if (fclose(f) != 0) { perror(fsrc); goto cleanup; }
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s", bin, asmf, src);
if (runwait(cmd) != 0) { fprintf(stderr, "w6c main failed\n"); return 1; }
if (runwait(cmd) != 0) { fprintf(stderr, "w6c main failed\n"); goto cleanup; }
snprintf(cmd, sizeof cmd, "%s/w6a -o %s %s", bin, obj, asmf);
if (runwait(cmd) != 0) { fprintf(stderr, "w6a main failed\n"); return 1; }
if (runwait(cmd) != 0) { fprintf(stderr, "w6a main failed\n"); goto cleanup; }
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s", bin, fasm, fsrc);
if (runwait(cmd) != 0) { fprintf(stderr, "w6c foo failed\n"); return 1; }
if (runwait(cmd) != 0) { fprintf(stderr, "w6c foo failed\n"); goto cleanup; }
snprintf(cmd, sizeof cmd, "%s/w6a -o %s %s", bin, fobj, fasm);
if (runwait(cmd) != 0) { fprintf(stderr, "w6a foo failed\n"); return 1; }
snprintf(cmd, sizeof cmd, "mkdir -p %s && ar rcs %s %s", libdir, lib, fobj);
if (runwait(cmd) != 0) { fprintf(stderr, "ar failed\n"); return 1; }
if (runwait(cmd) != 0) { fprintf(stderr, "w6a foo failed\n"); goto cleanup; }
snprintf(cmd, sizeof cmd, "ar rcs %s %s", lib, fobj);
if (runwait(cmd) != 0) { fprintf(stderr, "ar failed\n"); goto cleanup; }
int counts[] = { 1, 64, 65, 100, 128 };
int fail = 0;
@@ -138,15 +158,50 @@ main(void)
}
}
snprintf(cmd, sizeof cmd, "rm -rf %s %s %s %s %s %s %s",
src, asmf, obj, fsrc, fasm, fobj, libdir);
(void)runwait(cmd);
if (fail) {
fprintf(stderr, "w6l_manyflags: %d failure(s)\n", fail);
return 1;
goto cleanup;
}
result = 0;
cleanup:
{
int cleanfail = 0;
if (unlink(src) != 0 && errno != ENOENT) {
perror(src); cleanfail = 1;
}
if (unlink(asmf) != 0 && errno != ENOENT) {
perror(asmf); cleanfail = 1;
}
if (unlink(obj) != 0 && errno != ENOENT) {
perror(obj); cleanfail = 1;
}
if (unlink(fsrc) != 0 && errno != ENOENT) {
perror(fsrc); cleanfail = 1;
}
if (unlink(fasm) != 0 && errno != ENOENT) {
perror(fasm); cleanfail = 1;
}
if (unlink(fobj) != 0 && errno != ENOENT) {
perror(fobj); cleanfail = 1;
}
if (unlink(lib) != 0 && errno != ENOENT) {
perror(lib); cleanfail = 1;
}
char exe[128];
snprintf(exe, sizeof exe, "%s/wwfc_x", libdir);
if (unlink(exe) != 0 && errno != ENOENT) {
perror(exe);
cleanfail = 1;
}
if (rmdir(libdir) != 0) {
perror(libdir);
cleanfail = 1;
}
if (cleanfail && result == 0) result = 1;
}
if (result != 0) return result;
printf("w6l_manyflags: -L past slot 64 honoured by both stages "
"(1/64/65/100/128 flags)\n");
return 0;
return result;
}

View File

@@ -36,7 +36,7 @@
* type table, so cstage's single `size`/offset and wwstage's tinfo +
* astsize/astoffset folds emit identical asm.
*
* IDENTITY (identity_reject, CSTAGE-asserted): assigning a packed struct
* IDENTITY (identity_reject, stage-asymmetric): assigning a packed struct
* value to a structurally-identical UNPACKED twin is a type error — proves
* packed is part of type identity (harec types.c:621). cstage rejects it
* (structural type_eq honours packed). wwstage's struct-vs-struct
@@ -44,9 +44,9 @@
* (#224/#10: a bare cross-module same-leaf type name can mis-resolve, so
* wwstage cannot confidently reject ANY same-kind struct mismatch — it
* already accepts `struct{a:u8}` into `struct{x:u64,y:u64}`); the packed
* twin rides that same deferred arc. So this row asserts ONLY that cstage
* rejects; wwstage's accept is the documented #224/#10 divergence, not a
* #51 regression.
* twin rides that same deferred arc. The row explicitly requires cstage to
* reject and wwstage to accept; the latter is the documented #224/#10
* divergence, not a #51 regression.
*
* WWI ROUND-TRIP (wwi_roundtrip): a library package exports
* `type Ev = struct @packed {...}`; an importing package computes size(Ev)
@@ -58,6 +58,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -72,7 +73,7 @@ runwait(const char *cmd)
}
/* want sentinels (outside any real exit code 0..255). */
#define REJECT_CSTAGE (-2147483647 - 1) /* cstage must FAIL to build */
#define REJECT_CSTAGE (-2147483647 - 1) /* cstage fails; wwstage runs 0 */
/* The packed/unpacked twins shared by the layout rows. Each row's `expr`
* is spliced into a fn body that has `p` (packed) and `u` (unpacked) of
@@ -199,9 +200,8 @@ static const struct row rows[] = {
"\treturn (p.b: i32) + after - (p.a: i32);\n"
"};\n", 20 },
/* identity: packed twin not assignable to unpacked twin. cstage
* rejects (structural type_eq honours packed); wwstage rides the
* #224/#10 struct-leniency (see header). CSTAGE-asserted only. */
/* identity: cstage rejects the packed/unpacked assignment; wwstage
* accepts it through the documented #224/#10 leniency (see header). */
{ "identity_reject",
"package main;\n"
"type A = struct @packed { x: u8, y: u64 };\n"
@@ -211,76 +211,93 @@ static const struct row rows[] = {
"\tlet b: B = a;\n"
"\treturn 0;\n"
"};\n", REJECT_CSTAGE },
/* unknown struct attribute is a loud parse error (parse.c /
* parse.ww `@%s != packed` branch). cstage-asserted (the reject
* harness only pins cstage); wwstage errmsg rejects symmetrically. */
{ "unknown_attr_reject",
"package main;\n"
"type P = struct @foo { a: u8 };\n"
"export fn main() i32 = { return 0; };\n", REJECT_CSTAGE },
};
static int
run_driver(const char *driver, const struct row *r, int i)
{
char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024];
char tmpdir[64], src[128], outbin[128], scratch[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/pk51_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/pk51_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/pk51_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
snprintf(scratch, sizeof scratch, "%s.sepwork", outbin);
if (mkdir(tmpdir, 0755) != 0) {
perror(tmpdir);
return -2;
}
int result = -2;
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -2; }
if (!f) { perror(src); goto cleanup; }
fputs(r->src, f);
fclose(f);
if (fclose(f) != 0) { perror(src); goto cleanup; }
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, src);
int built = runwait(cmd);
if (built != 0) {
/* build failed (a reject, or a real error). Caller decides. */
runwait(rmcmd);
return -1;
result = -1;
goto cleanup;
}
int got = runwait(outbin);
result = runwait(outbin);
runwait(rmcmd);
return got;
cleanup:
/* `ww build -o` retains this exact caller-owned scratch tree. */
snprintf(cmd, sizeof cmd, "rm -rf %s", scratch);
int cleanfail = runwait(cmd) != 0;
if (unlink(outbin) != 0 && errno != ENOENT) {
perror(outbin); cleanfail = 1;
}
if (unlink(src) != 0 && errno != ENOENT) {
perror(src); cleanfail = 1;
}
if (rmdir(tmpdir) != 0) {
perror(tmpdir); cleanfail = 1;
}
if (cleanfail) {
fprintf(stderr, "row[%s]: cleanup failed after exit=%d\n",
r->label, result);
return -2;
}
return result;
}
/* asm_byte_identical — w6c (cstage) vs w6c_ww (wwstage) .s diff. */
static int
asm_byte_identical(const char *bin, const struct row *r, int i)
{
char src[64], cs[64], ws[64], cmd[1024];
snprintf(src, sizeof src, "/tmp/pk51a_%d_%d.ww", getpid(), i);
snprintf(cs, sizeof cs, "/tmp/pk51a_%d_%d_c.s", getpid(), i);
snprintf(ws, sizeof ws, "/tmp/pk51a_%d_%d_w.s", getpid(), i);
char tmpdir[64], src[128], cs[128], ws[128], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/pk51a_%d_%d", getpid(), i);
snprintf(src, sizeof src, "%s/input.ww", tmpdir);
snprintf(cs, sizeof cs, "%s/c.s", tmpdir);
snprintf(ws, sizeof ws, "%s/w.s", tmpdir);
if (mkdir(tmpdir, 0755) != 0) {
perror(tmpdir);
return -1;
}
int rc = -1;
FILE *f = fopen(src, "wb");
if (!f) return -1;
if (!f) { perror(src); goto cleanup; }
fputs(r->src, f);
fclose(f);
if (fclose(f) != 0) { perror(src); goto cleanup; }
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
unlink(src);
return -1;
goto cleanup;
}
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null", bin, ws, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
unlink(src); unlink(cs);
return -1;
goto cleanup;
}
FILE *fc = fopen(cs, "rb");
FILE *fw = fopen(ws, "rb");
int rc = 0;
rc = 0;
if (!fc || !fw) {
rc = -1;
} else {
@@ -296,7 +313,20 @@ asm_byte_identical(const char *bin, const struct row *r, int i)
if (rc != 0)
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
r->label);
unlink(src); unlink(cs); unlink(ws);
cleanup:
if (unlink(src) != 0 && errno != ENOENT) {
perror(src); rc = -1;
}
if (unlink(cs) != 0 && errno != ENOENT) {
perror(cs); rc = -1;
}
if (unlink(ws) != 0 && errno != ENOENT) {
perror(ws); rc = -1;
}
if (rmdir(tmpdir) != 0) {
perror(tmpdir); rc = -1;
}
return rc;
}
@@ -308,20 +338,32 @@ asm_byte_identical(const char *bin, const struct row *r, int i)
static int
wwi_roundtrip(const char *driver, int idx)
{
char root[80], libdir[128], appww[160], cmd[1024], outbin[200], rm[200];
char root[80], libroot[112], libdir[128], appdir[112];
char pk2[160], appww[160], outbin[200], scratch[220], cmd[1024];
snprintf(root, sizeof root, "/tmp/pk51w_%d_%d", getpid(), idx);
snprintf(libroot, sizeof libroot, "%s/lib", root);
snprintf(libdir, sizeof libdir, "%s/lib/pk2", root);
snprintf(appdir, sizeof appdir, "%s/app", root);
snprintf(pk2, sizeof pk2, "%s/pk2.ww", libdir);
snprintf(appww, sizeof appww, "%s/app/main.ww", root);
snprintf(rm, sizeof rm, "rm -rf %s", root);
snprintf(outbin, sizeof outbin, "%s/app/m", root);
snprintf(scratch, sizeof scratch, "%s.sepwork", outbin);
if (mkdir(root, 0755) != 0) {
perror(root);
return -1;
}
int result = -1;
int cleanfail;
int libroot_owned = 0, libdir_owned = 0, app_owned = 0;
if (mkdir(libroot, 0755) != 0) { perror(libroot); goto cleanup; }
libroot_owned = 1;
if (mkdir(libdir, 0755) != 0) { perror(libdir); goto cleanup; }
libdir_owned = 1;
if (mkdir(appdir, 0755) != 0) { perror(appdir); goto cleanup; }
app_owned = 1;
char mk[256];
snprintf(mk, sizeof mk, "mkdir -p %s/lib/pk2 %s/app", root, root);
if (runwait(mk) != 0) { runwait(rm); return -1; }
char pk2[256];
snprintf(pk2, sizeof pk2, "%s/lib/pk2/pk2.ww", root);
FILE *f = fopen(pk2, "wb");
if (!f) { runwait(rm); return -1; }
if (!f) { perror(pk2); goto cleanup; }
/* pk2 exports ONLY the packed type — no helper fns. The size/offset
* are computed on the IMPORTER side (below), so the value depends on
* the .wwi re-parse laying Ev out packed. (If evsize()/evoff() lived
@@ -329,10 +371,10 @@ wwi_roundtrip(const char *driver, int idx)
* @packed re-parse would never be exercised.) */
fputs("package pk2;\n"
"export type Ev = struct @packed { tag: u8, data: u64 };\n", f);
fclose(f);
if (fclose(f) != 0) { perror(pk2); goto cleanup; }
f = fopen(appww, "wb");
if (!f) { runwait(rm); return -1; }
if (!f) { perror(appww); goto cleanup; }
/* importer computes layout from the re-parsed .wwi: packed → 9*10+1
* = 91; a dropped @packed (unpacked Ev) would give 16*10+8 = 168. */
fputs("package main;\n"
@@ -341,20 +383,45 @@ wwi_roundtrip(const char *driver, int idx)
"\tlet e: pk2.Ev = pk2.Ev { tag = 1u8, data = 2u64 };\n"
"\treturn size(pk2.Ev): i32 * 10 + offset(e.data): i32;\n"
"};\n", f);
fclose(f);
if (fclose(f) != 0) { perror(appww); goto cleanup; }
snprintf(cmd, sizeof cmd,
"cd %s/app && %s build -I %s/lib -o m main.ww 2>/dev/null",
root, driver, root);
if (runwait(cmd) != 0) {
runwait(rm);
return -1;
goto cleanup;
}
snprintf(outbin, sizeof outbin, "%s/app/m", root);
int got = runwait(outbin);
result = runwait(outbin);
runwait(rm);
return got;
cleanup:
cleanfail = 0;
if (app_owned) {
snprintf(cmd, sizeof cmd, "rm -rf %s", scratch);
if (runwait(cmd) != 0) cleanfail = 1;
if (unlink(outbin) != 0 && errno != ENOENT) {
perror(outbin); cleanfail = 1;
}
if (unlink(appww) != 0 && errno != ENOENT) {
perror(appww); cleanfail = 1;
}
}
if (libdir_owned && unlink(pk2) != 0 && errno != ENOENT) {
perror(pk2); cleanfail = 1;
}
if (app_owned && rmdir(appdir) != 0) {
perror(appdir); cleanfail = 1;
}
if (libdir_owned && rmdir(libdir) != 0) {
perror(libdir); cleanfail = 1;
}
if (libroot_owned && rmdir(libroot) != 0) {
perror(libroot); cleanfail = 1;
}
if (rmdir(root) != 0) {
perror(root); cleanfail = 1;
}
if (cleanfail && result == 91) result = -1;
return result;
}
int
@@ -394,23 +461,14 @@ main(void)
for (int i = 0; i < n; i++) {
int got = run_driver(drivers[d].path, &rows[i], i);
total++;
int bad;
if (rows[i].want == REJECT_CSTAGE) {
/* cstage must reject (build fail → got==-1). wwstage
* rides the #224/#10 struct-leniency (header) — its
* disposition is not asserted here. */
if (is_cstage)
bad = (got != -1);
else
bad = 0;
} else {
bad = (got != rows[i].want);
}
int want = rows[i].want;
if (want == REJECT_CSTAGE) want = is_cstage ? -1 : 0;
int bad = (got != want);
if (bad) {
fprintf(stderr,
"struct_packed[%s][%s]: exit=%d want=%d\n",
drivers[d].name, rows[i].label,
got, rows[i].want);
got, want);
fail++;
}
}

View File

@@ -68,6 +68,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -154,31 +155,47 @@ static const struct row rows[] = {
static int
run_driver(const char *driver, const struct row *r, int i)
{
char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024];
char tmpdir[64], src[128], outbin[128], scratch[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/aee_%d_d_%d", getpid(), i);
snprintf(src, sizeof src, "%s/aee_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/aee_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
snprintf(scratch, sizeof scratch, "%s.sepwork", outbin);
mkdir(tmpdir, 0755);
if (mkdir(tmpdir, 0755) != 0) {
perror(tmpdir);
return -1;
}
int result = -1;
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
if (!f) { perror(src); goto cleanup; }
wwtest_fputs(r->src, f);
fclose(f);
if (fclose(f) != 0) { perror(src); goto cleanup; }
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
runwait(rmcmd);
return -1;
goto cleanup;
}
int got = runwait(outbin);
result = runwait(outbin);
runwait(rmcmd);
return got;
cleanup:
/* `ww build -o` retains this exact caller-owned scratch tree. */
snprintf(cmd, sizeof cmd, "rm -rf %s", scratch);
int cleanfail = runwait(cmd) != 0;
if (unlink(outbin) != 0 && errno != ENOENT) {
perror(outbin); cleanfail = 1;
}
if (unlink(src) != 0 && errno != ENOENT) {
perror(src); cleanfail = 1;
}
if (rmdir(tmpdir) != 0) {
perror(tmpdir); cleanfail = 1;
}
if (cleanfail && result == r->want) result = -1;
return result;
}
/* asm_byte_identical — generate .s via cstage's w6c and wwstage's
@@ -188,33 +205,37 @@ run_driver(const char *driver, const struct row *r, int i)
static int
asm_byte_identical(const char *bin, const struct row *r, int i)
{
char src[64], cs[64], ws[64], cmd[1024];
snprintf(src, sizeof src, "/tmp/aee_asm_%d_%d.ww", getpid(), i);
snprintf(cs, sizeof cs, "/tmp/aee_asm_%d_%d_c.s", getpid(), i);
snprintf(ws, sizeof ws, "/tmp/aee_asm_%d_%d_w.s", getpid(), i);
char tmpdir[64], src[128], cs[128], ws[128], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/aee_asm_%d_%d", getpid(), i);
snprintf(src, sizeof src, "%s/input.ww", tmpdir);
snprintf(cs, sizeof cs, "%s/c.s", tmpdir);
snprintf(ws, sizeof ws, "%s/w.s", tmpdir);
if (mkdir(tmpdir, 0755) != 0) {
perror(tmpdir);
return -1;
}
int rc = -1;
FILE *f = fopen(src, "wb");
if (!f) return -1;
if (!f) { perror(src); goto cleanup; }
wwtest_fputs(r->src, f);
fclose(f);
if (fclose(f) != 0) { perror(src); goto cleanup; }
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
unlink(src);
return -1;
goto cleanup;
}
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
bin, ws, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
unlink(src); unlink(cs);
return -1;
goto cleanup;
}
FILE *fc = fopen(cs, "rb");
FILE *fw = fopen(ws, "rb");
int rc = 0;
rc = 0;
if (!fc || !fw) {
rc = -1;
} else {
@@ -230,7 +251,20 @@ asm_byte_identical(const char *bin, const struct row *r, int i)
if (rc != 0)
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
r->label);
unlink(src); unlink(cs); unlink(ws);
cleanup:
if (unlink(src) != 0 && errno != ENOENT) {
perror(src); rc = -1;
}
if (unlink(cs) != 0 && errno != ENOENT) {
perror(cs); rc = -1;
}
if (unlink(ws) != 0 && errno != ENOENT) {
perror(ws); rc = -1;
}
if (rmdir(tmpdir) != 0) {
perror(tmpdir); rc = -1;
}
return rc;
}

View File

@@ -68,6 +68,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -247,31 +248,48 @@ static const struct row rows[] = {
static int
run_driver(const char *driver, const struct row *r, int i)
{
char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024];
char tmpdir[64], src[128], outbin[128], scratch[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/asse_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/asse_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/asse_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
snprintf(scratch, sizeof scratch, "%s.sepwork", outbin);
if (mkdir(tmpdir, 0755) != 0) {
perror(tmpdir);
return -1;
}
int result = -1;
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
if (!f) { perror(src); goto cleanup; }
fputs(r->src, f);
fclose(f);
if (fclose(f) != 0) { perror(src); goto cleanup; }
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
runwait(rmcmd);
return -1;
goto cleanup;
}
int got = runwait(outbin);
result = runwait(outbin);
runwait(rmcmd);
return got;
cleanup:
/* `ww build -o` retains this exact caller-owned scratch tree. */
snprintf(cmd, sizeof cmd, "rm -rf %s", scratch);
int cleanfail = runwait(cmd) != 0;
if (unlink(outbin) != 0 && errno != ENOENT) {
perror(outbin); cleanfail = 1;
}
if (unlink(src) != 0 && errno != ENOENT) {
perror(src); cleanfail = 1;
}
if (rmdir(tmpdir) != 0) {
perror(tmpdir); cleanfail = 1;
}
if (cleanfail && (r->want == BYTEID_ONLY || result == r->want))
result = -1;
return result;
}
/* asm_byte_identical — generate .s via cstage's w6c and wwstage's
@@ -281,33 +299,37 @@ run_driver(const char *driver, const struct row *r, int i)
static int
asm_byte_identical(const char *bin, const struct row *r, int i)
{
char src[64], cs[64], ws[64], cmd[1024];
snprintf(src, sizeof src, "/tmp/asse_asm_%d_%d.ww", getpid(), i);
snprintf(cs, sizeof cs, "/tmp/asse_asm_%d_%d_c.s", getpid(), i);
snprintf(ws, sizeof ws, "/tmp/asse_asm_%d_%d_w.s", getpid(), i);
char tmpdir[64], src[128], cs[128], ws[128], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/asse_asm_%d_%d", getpid(), i);
snprintf(src, sizeof src, "%s/input.ww", tmpdir);
snprintf(cs, sizeof cs, "%s/c.s", tmpdir);
snprintf(ws, sizeof ws, "%s/w.s", tmpdir);
if (mkdir(tmpdir, 0755) != 0) {
perror(tmpdir);
return -1;
}
int rc = -1;
FILE *f = fopen(src, "wb");
if (!f) return -1;
if (!f) { perror(src); goto cleanup; }
fputs(r->src, f);
fclose(f);
if (fclose(f) != 0) { perror(src); goto cleanup; }
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
unlink(src);
return -1;
goto cleanup;
}
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
bin, ws, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
unlink(src); unlink(cs);
return -1;
goto cleanup;
}
FILE *fc = fopen(cs, "rb");
FILE *fw = fopen(ws, "rb");
int rc = 0;
rc = 0;
if (!fc || !fw) {
rc = -1;
} else {
@@ -323,7 +345,20 @@ asm_byte_identical(const char *bin, const struct row *r, int i)
if (rc != 0)
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
r->label);
unlink(src); unlink(cs); unlink(ws);
cleanup:
if (unlink(src) != 0 && errno != ENOENT) {
perror(src); rc = -1;
}
if (unlink(cs) != 0 && errno != ENOENT) {
perror(cs); rc = -1;
}
if (unlink(ws) != 0 && errno != ENOENT) {
perror(ws); rc = -1;
}
if (rmdir(tmpdir) != 0) {
perror(tmpdir); rc = -1;
}
return rc;
}

View File

@@ -47,6 +47,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -187,89 +188,116 @@ static const char *neg[] = {
static int
run_driver(const char *driver, const struct row *r, int i)
{
char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024];
char tmpdir[64], src[128], outbin[128], scratch[160], rmcmd[192], cmd[1024];
int result = -1, setupfail = 0, cleanfail = 0;
snprintf(tmpdir, sizeof tmpdir, "/tmp/ail_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
if (mkdir(tmpdir, 0755) != 0) return -1;
snprintf(src, sizeof src, "%s/ail_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/ail_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
snprintf(scratch, sizeof scratch, "%s.sepwork", outbin);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
fputs(r->src, f);
fclose(f);
if (!f) { setupfail = 1; goto cleanup; }
int writefail = fputs(r->src, f) == EOF;
if (fclose(f) != 0) writefail = 1;
if (writefail) { setupfail = 1; goto cleanup; }
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
runwait(rmcmd);
return -1;
goto cleanup;
}
int got = runwait(outbin);
runwait(rmcmd);
return got;
result = runwait(outbin);
cleanup:
if (setupfail)
fprintf(stderr, "row[%s]: temporary setup failed\n", r->label);
snprintf(rmcmd, sizeof rmcmd, "rm -rf -- %s", scratch);
if (runwait(rmcmd) != 0) cleanfail = 1;
if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(tmpdir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "row[%s]: temporary cleanup failed\n", r->label);
if (result == r->want) return -1;
}
return result;
}
/* build_should_fail — a `[_]T` that can't infer must error on `driver`;
* returns 0 when the build correctly FAILS, non-zero when it wrongly
* succeeded. */
* returns 0 when the build correctly FAILS, positive when it wrongly
* succeeds, and negative on fixture infrastructure failure. */
static int
build_should_fail(const char *driver, const char *src, int i)
{
char tmpdir[64], s[128], outbin[128], rmcmd[160], cmd[1024];
char tmpdir[64], s[128], outbin[128], scratch[160], rmcmd[192], cmd[1024];
int result = -1, setupfail = 0, cleanfail = 0;
snprintf(tmpdir, sizeof tmpdir, "/tmp/ailn_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
if (mkdir(tmpdir, 0755) != 0) return -1;
snprintf(s, sizeof s, "%s/ailn_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/ailn_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
snprintf(scratch, sizeof scratch, "%s.sepwork", outbin);
FILE *f = fopen(s, "wb");
if (!f) { runwait(rmcmd); return -1; }
fputs(src, f);
fclose(f);
if (!f) { setupfail = 1; goto cleanup; }
int writefail = fputs(src, f) == EOF;
if (fclose(f) != 0) writefail = 1;
if (writefail) { setupfail = 1; goto cleanup; }
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, s);
int rc = runwait(cmd);
runwait(rmcmd);
return rc == 0 ? -1 : 0; /* build must NOT succeed */
result = rc == 0 ? 1 : 0; /* build must NOT succeed */
cleanup:
if (setupfail)
fprintf(stderr, "arr_infer_len[neg%d]: temporary setup failed\n", i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf -- %s", scratch);
if (runwait(rmcmd) != 0) cleanfail = 1;
if (unlink(s) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(tmpdir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "arr_infer_len[neg%d]: temporary cleanup failed\n", i);
if (result == 0) return -1;
}
return result;
}
/* asm_byte_identical — w6c vs w6c_ww .s for the same source must match. */
static int
asm_byte_identical(const char *bin, const struct row *r, int i)
{
char src[64], cs[64], ws[64], cmd[1024];
snprintf(src, sizeof src, "/tmp/ail_asm_%d_%d.ww", getpid(), i);
snprintf(cs, sizeof cs, "/tmp/ail_asm_%d_%d_c.s", getpid(), i);
snprintf(ws, sizeof ws, "/tmp/ail_asm_%d_%d_w.s", getpid(), i);
char tmpdir[64], src[96], cs[96], ws[96], cmd[1024];
int rc = -1, cleanfail = 0;
snprintf(tmpdir, sizeof tmpdir, "/tmp/ail_asm_%d_%d", getpid(), i);
if (mkdir(tmpdir, 0755) != 0) return -1;
snprintf(src, sizeof src, "%s/t.ww", tmpdir);
snprintf(cs, sizeof cs, "%s/c.s", tmpdir);
snprintf(ws, sizeof ws, "%s/w.s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) return -1;
fputs(r->src, f);
fclose(f);
if (!f) goto cleanup;
int writefail = fputs(r->src, f) == EOF;
if (fclose(f) != 0) writefail = 1;
if (writefail) goto cleanup;
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
unlink(src);
return -1;
goto cleanup;
}
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
bin, ws, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
unlink(src); unlink(cs);
return -1;
goto cleanup;
}
FILE *fc = fopen(cs, "rb");
FILE *fw = fopen(ws, "rb");
int rc = 0;
rc = 0;
if (!fc || !fw) {
rc = -1;
} else {
@@ -285,7 +313,15 @@ asm_byte_identical(const char *bin, const struct row *r, int i)
if (rc != 0)
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
r->label);
unlink(src); unlink(cs); unlink(ws);
cleanup:
if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(cs) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(ws) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(tmpdir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "row[%s]: temporary cleanup failed\n", r->label);
return -1;
}
return rc;
}
@@ -338,12 +374,17 @@ main(void)
}
for (int i = 0; i < nn; i++) {
total++;
if (build_should_fail(drivers[d].path, neg[i],
100 + i) != 0) {
fprintf(stderr,
"arr_infer_len[%s][neg%d]: built ok, "
"expected a loud error\n",
drivers[d].name, i);
int nr = build_should_fail(drivers[d].path, neg[i], 100 + i);
if (nr != 0) {
if (nr > 0)
fprintf(stderr,
"arr_infer_len[%s][neg%d]: built ok, "
"expected a loud error\n",
drivers[d].name, i);
else
fprintf(stderr,
"arr_infer_len[%s][neg%d]: fixture infrastructure failed\n",
drivers[d].name, i);
fail++;
}
}

View File

@@ -51,6 +51,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -188,56 +189,81 @@ static const char *neg[] = {
static int
run_driver(const char *driver, const struct row *r, int i)
{
char src[128], tmpdir[64], outbin[128], rmcmd[160], cmd[1024];
char src[128], tmpdir[64], outbin[128], scratch[160], rmcmd[192], cmd[1024];
int result = -1, setupfail = 0, cleanfail = 0;
snprintf(tmpdir, sizeof tmpdir, "/tmp/atag_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
if (mkdir(tmpdir, 0755) != 0) return -1;
snprintf(src, sizeof src, "%s/atag_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/atag_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
snprintf(scratch, sizeof scratch, "%s.sepwork", outbin);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
fputs(r->src, f);
fclose(f);
if (!f) { setupfail = 1; goto cleanup; }
int writefail = fputs(r->src, f) == EOF;
if (fclose(f) != 0) writefail = 1;
if (writefail) { setupfail = 1; goto cleanup; }
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
runwait(rmcmd);
return -1;
goto cleanup;
}
int got = runwait(outbin);
runwait(rmcmd);
return got;
result = runwait(outbin);
cleanup:
if (setupfail)
fprintf(stderr, "row[%s]: temporary setup failed\n", r->label);
snprintf(rmcmd, sizeof rmcmd, "rm -rf -- %s", scratch);
if (runwait(rmcmd) != 0) cleanfail = 1;
if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(tmpdir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "row[%s]: temporary cleanup failed\n", r->label);
if (result == r->want) return -1;
}
return result;
}
/* build_should_fail — a `[N]tagged=[x,...]` repeat-fill must error on
* `driver` (rule-7 loud-stop); returns 0 when the build correctly
* FAILS, non-zero when it wrongly succeeded. */
* FAILS, positive when it wrongly succeeds, and negative on fixture
* infrastructure failure. */
static int
build_should_fail(const char *driver, const char *src, int i)
{
char s[128], tmpdir[64], outbin[128], rmcmd[160], cmd[1024];
char s[128], tmpdir[64], outbin[128], scratch[160], rmcmd[192], cmd[1024];
int result = -1, setupfail = 0, cleanfail = 0;
snprintf(tmpdir, sizeof tmpdir, "/tmp/atagn_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
if (mkdir(tmpdir, 0755) != 0) return -1;
snprintf(s, sizeof s, "%s/atagn_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/atagn_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
snprintf(scratch, sizeof scratch, "%s.sepwork", outbin);
FILE *f = fopen(s, "wb");
if (!f) { runwait(rmcmd); return -1; }
fputs(src, f);
fclose(f);
if (!f) { setupfail = 1; goto cleanup; }
int writefail = fputs(src, f) == EOF;
if (fclose(f) != 0) writefail = 1;
if (writefail) { setupfail = 1; goto cleanup; }
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, s);
int rc = runwait(cmd);
runwait(rmcmd);
return rc == 0 ? -1 : 0; /* build must NOT succeed */
result = rc == 0 ? 1 : 0; /* build must NOT succeed */
cleanup:
if (setupfail)
fprintf(stderr, "arr_tagged_elem[neg%d]: temporary setup failed\n", i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf -- %s", scratch);
if (runwait(rmcmd) != 0) cleanfail = 1;
if (unlink(s) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(tmpdir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "arr_tagged_elem[neg%d]: temporary cleanup failed\n", i);
if (result == 0) return -1;
}
return result;
}
/* asm_byte_identical — generate .s via cstage's w6c and wwstage's
@@ -247,33 +273,35 @@ build_should_fail(const char *driver, const char *src, int i)
static int
asm_byte_identical(const char *bin, const struct row *r, int i)
{
char src[64], cs[64], ws[64], cmd[1024];
snprintf(src, sizeof src, "/tmp/atag_asm_%d_%d.ww", getpid(), i);
snprintf(cs, sizeof cs, "/tmp/atag_asm_%d_%d_c.s", getpid(), i);
snprintf(ws, sizeof ws, "/tmp/atag_asm_%d_%d_w.s", getpid(), i);
char tmpdir[64], src[96], cs[96], ws[96], cmd[1024];
int rc = -1, cleanfail = 0;
snprintf(tmpdir, sizeof tmpdir, "/tmp/atag_asm_%d_%d", getpid(), i);
if (mkdir(tmpdir, 0755) != 0) return -1;
snprintf(src, sizeof src, "%s/t.ww", tmpdir);
snprintf(cs, sizeof cs, "%s/c.s", tmpdir);
snprintf(ws, sizeof ws, "%s/w.s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) return -1;
fputs(r->src, f);
fclose(f);
if (!f) goto cleanup;
int writefail = fputs(r->src, f) == EOF;
if (fclose(f) != 0) writefail = 1;
if (writefail) goto cleanup;
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
unlink(src);
return -1;
goto cleanup;
}
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
bin, ws, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
unlink(src); unlink(cs);
return -1;
goto cleanup;
}
FILE *fc = fopen(cs, "rb");
FILE *fw = fopen(ws, "rb");
int rc = 0;
rc = 0;
if (!fc || !fw) {
rc = -1;
} else {
@@ -289,7 +317,16 @@ asm_byte_identical(const char *bin, const struct row *r, int i)
if (rc != 0)
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
r->label);
unlink(src); unlink(cs); unlink(ws);
cleanup:
if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(cs) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(ws) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(tmpdir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "row[%s]: temporary cleanup failed\n", r->label);
return -1;
}
return rc;
}
@@ -342,12 +379,17 @@ main(void)
}
for (int i = 0; i < nn; i++) {
total++;
if (build_should_fail(drivers[d].path, neg[i],
100 + i) != 0) {
fprintf(stderr,
"arr_tagged_elem[%s][neg%d]: built ok, "
"expected a loud error (rule-7)\n",
drivers[d].name, i);
int nr = build_should_fail(drivers[d].path, neg[i], 100 + i);
if (nr != 0) {
if (nr > 0)
fprintf(stderr,
"arr_tagged_elem[%s][neg%d]: built ok, "
"expected a loud error (rule-7)\n",
drivers[d].name, i);
else
fprintf(stderr,
"arr_tagged_elem[%s][neg%d]: fixture infrastructure failed\n",
drivers[d].name, i);
fail++;
}
}

View File

@@ -26,6 +26,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -82,31 +83,39 @@ static const struct row rows[] = {
static int
run_driver(const char *driver, const struct row *r, int i)
{
char src[128], tmpdir[64], outbin[128], rmcmd[160], cmd[1024];
char src[128], tmpdir[64], outbin[128], scratch[160], rmcmd[192], cmd[1024];
int result = -1, cleanfail = 0;
snprintf(tmpdir, sizeof tmpdir, "/tmp/ssgz_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
if (mkdir(tmpdir, 0755) != 0) return -1;
snprintf(src, sizeof src, "%s/ssgz_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/ssgz_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
snprintf(scratch, sizeof scratch, "%s.sepwork", outbin);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
fputs(r->src, f);
fclose(f);
if (!f) goto cleanup;
int writefail = fputs(r->src, f) == EOF;
if (fclose(f) != 0) writefail = 1;
if (writefail) goto cleanup;
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
runwait(rmcmd);
return -1;
goto cleanup;
}
int got = runwait(outbin);
runwait(rmcmd);
return got;
result = runwait(outbin);
cleanup:
snprintf(rmcmd, sizeof rmcmd, "rm -rf -- %s", scratch);
if (runwait(rmcmd) != 0) cleanfail = 1;
if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(tmpdir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "row[%s]: temporary cleanup failed\n", r->label);
if (result == r->want) return -1;
}
return result;
}
/* asm_byte_identical — w6c vs w6c_ww .s for the same source must match.
@@ -115,33 +124,35 @@ run_driver(const char *driver, const struct row *r, int i)
static int
asm_byte_identical(const char *bin, const struct row *r, int i)
{
char src[64], cs[64], ws[64], cmd[1024];
snprintf(src, sizeof src, "/tmp/ssgz_asm_%d_%d.ww", getpid(), i);
snprintf(cs, sizeof cs, "/tmp/ssgz_asm_%d_%d_c.s", getpid(), i);
snprintf(ws, sizeof ws, "/tmp/ssgz_asm_%d_%d_w.s", getpid(), i);
char tmpdir[64], src[96], cs[96], ws[96], cmd[1024];
int rc = -1, cleanfail = 0;
snprintf(tmpdir, sizeof tmpdir, "/tmp/ssgz_asm_%d_%d", getpid(), i);
if (mkdir(tmpdir, 0755) != 0) return -1;
snprintf(src, sizeof src, "%s/t.ww", tmpdir);
snprintf(cs, sizeof cs, "%s/c.s", tmpdir);
snprintf(ws, sizeof ws, "%s/w.s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) return -1;
fputs(r->src, f);
fclose(f);
if (!f) goto cleanup;
int writefail = fputs(r->src, f) == EOF;
if (fclose(f) != 0) writefail = 1;
if (writefail) goto cleanup;
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
unlink(src);
return -1;
goto cleanup;
}
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
bin, ws, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
unlink(src); unlink(cs);
return -1;
goto cleanup;
}
FILE *fc = fopen(cs, "rb");
FILE *fw = fopen(ws, "rb");
int rc = 0;
rc = 0;
if (!fc || !fw) {
rc = -1;
} else {
@@ -157,7 +168,15 @@ asm_byte_identical(const char *bin, const struct row *r, int i)
if (rc != 0)
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
r->label);
unlink(src); unlink(cs); unlink(ws);
cleanup:
if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(cs) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(ws) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(tmpdir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "row[%s]: temporary cleanup failed\n", r->label);
return -1;
}
return rc;
}

View File

@@ -50,6 +50,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -194,91 +195,116 @@ static const char *neg[] = {
static int
run_driver(const char *driver, const struct row *r, int i)
{
char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024];
char tmpdir[64], src[128], outbin[128], scratch[160], rmcmd[192], cmd[1024];
int result = -1, setupfail = 0, cleanfail = 0;
snprintf(tmpdir, sizeof tmpdir, "/tmp/slg_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
if (mkdir(tmpdir, 0755) != 0) return -1;
snprintf(src, sizeof src, "%s/slg_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/slg_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
snprintf(scratch, sizeof scratch, "%s.sepwork", outbin);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
fputs(r->src, f);
fclose(f);
if (!f) { setupfail = 1; goto cleanup; }
int writefail = fputs(r->src, f) == EOF;
if (fclose(f) != 0) writefail = 1;
if (writefail) { setupfail = 1; goto cleanup; }
/* #8: -o pins binary + .sepwork scratch under tmpdir; rm -rf on
* every exit path removes the now-non-empty dir. */
/* Ordinary build retains caller-owned .sepwork scratch beside -o. */
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
runwait(rmcmd);
return -1;
goto cleanup;
}
int got = runwait(outbin);
runwait(rmcmd);
return got;
result = runwait(outbin);
cleanup:
if (setupfail)
fprintf(stderr, "row[%s]: temporary setup failed\n", r->label);
snprintf(rmcmd, sizeof rmcmd, "rm -rf -- %s", scratch);
if (runwait(rmcmd) != 0) cleanfail = 1;
if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(tmpdir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "row[%s]: temporary cleanup failed\n", r->label);
if (result == r->want) return -1;
}
return result;
}
/* build_should_fail — returns 0 when the build correctly FAILS. */
/* build_should_fail — 0 on the expected rejection, positive on an
* unexpected successful build, negative on fixture infrastructure failure. */
static int
build_should_fail(const char *driver, const char *src, int i)
{
char tmpdir[64], s[128], outbin[128], rmcmd[160], cmd[1024];
char tmpdir[64], s[128], outbin[128], scratch[160], rmcmd[192], cmd[1024];
int result = -1, setupfail = 0, cleanfail = 0;
snprintf(tmpdir, sizeof tmpdir, "/tmp/slgn_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
if (mkdir(tmpdir, 0755) != 0) return -1;
snprintf(s, sizeof s, "%s/slgn_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/slgn_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
snprintf(scratch, sizeof scratch, "%s.sepwork", outbin);
FILE *f = fopen(s, "wb");
if (!f) { runwait(rmcmd); return -1; }
fputs(src, f);
fclose(f);
if (!f) { setupfail = 1; goto cleanup; }
int writefail = fputs(src, f) == EOF;
if (fclose(f) != 0) writefail = 1;
if (writefail) { setupfail = 1; goto cleanup; }
/* #8: -o pins binary + .sepwork scratch under tmpdir; a reject build
* still mkdir's scratch, so rm -rf must run on this path too. */
/* A rejected ordinary build can also leave caller-owned .sepwork. */
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, s);
int rc = runwait(cmd);
runwait(rmcmd);
return rc == 0 ? -1 : 0; /* build must NOT succeed */
result = rc == 0 ? 1 : 0; /* build must NOT succeed */
cleanup:
if (setupfail)
fprintf(stderr, "slice_literal_global[neg%d]: temporary setup failed\n", i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf -- %s", scratch);
if (runwait(rmcmd) != 0) cleanfail = 1;
if (unlink(s) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(tmpdir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "slice_literal_global[neg%d]: temporary cleanup failed\n", i);
if (result == 0) return -1;
}
return result;
}
/* asm_byte_identical — w6c vs w6c_ww .s for the same source must match. */
static int
asm_byte_identical(const char *bin, const struct row *r, int i)
{
char src[64], cs[64], ws[64], cmd[1024];
snprintf(src, sizeof src, "/tmp/slg_asm_%d_%d.ww", getpid(), i);
snprintf(cs, sizeof cs, "/tmp/slg_asm_%d_%d_c.s", getpid(), i);
snprintf(ws, sizeof ws, "/tmp/slg_asm_%d_%d_w.s", getpid(), i);
char tmpdir[64], src[96], cs[96], ws[96], cmd[1024];
int rc = -1, cleanfail = 0;
snprintf(tmpdir, sizeof tmpdir, "/tmp/slg_asm_%d_%d", getpid(), i);
if (mkdir(tmpdir, 0755) != 0) return -1;
snprintf(src, sizeof src, "%s/t.ww", tmpdir);
snprintf(cs, sizeof cs, "%s/c.s", tmpdir);
snprintf(ws, sizeof ws, "%s/w.s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) return -1;
fputs(r->src, f);
fclose(f);
if (!f) goto cleanup;
int writefail = fputs(r->src, f) == EOF;
if (fclose(f) != 0) writefail = 1;
if (writefail) goto cleanup;
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
unlink(src);
return -1;
goto cleanup;
}
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
bin, ws, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
unlink(src); unlink(cs);
return -1;
goto cleanup;
}
FILE *fc = fopen(cs, "rb");
FILE *fw = fopen(ws, "rb");
int rc = 0;
rc = 0;
if (!fc || !fw) {
rc = -1;
} else {
@@ -294,7 +320,15 @@ asm_byte_identical(const char *bin, const struct row *r, int i)
if (rc != 0)
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
r->label);
unlink(src); unlink(cs); unlink(ws);
cleanup:
if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(cs) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(ws) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(tmpdir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "row[%s]: temporary cleanup failed\n", r->label);
return -1;
}
return rc;
}
@@ -347,12 +381,17 @@ main(void)
}
for (int i = 0; i < nn; i++) {
total++;
if (build_should_fail(drivers[d].path, neg[i],
100 + i) != 0) {
fprintf(stderr,
"slice_literal_global[%s][neg%d]: built ok, "
"expected a loud error\n",
drivers[d].name, i);
int nr = build_should_fail(drivers[d].path, neg[i], 100 + i);
if (nr != 0) {
if (nr > 0)
fprintf(stderr,
"slice_literal_global[%s][neg%d]: built ok, "
"expected a loud error\n",
drivers[d].name, i);
else
fprintf(stderr,
"slice_literal_global[%s][neg%d]: fixture infrastructure failed\n",
drivers[d].name, i);
fail++;
}
}

View File

@@ -45,6 +45,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -130,31 +131,40 @@ static const struct row rows[] = {
static int
run_driver(const char *driver, const struct row *r, int i)
{
char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024];
char tmpdir[64], src[128], outbin[128], scratch[160], rmcmd[192], cmd[1024];
int result = -1, cleanfail = 0;
snprintf(tmpdir, sizeof tmpdir, "/tmp/gae_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
if (mkdir(tmpdir, 0755) != 0) return -1;
snprintf(src, sizeof src, "%s/gae_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/gae_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
snprintf(scratch, sizeof scratch, "%s.sepwork", outbin);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
if (!f) goto cleanup;
wwtest_fputs(r->src, f);
fclose(f);
int writefail = ferror(f);
if (fclose(f) != 0) writefail = 1;
if (writefail) goto cleanup;
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
runwait(rmcmd);
return -1;
goto cleanup;
}
int got = runwait(outbin);
runwait(rmcmd);
return got;
result = runwait(outbin);
cleanup:
snprintf(rmcmd, sizeof rmcmd, "rm -rf -- %s", scratch);
if (runwait(rmcmd) != 0) cleanfail = 1;
if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(tmpdir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "row[%s]: temporary cleanup failed\n", r->label);
if (result == r->want) return -1;
}
return result;
}
/* asm_byte_identical — generate .s via cstage's w6c and wwstage's
@@ -163,33 +173,36 @@ run_driver(const char *driver, const struct row *r, int i)
static int
asm_byte_identical(const char *bin, const struct row *r, int i)
{
char src[64], cs[64], ws[64], cmd[1024];
snprintf(src, sizeof src, "/tmp/gae_asm_%d_%d.ww", getpid(), i);
snprintf(cs, sizeof cs, "/tmp/gae_asm_%d_%d_c.s", getpid(), i);
snprintf(ws, sizeof ws, "/tmp/gae_asm_%d_%d_w.s", getpid(), i);
char tmpdir[64], src[96], cs[96], ws[96], cmd[1024];
int rc = -1, cleanfail = 0;
snprintf(tmpdir, sizeof tmpdir, "/tmp/gae_asm_%d_%d", getpid(), i);
if (mkdir(tmpdir, 0755) != 0) return -1;
snprintf(src, sizeof src, "%s/t.ww", tmpdir);
snprintf(cs, sizeof cs, "%s/c.s", tmpdir);
snprintf(ws, sizeof ws, "%s/w.s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) return -1;
if (!f) goto cleanup;
wwtest_fputs(r->src, f);
fclose(f);
int writefail = ferror(f);
if (fclose(f) != 0) writefail = 1;
if (writefail) goto cleanup;
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
unlink(src);
return -1;
goto cleanup;
}
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
bin, ws, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
unlink(src); unlink(cs);
return -1;
goto cleanup;
}
FILE *fc = fopen(cs, "rb");
FILE *fw = fopen(ws, "rb");
int rc = 0;
rc = 0;
if (!fc || !fw) {
rc = -1;
} else {
@@ -205,7 +218,15 @@ asm_byte_identical(const char *bin, const struct row *r, int i)
if (rc != 0)
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
r->label);
unlink(src); unlink(cs); unlink(ws);
cleanup:
if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(cs) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(ws) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(tmpdir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "row[%s]: temporary cleanup failed\n", r->label);
return -1;
}
return rc;
}

View File

@@ -25,6 +25,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
@@ -50,55 +51,73 @@ static const char *SRC =
static int
run_driver(const char *driver)
{
char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024];
char tmpdir[64], src[128], outbin[128], scratch[160], rmcmd[192], cmd[1024];
int result = -1, cleanfail = 0;
snprintf(tmpdir, sizeof tmpdir, "/tmp/agnd_%d_d", getpid());
mkdir(tmpdir, 0755);
if (mkdir(tmpdir, 0755) != 0) return -1;
snprintf(src, sizeof src, "%s/agnd_%d.ww", tmpdir, getpid());
snprintf(outbin, sizeof outbin, "%s/agnd_%d", tmpdir, getpid());
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
snprintf(scratch, sizeof scratch, "%s.sepwork", outbin);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
fputs(SRC, f);
fclose(f);
if (!f) goto cleanup;
int writefail = fputs(SRC, f) == EOF;
if (fclose(f) != 0) writefail = 1;
if (writefail) goto cleanup;
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, src);
if (runwait(cmd) != 0) { runwait(rmcmd); return -1; }
int got = runwait(outbin);
runwait(rmcmd);
return got;
if (runwait(cmd) != 0) goto cleanup;
result = runwait(outbin);
cleanup:
snprintf(rmcmd, sizeof rmcmd, "rm -rf -- %s", scratch);
if (runwait(rmcmd) != 0) cleanfail = 1;
if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(tmpdir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "arrglob_nodup: temporary cleanup failed\n");
if (result == 6) return -1;
}
return result;
}
/* emit .s via `tool`, return the count of `DATAW main.g(SB)` rows, or -1. */
static int
dataw_rows(const char *bin, const char *tool)
{
char src[64], asmf[80], cmd[1024];
snprintf(src, sizeof src, "/tmp/agnd_asm_%d.ww", getpid());
snprintf(asmf, sizeof asmf, "/tmp/agnd_asm_%d_%s.s", getpid(), tool);
char tmpdir[80], src[96], asmf[96], cmd[1024];
int n = -1, cleanfail = 0;
snprintf(tmpdir, sizeof tmpdir, "/tmp/agnd_asm_%d_%s", getpid(), tool);
if (mkdir(tmpdir, 0755) != 0) return -1;
snprintf(src, sizeof src, "%s/t.ww", tmpdir);
snprintf(asmf, sizeof asmf, "%s/t.s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) return -1;
fputs(SRC, f);
fclose(f);
if (!f) goto cleanup;
int writefail = fputs(SRC, f) == EOF;
if (fclose(f) != 0) writefail = 1;
if (writefail) goto cleanup;
snprintf(cmd, sizeof cmd, "%s/%s -o %s %s 2>/dev/null", bin, tool, asmf, src);
if (runwait(cmd) != 0) { unlink(src); return -1; }
if (runwait(cmd) != 0) goto cleanup;
FILE *a = fopen(asmf, "rb");
int n = 0;
n = 0;
if (a) {
char line[512];
while (fgets(line, sizeof line, a))
if (strncmp(line, "DATAW main.g(SB)", 16) == 0) n++;
fclose(a);
} else {
n = -1;
} else n = -1;
cleanup:
if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(asmf) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(tmpdir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "arrglob_nodup[%s]: temporary cleanup failed\n", tool);
if (n == 1) return -1;
}
unlink(src); unlink(asmf);
return n;
}

View File

@@ -46,6 +46,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -174,43 +175,48 @@ static const struct row ROWS[] = {
/* build+run one row through `driver`; return exit code or -1. */
static int
run_driver(const char *driver, const char *src, int idx)
run_driver(const char *driver, const char *src, int idx, int want)
{
char tmpdir[64], srcf[128], outbin[160], rmcmd[192], cmd[2400];
char tmpdir[64], srcf[128], outbin[160], scratch[192], rmcmd[224], cmd[2400];
int result = -1, cleanfail = 0;
snprintf(tmpdir, sizeof tmpdir, "/tmp/gpfr_%d_%d_d", getpid(), idx);
mkdir(tmpdir, 0755);
if (mkdir(tmpdir, 0755) != 0) return -1;
snprintf(srcf, sizeof srcf, "%s/t.ww", tmpdir);
snprintf(outbin, sizeof outbin, "%s/t", tmpdir);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
snprintf(scratch, sizeof scratch, "%s.sepwork", outbin);
FILE *f = fopen(srcf, "wb");
if (!f) { runwait(rmcmd); return -1; }
fputs(src, f);
fclose(f);
if (!f) goto cleanup;
int writefail = fputs(src, f) == EOF;
if (fclose(f) != 0) writefail = 1;
if (writefail) goto cleanup;
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, srcf);
if (runwait(cmd) != 0) { runwait(rmcmd); return -1; }
int got = runwait(outbin);
runwait(rmcmd);
return got;
if (runwait(cmd) != 0) goto cleanup;
result = runwait(outbin);
cleanup:
snprintf(rmcmd, sizeof rmcmd, "rm -rf -- %s", scratch);
if (runwait(rmcmd) != 0) cleanfail = 1;
if (unlink(srcf) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(tmpdir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "globptr_field_read[%d]: temporary cleanup failed\n", idx);
if (result == want) return -1;
}
return result;
}
/* emit .s for `tool` (w6c / w6c_ww) into asmf; 0 ok, -1 on compile fail. */
/* Emit .s for `tool`; the caller owns srcf and asmf. */
static int
emit_asm(const char *bin, const char *tool, const char *src, int idx,
emit_asm(const char *bin, const char *tool, const char *srcf,
const char *asmf)
{
char srcf[80], cmd[2400];
snprintf(srcf, sizeof srcf, "/tmp/gpfr_asm_%d_%d.ww", getpid(), idx);
FILE *f = fopen(srcf, "wb");
if (!f) return -1;
fputs(src, f);
fclose(f);
char cmd[2400];
snprintf(cmd, sizeof cmd, "%s/%s -o %s %s 2>/dev/null",
bin, tool, asmf, srcf);
int rc = runwait(cmd);
unlink(srcf);
return rc == 0 ? 0 : -1;
}
@@ -250,7 +256,7 @@ main(void)
const struct row *r = &ROWS[i];
total++;
int gc = run_driver(cdrv, r->src, i);
int gc = run_driver(cdrv, r->src, i, r->want);
if (gc != r->want) {
fprintf(stderr, "globptr_field_read[%s,cstage]: run=%d want=%d\n",
r->name, gc, r->want);
@@ -258,7 +264,7 @@ main(void)
}
if (have_ww) {
total++;
int gw = run_driver(wdrv, r->src, i);
int gw = run_driver(wdrv, r->src, i, r->want);
if (gw != r->want) {
fprintf(stderr, "globptr_field_read[%s,wwstage]: run=%d want=%d\n",
r->name, gw, r->want);
@@ -268,18 +274,43 @@ main(void)
/* byte-id: w6c .s == w6c_ww .s */
if (have_ww) {
char csf[80], wwf[80];
snprintf(csf, sizeof csf, "/tmp/gpfr_cs_%d_%d.s", getpid(), i);
snprintf(wwf, sizeof wwf, "/tmp/gpfr_ww_%d_%d.s", getpid(), i);
char td[80], srcf[96], csf[96], wwf[96];
int ec = -1, ew = -1, same = 0, setupfail = 0, cleanfail = 0;
snprintf(td, sizeof td, "/tmp/gpfr_asm_%d_%d", getpid(), i);
snprintf(srcf, sizeof srcf, "%s/t.ww", td);
snprintf(csf, sizeof csf, "%s/c.s", td);
snprintf(wwf, sizeof wwf, "%s/w.s", td);
total++;
int ec = emit_asm(bin, "w6c", r->src, i, csf);
int ew = emit_asm(bin, "w6c_ww", r->src, i, wwf);
if (ec != 0 || ew != 0 || !files_identical(csf, wwf)) {
if (mkdir(td, 0755) == 0) {
FILE *f = fopen(srcf, "wb");
if (f) {
int writefail = fputs(r->src, f) == EOF;
if (fclose(f) != 0) writefail = 1;
if (!writefail) {
ec = emit_asm(bin, "w6c", srcf, csf);
ew = emit_asm(bin, "w6c_ww", srcf, wwf);
same = ec == 0 && ew == 0
&& files_identical(csf, wwf);
} else setupfail = 1;
} else setupfail = 1;
if (unlink(srcf) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(csf) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(wwf) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(td) != 0) cleanfail = 1;
} else setupfail = 1;
if (setupfail)
fprintf(stderr, "globptr_field_read[%s]: temporary setup failed\n",
r->name);
if (!same) {
fprintf(stderr, "globptr_field_read[%s]: cs/ww .s NOT "
"byte-identical (ec=%d ew=%d)\n", r->name, ec, ew);
fail++;
}
unlink(csf); unlink(wwf);
if (cleanfail) {
fprintf(stderr, "globptr_field_read[%s]: temporary cleanup failed\n",
r->name);
if (same) fail++;
}
}
}

View File

@@ -32,6 +32,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -108,43 +109,48 @@ static const struct row ROWS[] = {
/* build+run one row through `driver`; return exit code or -1. */
static int
run_driver(const char *driver, const char *src, int idx)
run_driver(const char *driver, const char *src, int idx, int want)
{
char tmpdir[64], srcf[128], outbin[160], rmcmd[192], cmd[2400];
char tmpdir[64], srcf[128], outbin[160], scratch[192], rmcmd[224], cmd[2400];
int result = -1, cleanfail = 0;
snprintf(tmpdir, sizeof tmpdir, "/tmp/gpfs_%d_%d_d", getpid(), idx);
mkdir(tmpdir, 0755);
if (mkdir(tmpdir, 0755) != 0) return -1;
snprintf(srcf, sizeof srcf, "%s/t.ww", tmpdir);
snprintf(outbin, sizeof outbin, "%s/t", tmpdir);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
snprintf(scratch, sizeof scratch, "%s.sepwork", outbin);
FILE *f = fopen(srcf, "wb");
if (!f) { runwait(rmcmd); return -1; }
fputs(src, f);
fclose(f);
if (!f) goto cleanup;
int writefail = fputs(src, f) == EOF;
if (fclose(f) != 0) writefail = 1;
if (writefail) goto cleanup;
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, srcf);
if (runwait(cmd) != 0) { runwait(rmcmd); return -1; }
int got = runwait(outbin);
runwait(rmcmd);
return got;
if (runwait(cmd) != 0) goto cleanup;
result = runwait(outbin);
cleanup:
snprintf(rmcmd, sizeof rmcmd, "rm -rf -- %s", scratch);
if (runwait(rmcmd) != 0) cleanfail = 1;
if (unlink(srcf) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(tmpdir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "globptr_field_store[%d]: temporary cleanup failed\n", idx);
if (result == want) return -1;
}
return result;
}
/* emit .s for `tool` (w6c / w6c_ww) into asmf; 0 ok, -1 on compile fail. */
/* Emit .s for `tool`; the caller owns srcf and asmf. */
static int
emit_asm(const char *bin, const char *tool, const char *src, int idx,
emit_asm(const char *bin, const char *tool, const char *srcf,
const char *asmf)
{
char srcf[80], cmd[2400];
snprintf(srcf, sizeof srcf, "/tmp/gpfs_asm_%d_%d.ww", getpid(), idx);
FILE *f = fopen(srcf, "wb");
if (!f) return -1;
fputs(src, f);
fclose(f);
char cmd[2400];
snprintf(cmd, sizeof cmd, "%s/%s -o %s %s 2>/dev/null",
bin, tool, asmf, srcf);
int rc = runwait(cmd);
unlink(srcf);
return rc == 0 ? 0 : -1;
}
@@ -184,7 +190,7 @@ main(void)
const struct row *r = &ROWS[i];
total++;
int gc = run_driver(cdrv, r->src, i);
int gc = run_driver(cdrv, r->src, i, r->want);
if (gc != r->want) {
fprintf(stderr, "globptr_field_store[%s,cstage]: run=%d want=%d\n",
r->name, gc, r->want);
@@ -192,7 +198,7 @@ main(void)
}
if (have_ww) {
total++;
int gw = run_driver(wdrv, r->src, i);
int gw = run_driver(wdrv, r->src, i, r->want);
if (gw != r->want) {
fprintf(stderr, "globptr_field_store[%s,wwstage]: run=%d want=%d\n",
r->name, gw, r->want);
@@ -202,18 +208,43 @@ main(void)
/* byte-id: w6c .s == w6c_ww .s */
if (have_ww) {
char csf[80], wwf[80];
snprintf(csf, sizeof csf, "/tmp/gpfs_cs_%d_%d.s", getpid(), i);
snprintf(wwf, sizeof wwf, "/tmp/gpfs_ww_%d_%d.s", getpid(), i);
char td[80], srcf[96], csf[96], wwf[96];
int ec = -1, ew = -1, same = 0, setupfail = 0, cleanfail = 0;
snprintf(td, sizeof td, "/tmp/gpfs_asm_%d_%d", getpid(), i);
snprintf(srcf, sizeof srcf, "%s/t.ww", td);
snprintf(csf, sizeof csf, "%s/c.s", td);
snprintf(wwf, sizeof wwf, "%s/w.s", td);
total++;
int ec = emit_asm(bin, "w6c", r->src, i, csf);
int ew = emit_asm(bin, "w6c_ww", r->src, i, wwf);
if (ec != 0 || ew != 0 || !files_identical(csf, wwf)) {
if (mkdir(td, 0755) == 0) {
FILE *f = fopen(srcf, "wb");
if (f) {
int writefail = fputs(r->src, f) == EOF;
if (fclose(f) != 0) writefail = 1;
if (!writefail) {
ec = emit_asm(bin, "w6c", srcf, csf);
ew = emit_asm(bin, "w6c_ww", srcf, wwf);
same = ec == 0 && ew == 0
&& files_identical(csf, wwf);
} else setupfail = 1;
} else setupfail = 1;
if (unlink(srcf) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(csf) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(wwf) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(td) != 0) cleanfail = 1;
} else setupfail = 1;
if (setupfail)
fprintf(stderr, "globptr_field_store[%s]: temporary setup failed\n",
r->name);
if (!same) {
fprintf(stderr, "globptr_field_store[%s]: cs/ww .s NOT "
"byte-identical (ec=%d ew=%d)\n", r->name, ec, ew);
fail++;
}
unlink(csf); unlink(wwf);
if (cleanfail) {
fprintf(stderr, "globptr_field_store[%s]: temporary cleanup failed\n",
r->name);
if (same) fail++;
}
}
}

View File

@@ -48,6 +48,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -197,31 +198,39 @@ static const struct row rows[] = {
static int
run_driver(const char *driver, const struct row *r, int i)
{
char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024];
char tmpdir[64], src[128], outbin[128], scratch[160], rmcmd[192], cmd[1024];
int result = -1, cleanfail = 0;
snprintf(tmpdir, sizeof tmpdir, "/tmp/slf_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
if (mkdir(tmpdir, 0755) != 0) return -1;
snprintf(src, sizeof src, "%s/slf_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/slf_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
snprintf(scratch, sizeof scratch, "%s.sepwork", outbin);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
fputs(r->src, f);
fclose(f);
if (!f) goto cleanup;
int writefail = fputs(r->src, f) == EOF;
if (fclose(f) != 0) writefail = 1;
if (writefail) goto cleanup;
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
runwait(rmcmd);
return -1;
goto cleanup;
}
int got = runwait(outbin);
runwait(rmcmd);
return got;
result = runwait(outbin);
cleanup:
snprintf(rmcmd, sizeof rmcmd, "rm -rf -- %s", scratch);
if (runwait(rmcmd) != 0) cleanfail = 1;
if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(tmpdir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "row[%s]: temporary cleanup failed\n", r->label);
if (result == r->want) return -1;
}
return result;
}
/* asm_byte_identical — w6c vs w6c_ww .s for the same source must match.
@@ -230,33 +239,35 @@ run_driver(const char *driver, const struct row *r, int i)
static int
asm_byte_identical(const char *bin, const struct row *r, int i)
{
char src[64], cs[64], ws[64], cmd[1024];
snprintf(src, sizeof src, "/tmp/slf_asm_%d_%d.ww", getpid(), i);
snprintf(cs, sizeof cs, "/tmp/slf_asm_%d_%d_c.s", getpid(), i);
snprintf(ws, sizeof ws, "/tmp/slf_asm_%d_%d_w.s", getpid(), i);
char tmpdir[64], src[96], cs[96], ws[96], cmd[1024];
int rc = -1, cleanfail = 0;
snprintf(tmpdir, sizeof tmpdir, "/tmp/slf_asm_%d_%d", getpid(), i);
if (mkdir(tmpdir, 0755) != 0) return -1;
snprintf(src, sizeof src, "%s/t.ww", tmpdir);
snprintf(cs, sizeof cs, "%s/c.s", tmpdir);
snprintf(ws, sizeof ws, "%s/w.s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) return -1;
fputs(r->src, f);
fclose(f);
if (!f) goto cleanup;
int writefail = fputs(r->src, f) == EOF;
if (fclose(f) != 0) writefail = 1;
if (writefail) goto cleanup;
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
unlink(src);
return -1;
goto cleanup;
}
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
bin, ws, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
unlink(src); unlink(cs);
return -1;
goto cleanup;
}
FILE *fc = fopen(cs, "rb");
FILE *fw = fopen(ws, "rb");
int rc = 0;
rc = 0;
if (!fc || !fw) {
rc = -1;
} else {
@@ -272,7 +283,15 @@ asm_byte_identical(const char *bin, const struct row *r, int i)
if (rc != 0)
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
r->label);
unlink(src); unlink(cs); unlink(ws);
cleanup:
if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(cs) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(ws) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(tmpdir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "row[%s]: temporary cleanup failed\n", r->label);
return -1;
}
return rc;
}

View File

@@ -21,6 +21,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -37,65 +38,99 @@ runwait(const char *cmd)
static int
run_pos(const char *driver, const char *fixdir, const char *tag)
{
char td[1024];
char td[1024], bin[1024], sepwork[1100];
snprintf(td, sizeof td, "/tmp/modcol_pos_%d_%s", getpid(), tag);
mkdir(td, 0755);
char rmcmd[1100];
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", td);
if (mkdir(td, 0755) != 0) {
fprintf(stderr, "modtype_leaf[%s]: mkdir %s failed\n", tag, td);
return 1;
}
snprintf(bin, sizeof bin, "%s/pos", td);
snprintf(sepwork, sizeof sepwork, "%s/pos.sepwork", td);
char cmd[2048];
/* cd into the fixture dir so the driver's source-dir-first import
* search resolves `use mod1; use mod2;`; -o moves the binary and its
* .sepwork OUT of the tracked fixture tree into the /tmp tmpdir. */
* search resolves `use mod1; use mod2;`; -o puts the binary and the
* caller-owned .sepwork under the directory acquired above. */
snprintf(cmd, sizeof cmd,
"cd %s && %s build -o %s/pos pos.ww >/dev/null 2>&1",
fixdir, driver, td);
int fail = 0;
if (runwait(cmd) != 0) {
fprintf(stderr, "modtype_leaf[%s]: pos.ww build failed\n", tag);
runwait(rmcmd);
return 1;
fail = 1;
} else {
int got = runwait(bin);
/* 3 + 5 (mod1.stream) + 7 + 11 (mod2.stream) = 26. */
if (got != 26) {
fprintf(stderr,
"modtype_leaf[%s]: pos.ww exit=%d want=26\n", tag, got);
fail = 1;
}
}
char bin[2048];
snprintf(bin, sizeof bin, "%s/pos", td);
int got = runwait(bin);
runwait(rmcmd);
/* 3 + 5 (mod1.stream) + 7 + 11 (mod2.stream) = 26. */
if (got != 26) {
fprintf(stderr,
"modtype_leaf[%s]: pos.ww exit=%d want=26\n", tag, got);
return 1;
snprintf(cmd, sizeof cmd, "rm -rf %s", sepwork);
if (runwait(cmd) != 0) {
fprintf(stderr, "modtype_leaf[%s]: cleanup %s failed\n",
tag, sepwork);
fail = 1;
}
return 0;
if (unlink(bin) != 0 && errno != ENOENT) {
fprintf(stderr, "modtype_leaf[%s]: cleanup %s failed\n", tag, bin);
fail = 1;
}
if (rmdir(td) != 0) {
fprintf(stderr, "modtype_leaf[%s]: cleanup %s failed\n", tag, td);
fail = 1;
}
return fail;
}
static int
run_neg(const char *driver, const char *fixdir, const char *tag)
{
char td[1024];
char td[1024], bin[1024], sepwork[1100];
snprintf(td, sizeof td, "/tmp/modcol_neg_%d_%s", getpid(), tag);
mkdir(td, 0755);
char rmcmd[1100];
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", td);
if (mkdir(td, 0755) != 0) {
fprintf(stderr, "modtype_leaf[%s]: mkdir %s failed\n", tag, td);
return 1;
}
snprintf(bin, sizeof bin, "%s/neg", td);
snprintf(sepwork, sizeof sepwork, "%s/neg.sepwork", td);
char cmd[2048];
/* Build must fail with a field-resolution error: the `c` field
* lives on mod2.stream, not mod1.stream. If pre-fix sym-binding
* smashed mod1.stream's identity into mod2.stream's slot, this
* would silently succeed. -o moves the binary and its .sepwork (the
* scratch dir is created even on the expected build failure) OUT of
* the tracked fixture tree into the /tmp tmpdir. */
* would silently succeed. -o puts the binary and its caller-owned
* .sepwork (created even on the expected build failure) under the
* directory acquired above. */
snprintf(cmd, sizeof cmd,
"cd %s && %s build -o %s/neg neg.ww >/dev/null 2>&1",
fixdir, driver, td);
int rc = runwait(cmd);
runwait(rmcmd);
int fail = 0;
if (rc == 0) {
fprintf(stderr,
"modtype_leaf[%s]: neg.ww built but should have errored\n",
tag);
return 1;
fail = 1;
}
return 0;
snprintf(cmd, sizeof cmd, "rm -rf %s", sepwork);
if (runwait(cmd) != 0) {
fprintf(stderr, "modtype_leaf[%s]: cleanup %s failed\n",
tag, sepwork);
fail = 1;
}
if (unlink(bin) != 0 && errno != ENOENT) {
fprintf(stderr, "modtype_leaf[%s]: cleanup %s failed\n", tag, bin);
fail = 1;
}
if (rmdir(td) != 0) {
fprintf(stderr, "modtype_leaf[%s]: cleanup %s failed\n", tag, td);
fail = 1;
}
return fail;
}
int

View File

@@ -24,6 +24,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -40,29 +41,46 @@ runwait(const char *cmd)
static int
run_pos(const char *driver, const char *fixdir, const char *tag)
{
/* -o into a /tmp tmpdir so the binary AND `<stem>.sepwork/` land
* outside the repo; the cd keeps source-dir-first import search
/* -o puts the binary and caller-owned `<stem>.sepwork/` under the
* acquired directory; the cd keeps source-dir-first import search
* resolving `use mod1; use mod2;`. */
char td[1024];
char td[1024], outbin[1024], sepwork[1100];
snprintf(td, sizeof td, "/tmp/smprefer_%d_%s", getpid(), tag);
mkdir(td, 0755);
char rmcmd[1152];
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", td);
if (mkdir(td, 0755) != 0) {
fprintf(stderr, "samemod_prefer[%s]: mkdir %s failed\n", tag, td);
return 1;
}
snprintf(outbin, sizeof outbin, "%s/pos", td);
snprintf(sepwork, sizeof sepwork, "%s/pos.sepwork", td);
char cmd[2048];
snprintf(cmd, sizeof cmd,
"cd %s && %s build -o %s/pos pos.ww >/dev/null 2>&1",
fixdir, driver, td);
int fail = 0;
if (runwait(cmd) != 0) {
fprintf(stderr,
"samemod_prefer[%s]: pos.ww build failed — bare-leaf "
"`read` lookup likely cross-bound\n", tag);
runwait(rmcmd);
return 1;
fail = 1;
}
runwait(rmcmd);
return 0;
snprintf(cmd, sizeof cmd, "rm -rf %s", sepwork);
if (runwait(cmd) != 0) {
fprintf(stderr, "samemod_prefer[%s]: cleanup %s failed\n",
tag, sepwork);
fail = 1;
}
if (unlink(outbin) != 0 && errno != ENOENT) {
fprintf(stderr, "samemod_prefer[%s]: cleanup %s failed\n",
tag, outbin);
fail = 1;
}
if (rmdir(td) != 0) {
fprintf(stderr, "samemod_prefer[%s]: cleanup %s failed\n", tag, td);
fail = 1;
}
return fail;
}
int

View File

@@ -37,6 +37,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -293,29 +294,60 @@ static const struct row rows[] = {
static int
run_driver(const char *driver, const struct row *r, int i)
{
char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024];
char tmpdir[64], src[128], outbin[128], sepwork[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wcrs_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
if (mkdir(tmpdir, 0755) != 0) {
fprintf(stderr, "row[%s]: mkdir %s failed\n", r->label, tmpdir);
return -1;
}
snprintf(src, sizeof src, "%s/wcrs_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/wcrs_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
snprintf(sepwork, sizeof sepwork, "%s.sepwork", outbin);
int got = -1;
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
if (!f) goto cleanup;
wwtest_fputs(r->src, f);
fclose(f);
int writebad = ferror(f);
if (fclose(f) != 0) writebad = 1;
if (writebad) goto cleanup;
snprintf(cmd, sizeof cmd, "%s build -o %s %s", driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
runwait(rmcmd);
return -1;
goto cleanup;
}
int got = runwait(outbin);
got = runwait(outbin);
runwait(rmcmd);
/* Ordinary build leaves `<output>.sepwork` to its caller. This exact
* tree is beneath tmpdir, whose mkdir above established ownership. */
cleanup:
{
int cleanup_fail = 0;
snprintf(cmd, sizeof cmd, "rm -rf %s", sepwork);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: cleanup %s failed\n",
r->label, sepwork);
cleanup_fail = 1;
}
if (unlink(src) != 0 && errno != ENOENT) {
fprintf(stderr, "row[%s]: cleanup %s failed\n", r->label, src);
cleanup_fail = 1;
}
if (unlink(outbin) != 0 && errno != ENOENT) {
fprintf(stderr, "row[%s]: cleanup %s failed\n",
r->label, outbin);
cleanup_fail = 1;
}
if (rmdir(tmpdir) != 0) {
fprintf(stderr, "row[%s]: cleanup %s failed\n",
r->label, tmpdir);
cleanup_fail = 1;
}
if (cleanup_fail && got == r->want) got = -1;
}
return got;
}
@@ -326,33 +358,39 @@ run_driver(const char *driver, const struct row *r, int i)
static int
asm_byte_identical(const char *bin, const struct row *r, int i)
{
char src[64], cs[64], ws[64], cmd[1024];
snprintf(src, sizeof src, "/tmp/wcrs_asm_%d_%d.ww", getpid(), i);
snprintf(cs, sizeof cs, "/tmp/wcrs_asm_%d_%d_c.s", getpid(), i);
snprintf(ws, sizeof ws, "/tmp/wcrs_asm_%d_%d_w.s", getpid(), i);
char tmpdir[64], src[128], cs[128], ws[128], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wcrs_asm_%d_%d", getpid(), i);
if (mkdir(tmpdir, 0755) != 0) {
fprintf(stderr, "row[%s]: mkdir %s failed\n", r->label, tmpdir);
return -1;
}
snprintf(src, sizeof src, "%s/input.ww", tmpdir);
snprintf(cs, sizeof cs, "%s/cstage.s", tmpdir);
snprintf(ws, sizeof ws, "%s/wwstage.s", tmpdir);
int rc = -1;
FILE *f = fopen(src, "wb");
if (!f) return -1;
if (!f) goto cleanup;
wwtest_fputs(r->src, f);
fclose(f);
int writebad = ferror(f);
if (fclose(f) != 0) writebad = 1;
if (writebad) goto cleanup;
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
unlink(src);
return -1;
goto cleanup;
}
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
bin, ws, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
unlink(src); unlink(cs);
return -1;
goto cleanup;
}
FILE *fc = fopen(cs, "rb");
FILE *fw = fopen(ws, "rb");
int rc = 0;
rc = 0;
if (!fc || !fw) {
rc = -1;
} else {
@@ -368,7 +406,29 @@ asm_byte_identical(const char *bin, const struct row *r, int i)
if (rc != 0)
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
r->label);
unlink(src); unlink(cs); unlink(ws);
cleanup:
{
int cleanup_fail = 0;
if (unlink(src) != 0 && errno != ENOENT) {
fprintf(stderr, "row[%s]: cleanup %s failed\n", r->label, src);
cleanup_fail = 1;
}
if (unlink(cs) != 0 && errno != ENOENT) {
fprintf(stderr, "row[%s]: cleanup %s failed\n", r->label, cs);
cleanup_fail = 1;
}
if (unlink(ws) != 0 && errno != ENOENT) {
fprintf(stderr, "row[%s]: cleanup %s failed\n", r->label, ws);
cleanup_fail = 1;
}
if (rmdir(tmpdir) != 0) {
fprintf(stderr, "row[%s]: cleanup %s failed\n",
r->label, tmpdir);
cleanup_fail = 1;
}
if (cleanup_fail && rc == 0) rc = -1;
}
return rc;
}

View File

@@ -37,6 +37,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -53,20 +54,24 @@ runwait(const char *cmd)
static int
run_row(const char *driver, const char *fixdir, const char *tag)
{
/* Scratch dir OUT of the repo: the driver's sep `<stem>.sepwork/`
* follows the build output stem, so point -o into /tmp so both the
* binary and its .sepwork land there and die with rm -rf. */
/* Keep the output and its caller-owned `<stem>.sepwork/` in one
* invocation-owned directory outside the repository. */
char td[64];
snprintf(td, sizeof td, "/tmp/usepr_%d_%s", getpid(), tag);
mkdir(td, 0755);
char rmcmd[128];
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", td);
if (mkdir(td, 0755) != 0) {
fprintf(stderr, "use_promote_alias[%s]: mkdir %s: %s\n",
tag, td, strerror(errno));
return 1;
}
char src[64];
snprintf(src, sizeof src, "pos_%s.ww", tag);
char bin[2048];
char bin[2048], sepdir[2048], rmcmd[2112];
snprintf(bin, sizeof bin, "%s/out", td);
snprintf(sepdir, sizeof sepdir, "%s.sepwork", bin);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepdir);
char cmd[2048];
int failed = 0, got;
/* cd into the fixture dir so the driver's source-dir-first
* import search resolves `use <tag>mod;`; -o points OUT of the repo. */
snprintf(cmd, sizeof cmd,
@@ -75,17 +80,37 @@ run_row(const char *driver, const char *fixdir, const char *tag)
fprintf(stderr,
"use_promote_alias[%s]: build failed — SK_USE→SK_X "
"promotion likely dropped use_alias\n", tag);
runwait(rmcmd);
return 1;
failed = 1;
goto cleanup;
}
int got = runwait(bin);
runwait(rmcmd);
got = runwait(bin);
if (got != 42) {
fprintf(stderr,
"use_promote_alias[%s]: exit=%d want=42\n", tag, got);
return 1;
failed = 1;
}
return 0;
cleanup:
{
int cleanup_failed = 0;
if (runwait(rmcmd) != 0) {
fprintf(stderr, "use_promote_alias[%s]: remove %s failed\n",
tag, sepdir);
cleanup_failed = 1;
}
if (unlink(bin) != 0 && errno != ENOENT) {
fprintf(stderr, "use_promote_alias[%s]: unlink %s: %s\n",
tag, bin, strerror(errno));
cleanup_failed = 1;
}
if (rmdir(td) != 0) {
fprintf(stderr, "use_promote_alias[%s]: rmdir %s: %s\n",
tag, td, strerror(errno));
cleanup_failed = 1;
}
if (!failed && cleanup_failed) failed = 1;
}
return failed;
}
int

View File

@@ -5,6 +5,8 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -1802,32 +1804,82 @@ main(void)
for (int i = 0; rows[i].src; i++, n++) {
char tmpdir[64];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwe2e_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
if (mkdir(tmpdir, 0755) != 0) {
fprintf(stderr, "row %d: mkdir %s: %s\n",
i, tmpdir, strerror(errno));
fail++;
continue;
}
/* #8: source + binary + .sepwork scratch all live INSIDE tmpdir
* (`-o` pins the binary, scratch lands next to the in-tmpdir
* source) so the recursive cleanup removes everything. */
char src[128], outbin[128], rmcmd[160];
/* Source, output, and the caller-owned output `.sepwork` all live
* inside this invocation-owned directory. */
char src[128], outbin[128], sepdir[160], rmcmd[192];
snprintf(src, sizeof src, "%s/wwe2e_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/wwe2e_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
snprintf(sepdir, sizeof sepdir, "%s.sepwork", outbin);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepdir);
int row_failed = 0;
char cmd[1024];
int got;
FILE *f = fopen(src, "wb");
if (!f) {
fprintf(stderr, "row %d: fopen %s: %s\n",
i, src, strerror(errno));
row_failed = 1;
goto cleanup;
}
wwtest_fputs(rows[i].src, f);
fclose(f);
if (ferror(f)) {
fprintf(stderr, "row %d: write %s failed\n", i, src);
row_failed = 1;
}
if (fclose(f) != 0) {
fprintf(stderr, "row %d: close %s: %s\n",
i, src, strerror(errno));
row_failed = 1;
}
if (row_failed) goto cleanup;
char cmd[1024];
snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s",
bin, outbin, src);
if (runwait(cmd) != 0) { runwait(rmcmd); fail++; continue; }
if (runwait(cmd) != 0) {
row_failed = 1;
goto cleanup;
}
int got = runwait(outbin);
got = runwait(outbin);
if (got != rows[i].want_exit) {
fprintf(stderr, "row %d: exit %d, want %d\n src: %s\n",
i, got, rows[i].want_exit, rows[i].src);
fail++;
row_failed = 1;
}
runwait(rmcmd);
cleanup:
{
int cleanup_failed = 0;
if (runwait(rmcmd) != 0) {
fprintf(stderr, "row %d: remove %s failed\n", i, sepdir);
cleanup_failed = 1;
}
if (unlink(src) != 0 && errno != ENOENT) {
fprintf(stderr, "row %d: unlink %s: %s\n",
i, src, strerror(errno));
cleanup_failed = 1;
}
if (unlink(outbin) != 0 && errno != ENOENT) {
fprintf(stderr, "row %d: unlink %s: %s\n",
i, outbin, strerror(errno));
cleanup_failed = 1;
}
if (rmdir(tmpdir) != 0) {
fprintf(stderr, "row %d: rmdir %s: %s\n",
i, tmpdir, strerror(errno));
cleanup_failed = 1;
}
if (!row_failed && cleanup_failed) row_failed = 1;
}
if (row_failed) fail++;
}
if (fail) { fprintf(stderr, "%d/%d e2e tests failed\n", fail, n); return 1; }
printf("e2e: %d/%d ok\n", n, n);

View File

@@ -45,6 +45,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -61,37 +62,61 @@ runwait(const char *cmd)
static int
run_pos(const char *driver, const char *fixdir, const char *tag)
{
/* -o into a /tmp tmpdir so pos.sepwork follows the OUTPUT stem out
* of the tracked fixture tree; cd <fixdir> stays pos.ww imports
* siblings mod1/mod2 via source-dir-first resolution. */
/* Keep the output and caller-owned pos.sepwork outside the tracked
* fixture tree; cd <fixdir> stays so pos.ww imports siblings mod1/mod2. */
char td[1024];
snprintf(td, sizeof td, "/tmp/fnlbl_%d_%s", getpid(), tag);
mkdir(td, 0755);
char rmcmd[1088];
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", td);
if (mkdir(td, 0755) != 0) {
fprintf(stderr, "fnlabel_mangle[%s]: mkdir %s: %s\n",
tag, td, strerror(errno));
return 1;
}
char bin[2048], sepdir[2048], rmcmd[2112];
snprintf(bin, sizeof bin, "%s/pos", td);
snprintf(sepdir, sizeof sepdir, "%s.sepwork", bin);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepdir);
char cmd[2048];
int failed = 0, got;
snprintf(cmd, sizeof cmd,
"cd %s && %s build -o %s/pos pos.ww >/dev/null 2>&1",
fixdir, driver, td);
if (runwait(cmd) != 0) {
fprintf(stderr,
"fnlabel_mangle[%s]: pos.ww build failed\n", tag);
runwait(rmcmd);
return 1;
failed = 1;
goto cleanup;
}
char bin[2048];
snprintf(bin, sizeof bin, "%s/pos", td);
int got = runwait(bin);
runwait(rmcmd);
got = runwait(bin);
if (got != 112) {
fprintf(stderr,
"fnlabel_mangle[%s]: pos.ww exit=%d want=112 — "
"fn labels likely collapsed at link, or LEAQ-of-fn "
"N_DOT branch missing in cgdot\n", tag, got);
return 1;
failed = 1;
}
return 0;
cleanup:
{
int cleanup_failed = 0;
if (runwait(rmcmd) != 0) {
fprintf(stderr, "fnlabel_mangle[%s]: remove %s failed\n",
tag, sepdir);
cleanup_failed = 1;
}
if (unlink(bin) != 0 && errno != ENOENT) {
fprintf(stderr, "fnlabel_mangle[%s]: unlink %s: %s\n",
tag, bin, strerror(errno));
cleanup_failed = 1;
}
if (rmdir(td) != 0) {
fprintf(stderr, "fnlabel_mangle[%s]: rmdir %s: %s\n",
tag, td, strerror(errno));
cleanup_failed = 1;
}
if (!failed && cleanup_failed) failed = 1;
}
return failed;
}
int

View File

@@ -49,6 +49,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -69,25 +70,51 @@ run_neg(const char *driver, const char *fixdir, const char *tag)
snprintf(src, sizeof src, "neg_%s.ww", tag);
char td[128];
snprintf(td, sizeof td, "/tmp/psm_neg_%d_%s", getpid(), tag);
mkdir(td, 0755);
if (mkdir(td, 0755) != 0) {
fprintf(stderr, "param_shadow_mod[neg_%s]: mkdir %s: %s\n",
tag, td, strerror(errno));
return 1;
}
char out[160], sepdir[192], rmcmd[224];
snprintf(out, sizeof out, "%s/out", td);
snprintf(sepdir, sizeof sepdir, "%s.sepwork", out);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepdir);
char cmd[2048];
/* cd into the fixture dir so the driver's source-dir-first
* import search resolves `use shadowmod;`; -o moves the binary
* and its <stem>.sepwork OUT of the tracked fixture tree. */
* and caller-owned out.sepwork out of the tracked fixture tree. */
snprintf(cmd, sizeof cmd,
"cd %s && %s build -o %s/out %s >/dev/null 2>&1",
fixdir, driver, td, src);
"cd %s && %s build -o %s %s >/dev/null 2>&1",
fixdir, driver, out, src);
int rc = runwait(cmd);
char rmcmd[256];
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", td);
runwait(rmcmd);
int failed = 0;
if (rc == 0) {
fprintf(stderr,
"param_shadow_mod[neg_%s]: build unexpectedly succeeded "
"— shadow rule did not fire\n", tag);
return 1;
failed = 1;
}
return 0;
{
int cleanup_failed = 0;
if (runwait(rmcmd) != 0) {
fprintf(stderr, "param_shadow_mod[neg_%s]: remove %s failed\n",
tag, sepdir);
cleanup_failed = 1;
}
if (unlink(out) != 0 && errno != ENOENT) {
fprintf(stderr, "param_shadow_mod[neg_%s]: unlink %s: %s\n",
tag, out, strerror(errno));
cleanup_failed = 1;
}
if (rmdir(td) != 0) {
fprintf(stderr, "param_shadow_mod[neg_%s]: rmdir %s: %s\n",
tag, td, strerror(errno));
cleanup_failed = 1;
}
if (!failed && cleanup_failed) failed = 1;
}
return failed;
}
static int
@@ -95,32 +122,57 @@ run_pos(const char *driver, const char *fixdir)
{
char td[128];
snprintf(td, sizeof td, "/tmp/psm_pos_%d", getpid());
mkdir(td, 0755);
char rmcmd[256];
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", td);
if (mkdir(td, 0755) != 0) {
fprintf(stderr, "param_shadow_mod[pos_rename]: mkdir %s: %s\n",
td, strerror(errno));
return 1;
}
char out[160], sepdir[192], rmcmd[224];
snprintf(out, sizeof out, "%s/out", td);
snprintf(sepdir, sizeof sepdir, "%s.sepwork", out);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepdir);
char cmd[2048];
/* keep cd for sibling-import resolution; -o moves the binary and
* its pos_rename.sepwork OUT of the tracked fixture tree. */
/* Keep cd for sibling-import resolution; -o moves the binary and
* caller-owned out.sepwork out of the tracked fixture tree. */
snprintf(cmd, sizeof cmd,
"cd %s && %s build -o %s/out pos_rename.ww >/dev/null 2>&1",
fixdir, driver, td);
"cd %s && %s build -o %s pos_rename.ww >/dev/null 2>&1",
fixdir, driver, out);
int failed = 0, got;
if (runwait(cmd) != 0) {
runwait(rmcmd);
fprintf(stderr,
"param_shadow_mod[pos_rename]: build failed — rule "
"over-triggered on the rename\n");
return 1;
failed = 1;
goto cleanup;
}
char bin[2048];
snprintf(bin, sizeof bin, "%s/out", td);
int got = runwait(bin);
runwait(rmcmd);
got = runwait(out);
if (got != 42) {
fprintf(stderr,
"param_shadow_mod[pos_rename]: exit=%d want=42\n", got);
return 1;
failed = 1;
}
return 0;
cleanup:
{
int cleanup_failed = 0;
if (runwait(rmcmd) != 0) {
fprintf(stderr, "param_shadow_mod[pos_rename]: remove %s failed\n",
sepdir);
cleanup_failed = 1;
}
if (unlink(out) != 0 && errno != ENOENT) {
fprintf(stderr, "param_shadow_mod[pos_rename]: unlink %s: %s\n",
out, strerror(errno));
cleanup_failed = 1;
}
if (rmdir(td) != 0) {
fprintf(stderr, "param_shadow_mod[pos_rename]: rmdir %s: %s\n",
td, strerror(errno));
cleanup_failed = 1;
}
if (!failed && cleanup_failed) failed = 1;
}
return failed;
}
/*
@@ -134,34 +186,61 @@ run_pos(const char *driver, const char *fixdir)
static int
run_neg_selfimp(const char *comp, const char *fixdir, const char *tag)
{
char errp[96], cmd[2048];
snprintf(errp, sizeof errp, "/tmp/psm_selfimp_%s_%d.err", tag, getpid());
char td[128];
snprintf(td, sizeof td, "/tmp/psm_selfimp_%d_%s", getpid(), tag);
if (mkdir(td, 0755) != 0) {
fprintf(stderr, "param_shadow_mod[neg_selfimp-%s]: mkdir %s: %s\n",
tag, td, strerror(errno));
return 1;
}
char errp[160], cmd[2048];
snprintf(errp, sizeof errp, "%s/stderr", td);
snprintf(cmd, sizeof cmd,
"cd %s && %s selfimp/selfimptest.ww -o /dev/null 2>%s",
fixdir, comp, errp);
int rc = runwait(cmd);
int failed = 0, found = 0;
FILE *f;
if (rc == 0) {
fprintf(stderr, "param_shadow_mod[neg_selfimp-%s]: accepted "
"self-import (expected reject)\n", tag);
unlink(errp);
return 1;
failed = 1;
goto cleanup;
}
FILE *f = fopen(errp, "rb");
int found = 0;
f = fopen(errp, "rb");
if (f) {
char buf[4096];
size_t n = fread(buf, 1, sizeof buf - 1, f);
fclose(f);
buf[n] = '\0';
found = strstr(buf, "self-import") != NULL;
if (!ferror(f)) {
buf[n] = '\0';
found = strstr(buf, "self-import") != NULL;
}
if (fclose(f) != 0) found = 0;
}
unlink(errp);
if (!found) {
fprintf(stderr, "param_shadow_mod[neg_selfimp-%s]: rejected "
"but no 'self-import' on stderr\n", tag);
return 1;
failed = 1;
}
return 0;
cleanup:
{
int cleanup_failed = 0;
if (unlink(errp) != 0 && errno != ENOENT) {
fprintf(stderr,
"param_shadow_mod[neg_selfimp-%s]: unlink %s: %s\n",
tag, errp, strerror(errno));
cleanup_failed = 1;
}
if (rmdir(td) != 0) {
fprintf(stderr,
"param_shadow_mod[neg_selfimp-%s]: rmdir %s: %s\n",
tag, td, strerror(errno));
cleanup_failed = 1;
}
if (!failed && cleanup_failed) failed = 1;
}
return failed;
}
/*
@@ -177,32 +256,57 @@ run_pos_crossmod(const char *driver, const char *fixdir)
{
char td[128];
snprintf(td, sizeof td, "/tmp/psm_crossmod_%d", getpid());
mkdir(td, 0755);
char rmcmd[256];
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", td);
if (mkdir(td, 0755) != 0) {
fprintf(stderr, "param_shadow_mod[pos_crossmod]: mkdir %s: %s\n",
td, strerror(errno));
return 1;
}
char out[160], sepdir[192], rmcmd[224];
snprintf(out, sizeof out, "%s/out", td);
snprintf(sepdir, sizeof sepdir, "%s.sepwork", out);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepdir);
char cmd[2048];
/* keep cd for sibling-import resolution; -o moves the binary and
* its pos_crossmod.sepwork OUT of the tracked fixture tree. */
/* Keep cd for sibling-import resolution; -o moves the binary and
* caller-owned out.sepwork out of the tracked fixture tree. */
snprintf(cmd, sizeof cmd,
"cd %s && %s build -o %s/out pos_crossmod.ww >/dev/null 2>&1",
fixdir, driver, td);
"cd %s && %s build -o %s pos_crossmod.ww >/dev/null 2>&1",
fixdir, driver, out);
int failed = 0, got;
if (runwait(cmd) != 0) {
runwait(rmcmd);
fprintf(stderr,
"param_shadow_mod[pos_crossmod]: build failed — shadow "
"rule over-triggered on a cross-module param\n");
return 1;
failed = 1;
goto cleanup;
}
char bin[2048];
snprintf(bin, sizeof bin, "%s/out", td);
int got = runwait(bin);
runwait(rmcmd);
got = runwait(out);
if (got != 2) {
fprintf(stderr,
"param_shadow_mod[pos_crossmod]: exit=%d want=2\n", got);
return 1;
failed = 1;
}
return 0;
cleanup:
{
int cleanup_failed = 0;
if (runwait(rmcmd) != 0) {
fprintf(stderr, "param_shadow_mod[pos_crossmod]: remove %s failed\n",
sepdir);
cleanup_failed = 1;
}
if (unlink(out) != 0 && errno != ENOENT) {
fprintf(stderr, "param_shadow_mod[pos_crossmod]: unlink %s: %s\n",
out, strerror(errno));
cleanup_failed = 1;
}
if (rmdir(td) != 0) {
fprintf(stderr, "param_shadow_mod[pos_crossmod]: rmdir %s: %s\n",
td, strerror(errno));
cleanup_failed = 1;
}
if (!failed && cleanup_failed) failed = 1;
}
return failed;
}
int

View File

@@ -41,15 +41,15 @@
* neg_forrange_tuple_dup | build fails | site 1505
* neg_param_dup | build fails | site 1914
* neg_toplet_dup | build fails | site 1880
* pos_nested_block_shadow | exit=1 | scope boundary
* pos_nested_shadow_typed | exit=2 | (name,type) — bucket
* | | keys by name only
* pos_forrange_body_shadow | exit=99 | for body N_BLOCK
* | | opens a fresh scope
* pos_mcase_per_arm | exit=7 | match arm scope
* The four legal-shadow positive rows are owned by the directive fixtures
* under test/wcc/data/r71_redecl_*. This residual wrapper intentionally owns
* only the six C-stage-only rejects below: wwstage lacks per-block scoping,
* so these cannot be represented by the symmetric //ww:error contract.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -64,167 +64,128 @@ runwait(const char *cmd)
return -1;
}
/*
* kind == 0: negative — build must fail (any nonzero exit).
* kind == 1: positive — build must succeed AND binary exits with `want`.
*/
struct row { const char *label; int kind; const char *src; int want; };
struct row { const char *label; const char *src; };
static const struct row rows[] = {
/* neg: site 1443 — block-body let dup. */
{ "neg_let_same_block", 0,
{ "neg_let_same_block",
"fn main() i32 = {\n"
" let a: i32 = 1;\n"
" let a: i32 = 2;\n"
" return a;\n"
"};\n",
0 },
"};\n" },
/* neg: site 1562 — mlet shadows earlier same-block let. */
{ "neg_mlet_same_block", 0,
{ "neg_mlet_same_block",
"fn pair() (i32, i32) = { return (10, 20); };\n"
"fn main() i32 = {\n"
" let a: i32 = 1;\n"
" let (a, b) = pair();\n"
" return a + b;\n"
"};\n",
0 },
"};\n" },
/* neg: site 1562 — mlet pattern lists the same name twice. */
{ "neg_mlet_tuple_dup", 0,
{ "neg_mlet_tuple_dup",
"fn pair() (i32, i32) = { return (10, 20); };\n"
"fn main() i32 = {\n"
" let (a, a) = pair();\n"
" return a;\n"
"};\n",
0 },
"};\n" },
/* neg: site 1505 — forrange tuple-pattern lists same name twice. */
{ "neg_forrange_tuple_dup", 0,
{ "neg_forrange_tuple_dup",
"fn main() i32 = {\n"
" let xs: [1](i32, i32) = [(10i32, 20i32)];\n"
" for (let (a, a) .. xs) {\n"
" return a;\n"
" };\n"
" return -1;\n"
"};\n",
0 },
"};\n" },
/* neg: site 1914 — two params with the same name. */
{ "neg_param_dup", 0,
{ "neg_param_dup",
"fn f(a: i32, a: i32) i32 = { return a; };\n"
"fn main() i32 = { return f(1, 2); };\n",
0 },
"fn main() i32 = { return f(1, 2); };\n" },
/* neg: site 1880 — top-level let dup. */
{ "neg_toplet_dup", 0,
{ "neg_toplet_dup",
"let x: i32 = 1;\n"
"let x: i32 = 2;\n"
"fn main() i32 = { return x; };\n",
0 },
/* pos: nested-block shadow stays legal. Inner block opens a
* fresh scope, scope_define's per-scope hash bucket isolates the
* inner `a` from the outer one. Outer `a` untouched after the
* inner block exits. */
{ "pos_nested_block_shadow", 1,
"fn main() i32 = {\n"
" let a: i32 = 1;\n"
" {\n"
" let a: i32 = 99;\n"
" };\n"
" return a;\n"
"};\n",
1 },
/* pos: nested-block shadow with a different type. Confirms the
* scope-bucket key is the name alone, not (name, type) — the
* inner `s: str` does NOT collide with the outer `s: i32`. */
{ "pos_nested_shadow_typed", 1,
"fn main() i32 = {\n"
" let s: i32 = 2;\n"
" {\n"
" let s: str = \"hi\";\n"
" let _ = s;\n"
" };\n"
" return s;\n"
"};\n",
2 },
/* pos: for-loop body N_BLOCK opens a fresh scope, so a `let x`
* inside the body doesn't collide with the for-range's binding
* `x` (which lives in the per-loop scope, one level above). */
{ "pos_forrange_body_shadow", 1,
"fn main() i32 = {\n"
" let arr: [1]i32 = [42i32];\n"
" let r: i32 = 0;\n"
" for (let x .. arr) {\n"
" let x: i32 = 99;\n"
" r = x;\n"
" };\n"
" return r;\n"
"};\n",
99 },
/* pos: each match arm is its own scope, so two arms each
* binding `v` don't collide. Pre-#32 this was already legal
* (newscope wrapper at check.c:1186); pinned here so a future
* scope-tightening can't quietly remove that wrapper. */
{ "pos_mcase_per_arm", 1,
"type T = (i32 | f64);\n"
"fn main() i32 = {\n"
" let t: T = 7i32;\n"
" match (t) {\n"
" case let v: i32 => { return v; };\n"
" case let v: f64 => { return -1; };\n"
" };\n"
"};\n",
7 },
"fn main() i32 = { return x; };\n" },
};
static int
run_row(const char *driver, const struct row *r, int i)
{
char src[128], tmpdir[128], outbin[128], rmcmd[160], cmd[2048];
char src[128], tmpdir[128], outbin[128], sepdir[160], rmcmd[192];
char cmd[2048];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wcredecl_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
if (mkdir(tmpdir, 0755) != 0) {
fprintf(stderr, "redecl[%s]: mkdir %s: %s\n",
r->label, tmpdir, strerror(errno));
return -1;
}
snprintf(src, sizeof src, "%s/wcredecl_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/wcredecl_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
snprintf(sepdir, sizeof sepdir, "%s.sepwork", outbin);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepdir);
int failed = 0, rc;
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
if (!f) {
fprintf(stderr, "redecl[%s]: fopen %s: %s\n",
r->label, src, strerror(errno));
failed = 1;
goto cleanup;
}
wwtest_fputs(r->src, f);
fclose(f);
if (ferror(f)) {
fprintf(stderr, "redecl[%s]: write %s failed\n", r->label, src);
failed = 1;
}
if (fclose(f) != 0) {
fprintf(stderr, "redecl[%s]: close %s: %s\n",
r->label, src, strerror(errno));
failed = 1;
}
if (failed) goto cleanup;
snprintf(cmd, sizeof cmd, "%s build -o %s %s >/dev/null 2>&1",
driver, outbin, src);
int rc = runwait(cmd);
rc = runwait(cmd);
if (r->kind == 0) {
/* Negative — build must fail. */
if (rc == 0)
fprintf(stderr,
"redecl[%s]: build unexpectedly succeeded\n",
r->label);
runwait(rmcmd);
return rc == 0 ? -1 : 0;
if (rc == 0) {
fprintf(stderr, "redecl[%s]: build unexpectedly succeeded\n",
r->label);
failed = 1;
}
/* Positive — build then run. */
if (rc != 0) {
fprintf(stderr, "redecl[%s]: build failed\n", r->label);
runwait(rmcmd);
return -1;
cleanup:
{
int cleanup_failed = 0;
if (runwait(rmcmd) != 0) {
fprintf(stderr, "redecl[%s]: remove %s failed\n",
r->label, sepdir);
cleanup_failed = 1;
}
if (unlink(src) != 0 && errno != ENOENT) {
fprintf(stderr, "redecl[%s]: unlink %s: %s\n",
r->label, src, strerror(errno));
cleanup_failed = 1;
}
if (unlink(outbin) != 0 && errno != ENOENT) {
fprintf(stderr, "redecl[%s]: unlink %s: %s\n",
r->label, outbin, strerror(errno));
cleanup_failed = 1;
}
if (rmdir(tmpdir) != 0) {
fprintf(stderr, "redecl[%s]: rmdir %s: %s\n",
r->label, tmpdir, strerror(errno));
cleanup_failed = 1;
}
if (!failed && cleanup_failed) failed = 1;
}
int got = runwait(outbin);
runwait(rmcmd);
if (got != r->want) {
fprintf(stderr, "redecl[%s]: exit=%d want=%d\n",
r->label, got, r->want);
return -1;
}
return 0;
return failed ? -1 : 0;
}
int

View File

@@ -63,7 +63,6 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include "wwtestpkg.h"
@@ -77,9 +76,9 @@ runwait(const char *cmd)
}
struct row {
/* Runtime ownership: test/wcc/data/r718_struct_return_*. */
const char *label;
const char *src;
int want;
int frame; /* expected `TEXT f,$N` — 0 = don't check */
int max_off; /* assert no `-K(BP)` with K > max_off — 0 = don't check */
int check_byte_id; /* 1 = assert cstage-vs-wwstage asm byte-identical */
@@ -112,7 +111,6 @@ static const struct row rows[] = {
" if (q.nsec != 23i64) { return 4; };\n"
" return 0;\n"
"};\n",
0,
/* frame: i(16) + x(8) + r(16) + retscr(24) = 64 */
64,
64,
@@ -152,7 +150,6 @@ static const struct row rows[] = {
" if (c.nsec != 20i64) { return 6; };\n"
" return 0;\n"
"};\n",
0,
/* frame: i(16) + k(8) + r(16) + retscr(24) = 64 */
64,
64,
@@ -181,7 +178,6 @@ static const struct row rows[] = {
" if (r.nsec != 201i64) { return 2; };\n"
" return 0;\n"
"};\n",
0,
/* frame: i(16) + r(16) + retscr(24) = 56 -> aligned to 64 */
64,
64,
@@ -209,7 +205,6 @@ static const struct row rows[] = {
" };\n"
" return 0;\n"
"};\n",
0,
/* Frame/max_off depend on the tagged ABI shape; pin byte-id
* only, not specific values. The negative-offset bound here
* still catches a stomp regression because byte-identity
@@ -221,36 +216,6 @@ static const struct row rows[] = {
1 },
};
static int
run_driver(const char *driver, const struct row *r, int i)
{
char src[128], tmpdir[64], outbin[128], rmcmd[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wcsmr_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/wcsmr_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/wcsmr_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
wwtest_fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s build -o %s %s",
driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
runwait(rmcmd);
return -1;
}
int got = runwait(outbin);
runwait(rmcmd);
return got;
}
/* Scan asm output for the `TEXT f,$N` line; return N (or -1 if not
* found). Tolerates an optional MODULE-mangled prefix. */
static int
@@ -391,41 +356,12 @@ main(void)
bin = absbin;
}
char cdrv[1024];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
char wdrv[1024];
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
struct { const char *name; const char *path; int gated_on_existence; }
drivers[] = {
{ "cstage", cdrv, 0 },
{ "wwstage", wdrv, 1 },
{ NULL, NULL, 0 },
};
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
for (int d = 0; drivers[d].name; d++) {
if (drivers[d].gated_on_existence
&& access(drivers[d].path, X_OK) != 0) {
fprintf(stderr, "struct_multi_return_scratch: skip %s (no %s)\n",
drivers[d].name, drivers[d].path);
continue;
}
for (int i = 0; i < n; i++) {
int got = run_driver(drivers[d].path, &rows[i], i);
total++;
if (got != rows[i].want) {
fprintf(stderr,
"struct_multi_return_scratch[%s][%s]: exit=%d want=%d\n",
drivers[d].name, rows[i].label,
got, rows[i].want);
fail++;
}
}
}
if (access(wdrv, X_OK) == 0) {
for (int i = 0; i < n; i++) {
total++;

View File

@@ -42,7 +42,6 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include "wwtestpkg.h"
@@ -57,7 +56,8 @@ runwait(const char *cmd)
/* ---- 1. same-module EXEC rows (compile+link+run) -------------------- */
struct row { const char *label; const char *src; int want; };
/* Runtime ownership: test/wcc/data/r732_def_{sibling,chain,shift,widening}_*. */
struct row { const char *label; const char *src; };
static const struct row exec_rows[] = {
/* Bare sibling reference: `def B = A;`. Pre-#88 the rhs (N_IDENT)
@@ -69,8 +69,7 @@ static const struct row exec_rows[] = {
"\tlet x: i32 = B;\n"
"\tif (x == 7) { return 42; };\n"
"\treturn 1;\n"
"};\n",
42 },
"};\n" },
/* Sibling + arithmetic: the headline `def B = A + 1;`. */
{ "sibling-add",
@@ -80,8 +79,7 @@ static const struct row exec_rows[] = {
"\tlet x: i32 = B;\n"
"\tif (x == 6) { return 42; };\n"
"\treturn 1;\n"
"};\n",
42 },
"};\n" },
/* def -> def -> def chain: C resolves through B through A. Pins
* the recursive resolve (and that the depth guard doesn't trip on
@@ -94,8 +92,7 @@ static const struct row exec_rows[] = {
"\tlet x: i32 = C;\n"
"\tif (x == 30) { return 42; };\n"
"\treturn 1;\n"
"};\n",
42 },
"};\n" },
/* Pure arithmetic with a shift: `(1 << 7) - 1` == 127. Exercises
* the fold_binop shift + subtract path off any sibling ref. */
@@ -105,8 +102,7 @@ static const struct row exec_rows[] = {
"\tlet x: i32 = MASK;\n"
"\tif (x == 127) { return 42; };\n"
"\treturn 1;\n"
"};\n",
42 },
"};\n" },
/* Widening cast over a sibling ref: `A: i64` where A: i32 = 5.
* The N_CAST is stripped (i32->i64 widens, value fits), so W folds
@@ -118,42 +114,9 @@ static const struct row exec_rows[] = {
"\tlet x: i64 = W;\n"
"\tif (x == 5i64) { return 42; };\n"
"\treturn 1;\n"
"};\n",
42 },
"};\n" },
};
static int
run_driver(const char *driver, const struct row *r, int i)
{
char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/dcf_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/dcf_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/dcf_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
wwtest_fputs(r->src, f);
fclose(f);
/* timeout 180 per repo convention (990-997); test/run does not bound
* individual binaries, so an unguarded hang would stall make test. */
snprintf(cmd, sizeof cmd, "timeout 180 %s build -o %s %s 2>/dev/null",
driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
runwait(rmcmd);
return -1;
}
int got = runwait(outbin);
runwait(rmcmd);
return got;
}
/* ---- shared .s emit + slurp ----------------------------------------- */
static int
@@ -282,13 +245,8 @@ xmod_fold_present(const char *w6c, const char *stage)
struct failrow { const char *label; const char *src; };
static const struct failrow fail_rows[] = {
/* Same-module cycle: the depth guard must trip and fail loud, not
* hang and not silently drop the row. */
{ "same-module-cycle",
"def A: i32 = B;\n"
"def B: i32 = A;\n"
"export fn main() i32 = { return A; };\n" },
/* Residual combined-source artifact: cannot use the ordinary
* single-package //ww:error carrier. */
/* Cross-module cycle (combined source): a.J -> b.K -> a.J. */
{ "cross-module-cycle",
"package a;\n"
@@ -298,12 +256,6 @@ static const struct failrow fail_rows[] = {
"import a;\n"
"export def K: i32 = a.J;\n"
"export fn main() i32 = { return 0; };\n" },
/* Narrowing cast that loses the value: 0x1FF (511) does not fit a
* u8, so the cast must fail loud rather than silently truncate. */
{ "narrowing-cast",
"def Z: u8 = 0x1FF: u8;\n"
"export fn main() i32 = { let z: u8 = Z; return z: i32; };\n" },
};
/* Compile via w6c only (no link/run); success means the build FAILED as
@@ -355,42 +307,14 @@ main(void)
bin = absbin;
}
char cdrv[1024], wdrv[1024], w6c[1024], w6c_ww[1024];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
char w6c[1024], w6c_ww[1024];
snprintf(w6c, sizeof w6c, "%s/w6c", bin);
snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin);
int have_ww = (access(w6c_ww, X_OK) == 0);
int total = 0, fail = 0;
/* 1. same-module exec via cstage `ww` (+ wwstage `ww_ww` if built) */
struct { const char *name; const char *path; int gated; }
drivers[] = {
{ "cstage", cdrv, 0 },
{ "wwstage", wdrv, 1 },
{ NULL, NULL, 0 },
};
int nexec = (int)(sizeof exec_rows / sizeof exec_rows[0]);
for (int d = 0; drivers[d].name; d++) {
if (drivers[d].gated && access(drivers[d].path, X_OK) != 0) {
fprintf(stderr, "def_const_fold: skip %s (no %s)\n",
drivers[d].name, drivers[d].path);
continue;
}
for (int i = 0; i < nexec; i++) {
int got = run_driver(drivers[d].path, &exec_rows[i], i);
total++;
if (got != exec_rows[i].want) {
fprintf(stderr,
"def_const_fold[%s][%s]: exit=%d want=%d\n",
drivers[d].name, exec_rows[i].label,
got, exec_rows[i].want);
fail++;
}
}
}
/* 2. byte-id on every exec row (only when wwstage is built) */
if (have_ww) {
for (int i = 0; i < nexec; i++) {

View File

@@ -1,14 +1,14 @@
/*
* 737_direnum — driver-level sentinel for task #22 directory-as-
* module enumeration. Two row families pinning the contract:
* 737_direnum — driver-level sentinel for directory-package loading.
* Existing imported-directory fixtures plus a private dynamic table pin:
*
* ok — multi-file dir is concatenated by both stages; the entry
* reads cross-pkg bare-leaf fns from sibling files. Build
* must succeed for both C-built `ww` and ww-built `ww_ww`.
* bad — multi-file dir with mismatched `package <name>;` decls
* triggers the strict-same-package error (task #25 subset
* bundled with #22 because the failure mode is dir-enum's
* own; cross-stage symmetric).
* bad — multi-file dirs with conflicting `package <name>;` clauses;
* root — direct multi-file directory builds, deterministic source order,
* missing/invalid clauses, and root conflicts;
* leaf — an imported directory with one consistent but wrong package name.
*
* Asm-presence isn't checked separately — 968_utf8_run, 966_strings_
* run, 995_self_rebuild already exercise dir-enum end-to-end at
@@ -18,6 +18,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -43,6 +44,49 @@ stderr_contains(const char *path, const char *needle)
return strstr(buf, needle) != NULL;
}
static int
write_file(const char *path, const char *body)
{
FILE *f = fopen(path, "wb");
if (!f) return -1;
int failed = fputs(body, f) == EOF;
if (fclose(f) != 0) failed = 1;
return failed ? -1 : 0;
}
static int
files_equal(const char *a, const char *b)
{
FILE *fa = fopen(a, "rb"), *fb = fopen(b, "rb");
if (!fa || !fb) {
if (fa) fclose(fa);
if (fb) fclose(fb);
return 0;
}
int ca, cb;
do {
ca = fgetc(fa);
cb = fgetc(fb);
} while (ca == cb && ca != EOF);
fclose(fa);
fclose(fb);
return ca == cb;
}
static int
file_orders(const char *path, const char *first, const char *second)
{
FILE *f = fopen(path, "rb");
if (!f) return 0;
char buf[16384];
size_t n = fread(buf, 1, sizeof buf - 1, f);
fclose(f);
buf[n] = '\0';
char *a = strstr(buf, first);
char *b = strstr(buf, second);
return a != NULL && b != NULL && a < b;
}
int
main(void)
{
@@ -61,11 +105,21 @@ main(void)
snprintf(ww_ww, sizeof ww_ww, "%s/ww_ww", bin);
int total = 0, fail = 0;
char errp[64];
int have_owned = 0, have_rootok = 0, have_rootbad = 0;
int have_importbad = 0, have_wanted = 0;
int have_missing = 0, have_invalid = 0;
char owner[128];
snprintf(owner, sizeof owner, "/tmp/direnum_owner_%d", getpid());
if (mkdir(owner, 0755) != 0) {
fprintf(stderr, "737[setup]: cannot acquire temp directory: %s\n",
strerror(errno));
return 1;
}
char errp[512];
/* ok: cross-pkg bare-leaf via dir-enum, both stages. `ww run` routes
* its scratch through /tmp/ww_run_<pid>.sepwork and removes it itself
* (do_run keepscratch 0), so no per-invocation tmpdir is needed here. */
/* ok: cross-pkg bare-leaf via dir-enum, both stages. `ww run` owns
* /tmp/ww_run_<pid>/{main,main.sepwork} and removes both itself, so no
* per-invocation build directory is needed here. */
{
const char *src = "test/wcc/data/direnum/entry.ww";
char cmd[2048];
@@ -88,114 +142,365 @@ main(void)
}
}
/* bad: mismatched package decls in same dir → import-path mismatch
* error. E3-C1: the amalgamator's strict-same-package "differs from"
* check is deleted; under sep w6c rejects the composed unit with
* "package <p> does not match import path <i>" (wwstage is terser per
* #68, so match the shared "does not match import path" substring).
* Both stages must surface it in stderr. */
/* bad: the package loader validates the complete stored source list
* conflict-first, before checking the import-path leaf. Both stages must
* surface the same directory-level conflict. */
{
const char *src = "test/wcc/data/direnum/bad_entry.ww";
snprintf(errp, sizeof errp, "/tmp/direnum_%d_bad.err", getpid());
/* CLASS B: build INPUT stays the in-repo fixture, but -o points the
* output stem (and thus its <stem>.sepwork scratch) at a unique /tmp
* tmpdir so nothing leaks into the tracked tree — even on the
* expected build failure, since scratch is mkdir'd before the reject.
* rm -rf the tmpdir on every exit path. */
char td[64], rmcmd[128];
/* Build INPUT stays in-repo. Output, diagnostic, and the retained
* exact <stem>.sepwork tree stay beneath this invocation's root. */
char out[512];
char cmd[2048];
snprintf(td, sizeof td, "/tmp/direnum_%d_bad_c", getpid());
mkdir(td, 0755);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", td);
snprintf(cmd, sizeof cmd, "%s build -o %s/out %s 2>%s >/dev/null",
ww, td, src, errp);
snprintf(out, sizeof out, "%s/bad-c", owner);
snprintf(errp, sizeof errp, "%s/bad-c.err", owner);
snprintf(cmd, sizeof cmd, "%s build -o '%s' %s 2>'%s' >/dev/null",
ww, out, src, errp);
int rc = runwait(cmd);
runwait(rmcmd);
total++;
if (rc == 0) {
fprintf(stderr, "737[bad-cstage]: expected build failure, succeeded\n");
fail++;
} else if (!stderr_contains(errp, "does not match import path")) {
fprintf(stderr, "737[bad-cstage]: stderr missing 'does not match import path'\n");
} else if (!stderr_contains(errp, "conflicting package names")) {
fprintf(stderr, "737[bad-cstage]: stderr missing 'conflicting package names'\n");
fail++;
}
unlink(errp);
if (access(ww_ww, X_OK) == 0) {
snprintf(td, sizeof td, "/tmp/direnum_%d_bad_w", getpid());
mkdir(td, 0755);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", td);
snprintf(cmd, sizeof cmd, "%s build -o %s/out %s 2>%s >/dev/null",
ww_ww, td, src, errp);
snprintf(out, sizeof out, "%s/bad-w", owner);
snprintf(errp, sizeof errp, "%s/bad-w.err", owner);
snprintf(cmd, sizeof cmd,
"%s build -o '%s' %s 2>'%s' >/dev/null",
ww_ww, out, src, errp);
rc = runwait(cmd);
runwait(rmcmd);
total++;
if (rc == 0) {
fprintf(stderr, "737[bad-wwstage]: expected build failure, succeeded\n");
fail++;
} else if (!stderr_contains(errp, "does not match import path")) {
fprintf(stderr, "737[bad-wwstage]: stderr missing 'does not match import path'\n");
} else if (!stderr_contains(errp, "conflicting package names")) {
fprintf(stderr, "737[bad-wwstage]: stderr missing 'conflicting package names'\n");
fail++;
}
unlink(errp);
}
}
/* bad-deep: same mismatch, but one file's `package` decl sits behind
* a >2048-byte comment header. Pins the PREP-peek whole-file read
* (#16): the old 2048-capped peek missed that decl, so the strict-
* same-package check skipped the file and the mismatch went
* undetected (build wrongly succeeded); the whole-file peek finds it
* → "does not match import path". Both stages. (reviewer-batcha pin.) */
/* bad-deep: same conflict, but one clause sits behind a >2048-byte
* comment header. The loader's one whole-source header/import read must
* still validate it in both stages. */
{
const char *src = "test/wcc/data/direnum/bad_deep_entry.ww";
snprintf(errp, sizeof errp, "/tmp/direnum_%d_baddeep.err", getpid());
/* CLASS B (see bad block): -o into a unique /tmp tmpdir keeps the
* <stem>.sepwork scratch out of the tracked tree on the expected
* reject; rm -rf on every exit path. */
char td[64], rmcmd[128];
/* Same owned-root layout as the shallow conflict above. */
char out[512];
char cmd[2048];
snprintf(td, sizeof td, "/tmp/direnum_%d_baddeep_c", getpid());
mkdir(td, 0755);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", td);
snprintf(cmd, sizeof cmd, "%s build -o %s/out %s 2>%s >/dev/null",
ww, td, src, errp);
snprintf(out, sizeof out, "%s/baddeep-c", owner);
snprintf(errp, sizeof errp, "%s/baddeep-c.err", owner);
snprintf(cmd, sizeof cmd, "%s build -o '%s' %s 2>'%s' >/dev/null",
ww, out, src, errp);
int rc = runwait(cmd);
runwait(rmcmd);
total++;
if (rc == 0) {
fprintf(stderr, "737[bad-deep-cstage]: expected build failure, succeeded\n");
fail++;
} else if (!stderr_contains(errp, "does not match import path")) {
fprintf(stderr, "737[bad-deep-cstage]: stderr missing 'does not match import path'\n");
} else if (!stderr_contains(errp, "conflicting package names")) {
fprintf(stderr, "737[bad-deep-cstage]: stderr missing 'conflicting package names'\n");
fail++;
}
unlink(errp);
if (access(ww_ww, X_OK) == 0) {
snprintf(td, sizeof td, "/tmp/direnum_%d_baddeep_w", getpid());
mkdir(td, 0755);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", td);
snprintf(cmd, sizeof cmd, "%s build -o %s/out %s 2>%s >/dev/null",
ww_ww, td, src, errp);
snprintf(out, sizeof out, "%s/baddeep-w", owner);
snprintf(errp, sizeof errp, "%s/baddeep-w.err", owner);
snprintf(cmd, sizeof cmd,
"%s build -o '%s' %s 2>'%s' >/dev/null",
ww_ww, out, src, errp);
rc = runwait(cmd);
runwait(rmcmd);
total++;
if (rc == 0) {
fprintf(stderr, "737[bad-deep-wwstage]: expected build failure, succeeded\n");
fail++;
} else if (!stderr_contains(errp, "does not match import path")) {
fprintf(stderr, "737[bad-deep-wwstage]: stderr missing 'does not match import path'\n");
} else if (!stderr_contains(errp, "conflicting package names")) {
fprintf(stderr, "737[bad-deep-wwstage]: stderr missing 'conflicting package names'\n");
fail++;
}
unlink(errp);
}
}
/* Authoritative Package rows. All inputs, outputs, and retained build
* scratch live beneath the invocation-owned root and are removed below. */
{
char td[512], rootok[512], rootbad[512], importbad[512];
char wanted[512], missing[512], invalid[512];
char path[1024], cmd[4096];
snprintf(td, sizeof td, "%s/owned", owner);
snprintf(rootok, sizeof rootok, "%s/rootok", td);
snprintf(rootbad, sizeof rootbad, "%s/rootbad", td);
snprintf(importbad, sizeof importbad, "%s/importbad", td);
snprintf(wanted, sizeof wanted, "%s/wanted", importbad);
snprintf(missing, sizeof missing, "%s/missing", td);
snprintf(invalid, sizeof invalid, "%s/invalid", td);
int setup = 0;
if (mkdir(td, 0755) != 0) setup = 1;
else have_owned = 1;
if (!setup && mkdir(rootok, 0755) != 0) setup = 1;
else if (!setup) have_rootok = 1;
if (!setup && mkdir(rootbad, 0755) != 0) setup = 1;
else if (!setup) have_rootbad = 1;
if (!setup && mkdir(importbad, 0755) != 0) setup = 1;
else if (!setup) have_importbad = 1;
if (!setup && mkdir(wanted, 0755) != 0) setup = 1;
else if (!setup) have_wanted = 1;
if (!setup && mkdir(missing, 0755) != 0) setup = 1;
else if (!setup) have_missing = 1;
if (!setup && mkdir(invalid, 0755) != 0) setup = 1;
else if (!setup) have_invalid = 1;
if (!setup) snprintf(path, sizeof path, "%s/z.ww", rootok);
if (!setup) setup = write_file(path,
"package rootok;\n// ORDER-Z\n"
"fn value() i32 = { return 17; };\n");
if (!setup) snprintf(path, sizeof path, "%s/a.ww", rootok);
if (!setup) setup = write_file(path,
"// leading comment is part of the loader grammar\n"
"package rootok;\n// ORDER-A\n"
"fn main() i32 = { return value(); };\n");
if (!setup) snprintf(path, sizeof path, "%s/a.ww", rootbad);
if (!setup) setup = write_file(path,
"package rootbad;\nfn main() i32 = { return 0; };\n");
if (!setup) snprintf(path, sizeof path, "%s/b.ww", rootbad);
if (!setup) setup = write_file(path,
"package other;\nfn spare() i32 = { return 0; };\n");
if (!setup) snprintf(path, sizeof path, "%s/entry.ww", importbad);
if (!setup) setup = write_file(path,
"package main;\nimport wanted;\n"
"fn main() i32 = { return wanted.value(); };\n");
if (!setup) snprintf(path, sizeof path, "%s/a.ww", wanted);
if (!setup) setup = write_file(path,
"package other;\nexport fn value() i32 = { return 0; };\n");
if (!setup) snprintf(path, sizeof path, "%s/a.ww", missing);
if (!setup) setup = write_file(path,
"fn main() i32 = { return 0; };\n");
if (!setup) snprintf(path, sizeof path, "%s/a.ww", invalid);
if (!setup) setup = write_file(path,
"package 7bad;\nfn main() i32 = { return 0; };\n");
if (setup) {
fprintf(stderr, "737[owned-setup]: cannot create private fixtures\n");
fail++;
total++;
} else {
const char *drivers[] = { ww, ww_ww };
const char *stages[] = { "cstage", "wwstage" };
char units[2][512];
int built[2] = {0, 0};
for (int s = 0; s < 2; s++) {
if (access(drivers[s], X_OK) != 0) continue;
char out[512], err[512];
snprintf(out, sizeof out, "%s/root-%d", td, s);
snprintf(err, sizeof err, "%s/root-%d.err", td, s);
snprintf(cmd, sizeof cmd,
"%s build -o '%s' '%s' "
">/dev/null 2>'%s'", drivers[s], out,
rootok, err);
int rc = runwait(cmd);
total++;
if (rc != 0 || runwait(out) != 17) {
fprintf(stderr,
"737[root-%s]: build/run failed (build=%d)\n",
stages[s], rc);
fail++;
continue;
}
snprintf(units[s], sizeof units[s],
"%s.sepwork/__root.unit.ww", out);
if (!file_orders(units[s], "ORDER-A", "ORDER-Z")) {
fprintf(stderr,
"737[root-%s]: stored sources are not byte-sorted\n",
stages[s]);
fail++;
} else {
built[s] = 1;
}
}
if (built[0] && built[1]) {
total++;
if (!files_equal(units[0], units[1])) {
fprintf(stderr,
"737[root-parity]: C/WW units differ\n");
fail++;
}
}
struct rejectrow {
const char *tag;
const char *target;
const char *needle;
} rows[] = {
{ "root-conflict", rootbad, "conflicting package names" },
{ "import-leaf", NULL, "does not match import path" },
{ "root-missing", missing, "invalid or missing package clause" },
{ "root-invalid", invalid, "invalid or missing package clause" },
};
char entry[512];
snprintf(entry, sizeof entry, "%s/entry.ww", importbad);
rows[1].target = entry;
for (int r = 0; r < 4; r++) {
char errors[2][512];
int ran[2] = {0, 0};
for (int s = 0; s < 2; s++) {
if (access(drivers[s], X_OK) != 0) continue;
char out[512];
snprintf(out, sizeof out, "%s/%s-%d", td,
rows[r].tag, s);
snprintf(errors[s], sizeof errors[s], "%s/%s-%d.err",
td, rows[r].tag, s);
snprintf(cmd, sizeof cmd,
"%s build -o '%s' '%s' "
">/dev/null 2>'%s'", drivers[s], out,
rows[r].target, errors[s]);
int rc = runwait(cmd);
total++;
ran[s] = 1;
if (rc == 0
|| !stderr_contains(errors[s], rows[r].needle)) {
fprintf(stderr,
"737[%s-%s]: reject/diagnostic mismatch\n",
rows[r].tag, stages[s]);
fail++;
}
}
if (ran[0] && ran[1]) {
total++;
if (!files_equal(errors[0], errors[1])) {
fprintf(stderr,
"737[%s-parity]: C/WW diagnostics differ\n",
rows[r].tag);
fail++;
}
}
}
}
}
int cleanup_fail = 0;
char path[1024], cmd[2048];
const char *top_stems[] = { "bad-c", "bad-w", "baddeep-c", "baddeep-w" };
const char *suffixes[] = { "", ".err" };
for (size_t i = 0; i < sizeof top_stems / sizeof top_stems[0]; i++) {
snprintf(path, sizeof path, "%s/%s.sepwork", owner, top_stems[i]);
snprintf(cmd, sizeof cmd, "rm -rf -- '%s'", path);
if (runwait(cmd) != 0) {
fprintf(stderr, "737[cleanup]: remove %s failed\n", path);
cleanup_fail = 1;
}
}
const char *reject_tags[] = {
"root-conflict", "import-leaf", "root-missing", "root-invalid"
};
if (have_owned) {
for (int s = 0; s < 2; s++) {
snprintf(path, sizeof path, "%s/owned/root-%d.sepwork", owner, s);
snprintf(cmd, sizeof cmd, "rm -rf -- '%s'", path);
if (runwait(cmd) != 0) {
fprintf(stderr, "737[cleanup]: remove %s failed\n", path);
cleanup_fail = 1;
}
for (size_t r = 0; r < sizeof reject_tags / sizeof reject_tags[0]; r++) {
snprintf(path, sizeof path, "%s/owned/%s-%d.sepwork",
owner, reject_tags[r], s);
snprintf(cmd, sizeof cmd, "rm -rf -- '%s'", path);
if (runwait(cmd) != 0) {
fprintf(stderr, "737[cleanup]: remove %s failed\n", path);
cleanup_fail = 1;
}
}
}
}
for (size_t i = 0; i < sizeof top_stems / sizeof top_stems[0]; i++) {
for (size_t k = 0; k < sizeof suffixes / sizeof suffixes[0]; k++) {
snprintf(path, sizeof path, "%s/%s%s", owner,
top_stems[i], suffixes[k]);
if (unlink(path) != 0 && errno != ENOENT) {
fprintf(stderr, "737[cleanup]: unlink %s: %s\n",
path, strerror(errno));
cleanup_fail = 1;
}
}
}
if (have_owned) {
for (int s = 0; s < 2; s++) {
for (size_t k = 0; k < sizeof suffixes / sizeof suffixes[0]; k++) {
snprintf(path, sizeof path, "%s/owned/root-%d%s",
owner, s, suffixes[k]);
if (unlink(path) != 0 && errno != ENOENT) {
fprintf(stderr, "737[cleanup]: unlink %s: %s\n",
path, strerror(errno));
cleanup_fail = 1;
}
}
for (size_t r = 0; r < sizeof reject_tags / sizeof reject_tags[0]; r++) {
for (size_t k = 0; k < sizeof suffixes / sizeof suffixes[0]; k++) {
snprintf(path, sizeof path, "%s/owned/%s-%d%s",
owner, reject_tags[r], s, suffixes[k]);
if (unlink(path) != 0 && errno != ENOENT) {
fprintf(stderr, "737[cleanup]: unlink %s: %s\n",
path, strerror(errno));
cleanup_fail = 1;
}
}
}
}
}
struct owned_path {
const char *name;
int owned;
} fixture_files[] = {
{ "owned/rootok/z.ww", have_rootok },
{ "owned/rootok/a.ww", have_rootok },
{ "owned/rootbad/a.ww", have_rootbad },
{ "owned/rootbad/b.ww", have_rootbad },
{ "owned/importbad/entry.ww", have_importbad },
{ "owned/importbad/wanted/a.ww", have_wanted },
{ "owned/missing/a.ww", have_missing },
{ "owned/invalid/a.ww", have_invalid },
};
for (size_t i = 0; i < sizeof fixture_files / sizeof fixture_files[0]; i++) {
if (!fixture_files[i].owned) continue;
snprintf(path, sizeof path, "%s/%s", owner, fixture_files[i].name);
if (unlink(path) != 0 && errno != ENOENT) {
fprintf(stderr, "737[cleanup]: unlink %s: %s\n",
path, strerror(errno));
cleanup_fail = 1;
}
}
struct owned_path fixture_dirs[] = {
{ "owned/importbad/wanted", have_wanted },
{ "owned/rootok", have_rootok },
{ "owned/rootbad", have_rootbad },
{ "owned/importbad", have_importbad },
{ "owned/missing", have_missing },
{ "owned/invalid", have_invalid },
{ "owned", have_owned },
};
for (size_t i = 0; i < sizeof fixture_dirs / sizeof fixture_dirs[0]; i++) {
if (!fixture_dirs[i].owned) continue;
snprintf(path, sizeof path, "%s/%s", owner, fixture_dirs[i].name);
if (rmdir(path) != 0 && errno != ENOENT) {
fprintf(stderr, "737[cleanup]: rmdir %s: %s\n",
path, strerror(errno));
cleanup_fail = 1;
}
}
if (rmdir(owner) != 0) {
fprintf(stderr, "737[cleanup]: rmdir %s: %s\n",
owner, strerror(errno));
cleanup_fail = 1;
}
if (cleanup_fail) fail++;
if (fail) {
fprintf(stderr, "737_direnum: %d/%d fixtures failed\n", fail, total);
return 1;

View File

@@ -29,7 +29,6 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include "wwtestpkg.h"
@@ -70,8 +69,7 @@ struct row {
const char *label;
/* Direct-w6c source: bare fn produce, no package/imports. */
const char *asm_src;
/* Driver source: `package main; import os; ...` runtime wrapper. */
const char *run_src;
/* Runtime copies are owned by test/wcc/data/r744_letcopy_*. */
/* Source-slot loads expected in produce: at least N for the copy.
* Pre-fix cstage emitted 0; pre-fix wwstage emitted 1 with stale BX.
* `MOVQ -K(BP), AX` is also emitted by p2.a / p2.s.len reads in the
@@ -96,18 +94,6 @@ static const struct row rows[] = {
" if (p2.c != 33) { return 13; };\n"
" return 0;\n"
"};\n",
"package main;\n"
"import os;\n"
"type tri = struct { a: i32, b: i32, c: i32 };\n"
"fn produce() i32 = {\n"
" let p1: tri = tri{a=11, b=22, c=33};\n"
" let p2: tri = p1;\n"
" if (p2.a != 11) { return 11; };\n"
" if (p2.b != 22) { return 12; };\n"
" if (p2.c != 33) { return 13; };\n"
" return 0;\n"
"};\n"
"export fn main() i32 = { os.exit(produce()); };\n",
1 },
/* (b) i32 + str field, sz=24. Copy = 3 × MOVQ. */
{ "field_str",
@@ -120,18 +106,6 @@ static const struct row rows[] = {
" if (p2.s.len != 2) { return 12; };\n"
" return 0;\n"
"};\n",
"package main;\n"
"import os;\n"
"type ws = struct { a: i32, s: str };\n"
"fn produce() i32 = {\n"
" let p1: ws;\n"
" p1.a = 7; p1.s = \"hi\";\n"
" let p2: ws = p1;\n"
" if (p2.a != 7) { return 11; };\n"
" if (p2.s.len != 2) { return 12; };\n"
" return 0;\n"
"};\n"
"export fn main() i32 = { os.exit(produce()); };\n",
3 },
/* (c) i32 + []u8 slice field, sz=32. Copy = 4 × MOVQ. */
{ "field_slice",
@@ -145,19 +119,6 @@ static const struct row rows[] = {
" if (p2.b.len != 3) { return 12; };\n"
" return 0;\n"
"};\n",
"package main;\n"
"import os;\n"
"type wsl = struct { a: i32, b: []u8 };\n"
"fn produce() i32 = {\n"
" let raw: [3]u8 = [1: u8, 2: u8, 3: u8];\n"
" let p1: wsl;\n"
" p1.a = 9; p1.b = raw[0:3];\n"
" let p2: wsl = p1;\n"
" if (p2.a != 9) { return 11; };\n"
" if (p2.b.len != 3) { return 12; };\n"
" return 0;\n"
"};\n"
"export fn main() i32 = { os.exit(produce()); };\n",
4 },
/* (d) i32 + tagged (i32|str) field, sz=32. Copy = 4 × MOVQ.
* Runtime checks only p2.a; the asm load-count (>=4) and the
@@ -174,21 +135,6 @@ static const struct row rows[] = {
" if (p2.a != 5) { return 11; };\n"
" return 0;\n"
"};\n",
"package main;\n"
"import os;\n"
"type tag = (i32 | str);\n"
"type wtg = struct { a: i32, t: tag };\n"
"fn produce() i32 = {\n"
" let p1: wtg = wtg{a=5, t=42: tag};\n"
" let p2: wtg = p1;\n"
" if (p2.a != 5) { return 11; };\n"
" match (p2.t) {\n"
" case let x: i32 => if (x != 42) { return 12; };\n"
" case let s: str => return 13;\n"
" };\n"
" return 0;\n"
"};\n"
"export fn main() i32 = { os.exit(produce()); };\n",
4 },
};
@@ -243,39 +189,6 @@ emit_s(const char *w6c, const struct row *r, int i, int stage,
return rc;
}
static int
run_driver(const char *driver, const struct row *r, int i, int stage)
{
char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/lcs_run_%d_d_%d_%d",
getpid(), i, stage);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/lcs_run_%d_%d_%d.ww",
tmpdir, getpid(), i, stage);
snprintf(outbin, sizeof outbin, "%s/lcs_run_%d_%d_%d",
tmpdir, getpid(), i, stage);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
wwtest_fputs(r->run_src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s] build via %s failed\n",
r->label, driver);
runwait(rmcmd);
return -1;
}
int got = runwait(outbin);
runwait(rmcmd);
return got;
}
int
main(void)
{
@@ -289,14 +202,11 @@ main(void)
bin = absbin;
}
char w6c[640], w6c_ww[640], cdrv[640], wdrv[640];
char w6c[640], w6c_ww[640];
snprintf(w6c, sizeof w6c, "%s/w6c", bin);
snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin);
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
int have_ww = (access(w6c_ww, X_OK) == 0);
int have_wdrv = (access(wdrv, X_OK) == 0);
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
@@ -343,27 +253,6 @@ main(void)
}
unlink(cs_path);
/* Runtime correctness via cstage driver. */
total++;
int got = run_driver(cdrv, r, i, 2);
if (got != 0) {
fprintf(stderr,
"letcopy_struct[ww/%s]: exit=%d want=0\n",
r->label, got);
fail++;
}
/* Runtime correctness via wwstage driver. */
if (have_wdrv) {
total++;
got = run_driver(wdrv, r, i, 3);
if (got != 0) {
fprintf(stderr,
"letcopy_struct[ww_ww/%s]: exit=%d want=0\n",
r->label, got);
fail++;
}
}
}
if (fail) {

View File

@@ -39,6 +39,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -53,10 +54,9 @@ runwait(const char *cmd)
}
struct row {
/* Runtime ownership: test/wcc/data/r749_sumtype_forward_*. */
const char *label;
const char *src;
int want_cs;
int want_ws;
};
static const struct row rows[] = {
@@ -85,8 +85,7 @@ static const struct row rows[] = {
"export fn main() i32 = {\n"
" if (!pick(\"foo\", \"world\", 'x')) { return 11; };\n"
" return 0;\n"
"};\n",
0, 0 },
"};\n" },
/* 2. Ident-source sum-arg. needle is a (str|rune) local; helper
* call routes through the existing N_IDENT-aistagged path. The
@@ -105,8 +104,7 @@ static const struct row rows[] = {
" let needle: (str | rune) = \"foo\";\n"
" if (!helper(needle)) { return 11; };\n"
" return 0;\n"
"};\n",
0, 0 },
"};\n" },
/* 3. Literal-source sum-arg. helper(\"foo\") widens the str at
* the call site. Stays on the widening path (param tagged, arg
@@ -123,25 +121,9 @@ static const struct row rows[] = {
"export fn main() i32 = {\n"
" if (!helper(\"foo\")) { return 11; };\n"
" return 0;\n"
"};\n",
0, 0 },
"};\n" },
};
static int
build_with(const char *driver, const char *src_path, const char *outbin)
{
char cmd[1024];
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, src_path);
return runwait(cmd);
}
static int
exec_bin(const char *bin)
{
return runwait(bin);
}
/* read_file slurps a file whole, returning a malloc'd buffer
* (NUL-terminated) or NULL on error. */
static char *
@@ -195,12 +177,9 @@ main(void)
bin = absbin;
}
char cdrv[640], wdrv[640], cw6[640], ww6[640];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
char cw6[640], ww6[640];
snprintf(cw6, sizeof cw6, "%s/w6c", bin);
snprintf(ww6, sizeof ww6, "%s/w6c_ww", bin);
int have_ww = (access(wdrv, X_OK) == 0);
int have_w6cww = (access(ww6, X_OK) == 0);
int n = (int)(sizeof rows / sizeof rows[0]);
@@ -209,56 +188,37 @@ main(void)
for (int i = 0; i < n; i++) {
const struct row *r = &rows[i];
char src[128], tmpdir[64], outbin[128], rmcmd[160];
char src[160], tmpdir[96], css[160], wss[160];
snprintf(tmpdir, sizeof tmpdir, "/tmp/sumtype_fwd_%d_d_%d",
getpid(), i);
mkdir(tmpdir, 0755);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
if (mkdir(tmpdir, 0755) != 0) {
fprintf(stderr,
"sumtype_forward[%s]: cannot acquire temp directory: %s\n",
r->label, strerror(errno));
fail++; total++;
continue;
}
snprintf(src, sizeof src, "%s/sumtype_fwd_%d_%d.ww",
tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/sumtype_fwd_%d_%d",
tmpdir, getpid(), i);
snprintf(css, sizeof css, "%s/cs.s", tmpdir);
snprintf(wss, sizeof wss, "%s/ws.s", tmpdir);
char *cs_asm = NULL, *ws_asm = NULL;
size_t cs_n = 0, ws_n = 0;
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); fail++; total++; continue; }
fputs(r->src, f);
fclose(f);
/* Runtime parity (cstage). */
total++;
if (build_with(cdrv, src, outbin) != 0) {
fprintf(stderr,
"sumtype_forward[cs][%s]: build failed\n",
r->label);
fail++;
} else {
int got = exec_bin(outbin);
if (got != r->want_cs) {
fprintf(stderr,
"sumtype_forward[cs][%s]: rc=%d want=%d\n",
r->label, got, r->want_cs);
fail++;
}
if (!f) {
fprintf(stderr, "sumtype_forward[%s]: cannot create source: %s\n",
r->label, strerror(errno));
fail++; total++;
goto cleanup;
}
unlink(outbin);
/* Runtime parity (wwstage). */
if (have_ww) {
total++;
if (build_with(wdrv, src, outbin) != 0) {
fprintf(stderr,
"sumtype_forward[ws][%s]: build failed\n",
r->label);
fail++;
} else {
int got = exec_bin(outbin);
if (got != r->want_ws) {
fprintf(stderr,
"sumtype_forward[ws][%s]: rc=%d want=%d\n",
r->label, got, r->want_ws);
fail++;
}
}
unlink(outbin);
int write_failed = fputs(r->src, f) == EOF;
if (fclose(f) != 0) write_failed = 1;
if (write_failed) {
fprintf(stderr, "sumtype_forward[%s]: cannot write source\n",
r->label);
fail++; total++;
goto cleanup;
}
/* Byte-id of the helper(needles[i]) callsite window. Emit
@@ -266,23 +226,43 @@ main(void)
* (w6c emits no .sepwork, so no leak) and compare. Only the
* first row has a main.helper call; rows 2/3 also have one
* but a simpler shape — match them all for symmetry. */
char *cs_asm = NULL, *ws_asm = NULL;
size_t cs_n = 0, ws_n = 0;
if (have_w6cww) {
char css[160], wss[160], emit[1100];
snprintf(css, sizeof css, "%s/cs.s", tmpdir);
snprintf(wss, sizeof wss, "%s/ws.s", tmpdir);
char emit[1100];
total++;
snprintf(emit, sizeof emit, "%s -o %s %s 2>/dev/null",
cw6, css, src);
if (runwait(emit) == 0)
cs_asm = read_file(css, &cs_n);
if (runwait(emit) != 0) {
fprintf(stderr,
"sumtype_forward[cstage][%s]: w6c failed\n",
r->label);
fail++;
goto cleanup;
}
cs_asm = read_file(css, &cs_n);
if (cs_asm == NULL) {
fprintf(stderr,
"sumtype_forward[cstage][%s]: cannot read %s\n",
r->label, css);
fail++;
goto cleanup;
}
snprintf(emit, sizeof emit, "%s -o %s %s 2>/dev/null",
ww6, wss, src);
if (runwait(emit) == 0)
ws_asm = read_file(wss, &ws_n);
}
if (cs_asm && ws_asm) {
total++;
if (runwait(emit) != 0) {
fprintf(stderr,
"sumtype_forward[wwstage][%s]: w6c_ww failed\n",
r->label);
fail++;
goto cleanup;
}
ws_asm = read_file(wss, &ws_n);
if (ws_asm == NULL) {
fprintf(stderr,
"sumtype_forward[wwstage][%s]: cannot read %s\n",
r->label, wss);
fail++;
goto cleanup;
}
const char *cs_site = helper_callsite(cs_asm);
const char *ws_site = helper_callsite(ws_asm);
if (!cs_site || !ws_site) {
@@ -299,10 +279,25 @@ main(void)
fail++;
}
}
cleanup: ;
free(cs_asm);
free(ws_asm);
runwait(rmcmd);
int cleanup_fail = 0;
const char *files[] = { src, css, wss };
for (size_t k = 0; k < sizeof files / sizeof files[0]; k++) {
if (unlink(files[k]) != 0 && errno != ENOENT) {
fprintf(stderr, "sumtype_forward[%s]: unlink %s: %s\n",
r->label, files[k], strerror(errno));
cleanup_fail = 1;
}
}
if (rmdir(tmpdir) != 0) {
fprintf(stderr, "sumtype_forward[%s]: rmdir %s: %s\n",
r->label, tmpdir, strerror(errno));
cleanup_fail = 1;
}
if (cleanup_fail) fail++;
}
if (fail) {

View File

@@ -39,6 +39,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -176,7 +177,8 @@ static const struct row rows[] = {
/* Write `src` to `<dir>/<rel>`, mkdir'ing intermediate dirs. */
static int
write_one(const char *dir, const char *rel, const char *src)
write_one(const char *dir, const char *rel, const char *src,
int *parent_owned)
{
char path[512];
snprintf(path, sizeof path, "%s/%s", dir, rel);
@@ -184,14 +186,15 @@ write_one(const char *dir, const char *rel, const char *src)
char *slash = strchr(path + strlen(dir) + 1, '/');
if (slash) {
*slash = '\0';
mkdir(path, 0755);
if (mkdir(path, 0755) != 0) return -1;
*parent_owned = 1;
*slash = '/';
}
FILE *f = fopen(path, "wb");
if (!f) return -1;
fputs(src, f);
fclose(f);
return 0;
int failed = fputs(src, f) == EOF;
if (fclose(f) != 0) failed = 1;
return failed ? -1 : 0;
}
static char *
@@ -215,29 +218,43 @@ slurp(const char *path, size_t *outsz)
static int
run_row(const char *driver, const struct row *r, int idx, const char *tag)
{
char tmpdir[64];
char tmpdir[96];
snprintf(tmpdir, sizeof tmpdir, "/tmp/mklabel_%s_%d_%d",
tag, getpid(), idx);
mkdir(tmpdir, 0755);
if (mkdir(tmpdir, 0755) != 0) {
fprintf(stderr,
"mklabel_modscoped[%s][%s]: cannot acquire temp directory: %s\n",
tag, r->label, strerror(errno));
return 1;
}
int fail = 0;
int parent_owned[4] = { 0 };
char entry_bin[512], stem[512], alls[600], cmd[2048];
snprintf(entry_bin, sizeof entry_bin, "%s/%s", tmpdir, r->entry);
char *dot = strrchr(entry_bin, '.');
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
snprintf(stem, sizeof stem, "%s/%s", tmpdir, r->entry);
char *d2 = strrchr(stem, '.');
if (d2 && strcmp(d2, ".ww") == 0) *d2 = '\0';
snprintf(alls, sizeof alls, "%s/all.s", tmpdir);
for (int k = 0; k < r->n_files; k++) {
if (write_one(tmpdir, r->files[2 * k],
r->files[2 * k + 1]) != 0) {
r->files[2 * k + 1], &parent_owned[k]) != 0) {
fprintf(stderr,
"mklabel_modscoped[%s][%s]: write %s failed\n",
tag, r->label, r->files[2 * k]);
return 1;
fail++;
goto cleanup;
}
}
char cmd[2048];
/* #93 sep layout: emit asm to <entry-stem>.sepwork/<pkg>.s; pin
* WW_PKGCACHE under tmpdir so out/.pkgcache is untouched. */
* all outputs stay under tmpdir. */
snprintf(cmd, sizeof cmd,
"cd %s && b='%s'; WW_PKGCACHE=%s/pkgc %s build --sep "
"cd %s && b='%s'; %s build "
"-o \"${b%%.ww}\" \"$b\" >/dev/null 2>&1",
tmpdir, r->entry, tmpdir, driver);
tmpdir, r->entry, driver);
if (runwait(cmd) != 0) {
fprintf(stderr,
"mklabel_modscoped[%s][%s]: build failed\n",
@@ -247,10 +264,6 @@ run_row(const char *driver, const struct row *r, int idx, const char *tag)
}
/* Runtime check. */
char entry_bin[512];
snprintf(entry_bin, sizeof entry_bin, "%s/%s", tmpdir, r->entry);
char *dot = strrchr(entry_bin, '.');
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
int got = runwait(entry_bin);
if (got != r->want_exit) {
fprintf(stderr,
@@ -264,12 +277,7 @@ run_row(const char *driver, const struct row *r, int idx, const char *tag)
* cross-module label substrings (mod1.x / strings.x) live in the
* per-package files, so concat ALL of them (sorted glob) — a single
* __root.s read would FALSE-FAIL the multi-package rows. */
char stem[512];
snprintf(stem, sizeof stem, "%s/%s", tmpdir, r->entry);
char *d2 = strrchr(stem, '.');
if (d2 && strcmp(d2, ".ww") == 0) *d2 = '\0';
char alls[600], catcmd[1200];
snprintf(alls, sizeof alls, "%s/all.s", tmpdir);
char catcmd[1200];
snprintf(catcmd, sizeof catcmd, "cat %s.sepwork/*.s > %s 2>/dev/null",
stem, alls);
(void)runwait(catcmd);
@@ -293,10 +301,60 @@ run_row(const char *driver, const struct row *r, int idx, const char *tag)
free(asm_buf);
}
cleanup:
/* Best-effort cleanup. Leaving artifacts is fine on failure. */
snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir);
(void)runwait(cmd);
cleanup: ;
/* The retained build scratch is the only recursively removed path. */
int cleanup_fail = 0;
char sepwork[600];
snprintf(sepwork, sizeof sepwork, "%s.sepwork", stem);
snprintf(cmd, sizeof cmd, "rm -rf -- '%s'", sepwork);
if (runwait(cmd) != 0) {
fprintf(stderr,
"mklabel_modscoped[%s][%s]: remove %s failed\n",
tag, r->label, sepwork);
cleanup_fail = 1;
}
const char *outputs[] = { alls, entry_bin };
for (size_t k = 0; k < sizeof outputs / sizeof outputs[0]; k++) {
if (unlink(outputs[k]) != 0 && errno != ENOENT) {
fprintf(stderr,
"mklabel_modscoped[%s][%s]: unlink %s: %s\n",
tag, r->label, outputs[k], strerror(errno));
cleanup_fail = 1;
}
}
for (int k = 0; k < r->n_files; k++) {
char path[512];
snprintf(path, sizeof path, "%s/%s", tmpdir, r->files[2 * k]);
char *slash = strchr(path + strlen(tmpdir) + 1, '/');
if (slash && !parent_owned[k]) continue;
if (unlink(path) != 0 && errno != ENOENT) {
fprintf(stderr,
"mklabel_modscoped[%s][%s]: unlink %s: %s\n",
tag, r->label, path, strerror(errno));
cleanup_fail = 1;
}
}
for (int k = r->n_files - 1; k >= 0; k--) {
char path[512];
snprintf(path, sizeof path, "%s/%s", tmpdir, r->files[2 * k]);
char *slash = strrchr(path, '/');
if (parent_owned[k] && slash && slash > path + strlen(tmpdir)) {
*slash = '\0';
if (rmdir(path) != 0 && errno != ENOENT) {
fprintf(stderr,
"mklabel_modscoped[%s][%s]: rmdir %s: %s\n",
tag, r->label, path, strerror(errno));
cleanup_fail = 1;
}
}
}
if (rmdir(tmpdir) != 0) {
fprintf(stderr,
"mklabel_modscoped[%s][%s]: rmdir %s: %s\n",
tag, r->label, tmpdir, strerror(errno));
cleanup_fail = 1;
}
if (cleanup_fail) fail++;
return fail;
}

View File

@@ -42,14 +42,6 @@
* | mis-marshals — rc=11.
* | Post-fix both stages
* | rc=0.
* same_module_same_leaf | non-regression: two
* | fns in the same module
* | sharing a leaf (one
* | variadic, one not).
* | mklabel seq stays
* | distinct + each call
* | dispatches to its own
* | shape.
* bare_leaf_no_collision | control: variadic fn
* | declared in only one
* | module, called via
@@ -60,10 +52,16 @@
* | through bare
* | fnparamslookup
* | (post-#4d).
*
* The standalone same_module_same_leaf runtime control now lives in
* test/wcc/data/r75_modparam_same_module_same_leaf/case.ww. This wrapper
* retains only the two package-layout rows that require separately named
* imported source files and therefore are not expressible by case.ww.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -126,34 +124,6 @@ static const struct row rows[] = {
"};\n" },
"main.ww" },
/* Non-regression. Same module declares two fns sharing a leaf
* (one variadic, one not). The N_IDENT bare-leaf path still
* graduates via fnparamslookup's same-module-first walk (#4d);
* the N_DOT arm doesn't fire because there's no `mod.fn` use
* site. */
{ "same_module_same_leaf", 0, 1,
{ "main.ww",
"package main;\n"
"fn variadic_foo(a: i32, xs: i32...) i32 = {\n"
"\treturn a + xs.len;\n"
"};\n"
"fn scalar_foo(a: i32, n: (u8 | []u8)) i32 = {\n"
"\tmatch (n) {\n"
"\tcase let c: u8 => return a + (c: i32);\n"
"\tcase let s: []u8 => return a + s.len;\n"
"\t};\n"
"\treturn 0;\n"
"};\n"
"export fn main() i32 = {\n"
"\tlet n: (u8 | []u8) = 5u8;\n"
"\tlet a: i32 = variadic_foo(1, 2, 3, 4);\n"
"\tlet b: i32 = scalar_foo(10, n);\n"
"\tif (a != 4) { return 30; };\n"
"\tif (b != 15) { return 31; };\n"
"\treturn 0;\n"
"};\n" },
"main.ww" },
/* Control. A variadic fn exists in only one imported module;
* the caller calls it via N_DOT. No same-leaf collision, so
* fnparamslookupmod's same-module hint and the head-walk
@@ -179,39 +149,79 @@ static const struct row rows[] = {
};
static int
write_one(const char *dir, const char *rel, const char *src)
write_one(const char *dir, const char *rel, const char *src,
int *parent_owned)
{
char path[512];
snprintf(path, sizeof path, "%s/%s", dir, rel);
char *slash = strchr(path + strlen(dir) + 1, '/');
if (slash) {
*slash = '\0';
mkdir(path, 0755);
if (mkdir(path, 0755) != 0) return -1;
*parent_owned = 1;
*slash = '/';
}
FILE *f = fopen(path, "wb");
if (!f) return -1;
fputs(src, f);
fclose(f);
return 0;
int rc = fputs(src, f) == EOF ? -1 : 0;
if (fclose(f) != 0) rc = -1;
return rc;
}
static int
cleanup_row(const char *tmpdir, const struct row *r,
const int *parent_owned)
{
char path[512], cmd[640], entry[512];
int rc = 0;
for (int k = 0; k < r->n_files; k++) {
snprintf(path, sizeof path, "%s/%s", tmpdir, r->files[2 * k]);
char *slash = strchr(path + strlen(tmpdir) + 1, '/');
if (slash && !parent_owned[k]) continue;
if (unlink(path) != 0 && errno != ENOENT) rc = -1;
}
snprintf(entry, sizeof entry, "%s/%s", tmpdir, r->entry);
char *dot = strrchr(entry, '.');
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
if (unlink(entry) != 0 && errno != ENOENT) rc = -1;
snprintf(cmd, sizeof cmd, "rm -rf %s.sepwork", entry);
if (runwait(cmd) != 0) rc = -1;
for (int k = 0; k < r->n_files; k++) {
snprintf(path, sizeof path, "%s/%s", tmpdir, r->files[2 * k]);
char *slash = strchr(path + strlen(tmpdir) + 1, '/');
if (slash && parent_owned[k]) {
*slash = '\0';
if (rmdir(path) != 0 && errno != ENOENT) rc = -1;
}
}
if (rmdir(tmpdir) != 0) rc = -1;
return rc;
}
static int
run_row(const char *driver, const struct row *r, int idx, const char *tag)
{
char tmpdir[64];
snprintf(tmpdir, sizeof tmpdir, "/tmp/modparam_%s_%d_%d",
tag, getpid(), idx);
mkdir(tmpdir, 0755);
(void)idx;
char tmpdir[] = "/tmp/modparam_XXXXXX";
if (mkdtemp(tmpdir) == NULL) {
fprintf(stderr, "modparam_callee[%s][%s]: mkdtemp failed\n",
tag, r->label);
return 1;
}
int fail = 0;
int parent_owned[4] = { 0 };
for (int k = 0; k < r->n_files; k++) {
if (write_one(tmpdir, r->files[2 * k],
r->files[2 * k + 1]) != 0) {
r->files[2 * k + 1], &parent_owned[k]) != 0) {
fprintf(stderr,
"modparam_callee[%s][%s]: write %s failed\n",
tag, r->label, r->files[2 * k]);
return 1;
fail++;
goto cleanup;
}
}
@@ -239,8 +249,11 @@ run_row(const char *driver, const struct row *r, int idx, const char *tag)
}
cleanup:
snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir);
(void)runwait(cmd);
if (cleanup_row(tmpdir, r, parent_owned) != 0) {
fprintf(stderr, "modparam_callee[%s][%s]: cleanup failed\n",
tag, r->label);
if (fail == 0) fail++;
}
return fail;
}

View File

@@ -53,6 +53,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
@@ -125,7 +126,18 @@ emit_s(const char *w6c, const struct row *r, int i, char *out_s, size_t cap)
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, out_s, src);
int rc = runwait(cmd);
unlink(src);
int cleanfail = 0;
if (unlink(src) != 0 && errno != ENOENT) {
perror(src);
cleanfail = 1;
}
/* a failing compile can still leave a partial .s behind */
if (rc != 0 && unlink(out_s) != 0 && errno != ENOENT) {
perror(out_s);
cleanfail = 1;
}
if (cleanfail && rc == 0)
rc = -1;
return rc;
}
@@ -187,6 +199,7 @@ main(void)
int have_ww = (access(w6c_ww, X_OK) == 0);
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
int cleanfail = 0;
for (int i = 0; i < n; i++) {
char cs_path[128], ws_path[128];
@@ -200,19 +213,43 @@ main(void)
total++;
if (check_imm(cs_path, &rows[i], "cstage") != 0) fail++;
if (!have_ww) { unlink(cs_path); continue; }
if (!have_ww) {
if (unlink(cs_path) != 0 && errno != ENOENT) {
perror(cs_path);
cleanfail = 1;
}
continue;
}
if (emit_s(w6c_ww, &rows[i], i, ws_path, sizeof ws_path) != 0) {
fprintf(stderr,
"convwrap_audit[wwstage][%s]: w6c_ww failed\n",
rows[i].label);
fail++; total++;
unlink(cs_path); continue;
if (unlink(cs_path) != 0 && errno != ENOENT) {
perror(cs_path);
cleanfail = 1;
}
continue;
}
total++;
if (check_imm(ws_path, &rows[i], "wwstage") != 0) fail++;
unlink(cs_path); unlink(ws_path);
if (unlink(cs_path) != 0 && errno != ENOENT) {
perror(cs_path);
cleanfail = 1;
}
if (unlink(ws_path) != 0 && errno != ENOENT) {
perror(ws_path);
cleanfail = 1;
}
}
/* a cleanup failure must not pass silently, but must not mask a
* real assertion failure's own report either. */
if (cleanfail && fail == 0) {
fprintf(stderr, "convwrap_audit: cleanup failed\n");
return 1;
}
if (fail) {

View File

@@ -49,6 +49,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include "wwtestpkg.h"
@@ -117,7 +118,18 @@ emit_s(const char *w6c, const struct row *r, int i, char *out_s, size_t cap)
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, out_s, src);
int rc = runwait(cmd);
unlink(src);
int cleanfail = 0;
if (unlink(src) != 0 && errno != ENOENT) {
perror(src);
cleanfail = 1;
}
/* a failing compile can still leave a partial .s behind */
if (rc != 0 && unlink(out_s) != 0 && errno != ENOENT) {
perror(out_s);
cleanfail = 1;
}
if (cleanfail && rc == 0)
rc = -1;
return rc;
}
@@ -186,6 +198,7 @@ main(void)
int have_ww = (access(w6c_ww, X_OK) == 0);
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
int cleanfail = 0;
for (int i = 0; i < n; i++) {
char cs_path[128], ws_path[128];
@@ -199,14 +212,24 @@ main(void)
total++;
if (check_stride(cs_path, &rows[i], "cstage") != 0) fail++;
if (!have_ww) { unlink(cs_path); continue; }
if (!have_ww) {
if (unlink(cs_path) != 0 && errno != ENOENT) {
perror(cs_path);
cleanfail = 1;
}
continue;
}
if (emit_s(w6c_ww, &rows[i], i, ws_path, sizeof ws_path) != 0) {
fprintf(stderr,
"slice_of_slice_index[wwstage][%s]: w6c_ww failed\n",
rows[i].label);
fail++; total++;
unlink(cs_path); continue;
if (unlink(cs_path) != 0 && errno != ENOENT) {
perror(cs_path);
cleanfail = 1;
}
continue;
}
total++;
if (check_stride(ws_path, &rows[i], "wwstage") != 0) fail++;
@@ -222,7 +245,21 @@ main(void)
fail++;
}
unlink(cs_path); unlink(ws_path);
if (unlink(cs_path) != 0 && errno != ENOENT) {
perror(cs_path);
cleanfail = 1;
}
if (unlink(ws_path) != 0 && errno != ENOENT) {
perror(ws_path);
cleanfail = 1;
}
}
/* a cleanup failure must not pass silently, but must not mask a
* real assertion failure's own report either. */
if (cleanfail && fail == 0) {
fprintf(stderr, "slice_of_slice_index: cleanup failed\n");
return 1;
}
if (fail) {

View File

@@ -59,6 +59,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include "wwtestpkg.h"
@@ -149,7 +150,18 @@ emit_s(const char *w6c, const struct row *r, int i, char *out_s, size_t cap)
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, out_s, src);
int rc = runwait(cmd);
unlink(src);
int cleanfail = 0;
if (unlink(src) != 0 && errno != ENOENT) {
perror(src);
cleanfail = 1;
}
/* a failing compile can still leave a partial .s behind */
if (rc != 0 && unlink(out_s) != 0 && errno != ENOENT) {
perror(out_s);
cleanfail = 1;
}
if (cleanfail && rc == 0)
rc = -1;
return rc;
}
@@ -218,6 +230,7 @@ main(void)
int have_ww = (access(w6c_ww, X_OK) == 0);
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
int cleanfail = 0;
for (int i = 0; i < n; i++) {
char cs_path[128], ws_path[128];
@@ -231,14 +244,24 @@ main(void)
total++;
if (check_stride(cs_path, &rows[i], "cstage") != 0) fail++;
if (!have_ww) { unlink(cs_path); continue; }
if (!have_ww) {
if (unlink(cs_path) != 0 && errno != ENOENT) {
perror(cs_path);
cleanfail = 1;
}
continue;
}
if (emit_s(w6c_ww, &rows[i], i, ws_path, sizeof ws_path) != 0) {
fprintf(stderr,
"amp_dot_idx[wwstage][%s]: w6c_ww failed\n",
rows[i].label);
fail++; total++;
unlink(cs_path); continue;
if (unlink(cs_path) != 0 && errno != ENOENT) {
perror(cs_path);
cleanfail = 1;
}
continue;
}
total++;
if (check_stride(ws_path, &rows[i], "wwstage") != 0) fail++;
@@ -253,7 +276,21 @@ main(void)
fail++;
}
unlink(cs_path); unlink(ws_path);
if (unlink(cs_path) != 0 && errno != ENOENT) {
perror(cs_path);
cleanfail = 1;
}
if (unlink(ws_path) != 0 && errno != ENOENT) {
perror(ws_path);
cleanfail = 1;
}
}
/* a cleanup failure must not pass silently, but must not mask a
* real assertion failure's own report either. */
if (cleanfail && fail == 0) {
fprintf(stderr, "amp_dot_idx: cleanup failed\n");
return 1;
}
if (fail) {

View File

@@ -26,19 +26,13 @@
* r4_i32_double | `type b = a; type a = struct{i32...}` | wedge pre-fix (RC=14)
* | aliased twice → `type c = b;` |
*
* Each row asserts:
* - cstage RC == 0
* - wwstage RC == 0 (when w6c_ww present)
* - cstage and wwstage emit byte-identical asm
*
* Pre-fix: rows 2 and 4 fail the RC check on both stages and the
* byte-id is also broken on rows 2/4 (one stage emits sret discipline,
* the other emits the scalar-AX fall-through). Post-fix all 12
* fixtures pass.
* Runtime ownership moved to the matching r756_* fixtures. This carrier
* retains the cstage/wwstage assembly byte-identity discriminator.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -135,36 +129,6 @@ static const struct row rows[] = {
},
};
static int
build_and_run(const char *driver, const struct row *r, int i)
{
char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/acu_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/acu_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/acu_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s build -o %s %s",
driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
runwait(rmcmd);
return -1;
}
int got = runwait(outbin);
runwait(rmcmd);
return got;
}
static int
emit_s(const char *w6c, const struct row *r, int i, char *out_s, size_t cap)
{
@@ -180,7 +144,18 @@ emit_s(const char *w6c, const struct row *r, int i, char *out_s, size_t cap)
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, out_s, src);
int rc = runwait(cmd);
unlink(src);
int cleanfail = 0;
if (unlink(src) != 0 && errno != ENOENT) {
perror(src);
cleanfail = 1;
}
/* a failing compile can still leave a partial .s behind */
if (rc != 0 && unlink(out_s) != 0 && errno != ENOENT) {
perror(out_s);
cleanfail = 1;
}
if (cleanfail && rc == 0)
rc = -1;
return rc;
}
@@ -197,52 +172,40 @@ main(void)
bin = absbin;
}
char cdrv[640], wdrv[640];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
char cw6c[640], ww6c[640];
snprintf(cw6c, sizeof cw6c, "%s/w6c", bin);
snprintf(ww6c, sizeof ww6c, "%s/w6c_ww", bin);
int have_ww = (access(wdrv, X_OK) == 0)
&& (access(ww6c, X_OK) == 0);
int have_ww = access(ww6c, X_OK) == 0;
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
int cleanfail = 0;
for (int i = 0; i < n; i++) {
/* cstage RC */
total++;
int got_c = build_and_run(cdrv, &rows[i], i);
if (got_c != 0) {
fprintf(stderr,
"alias_chain[cstage][%s]: exit=%d want=0\n",
rows[i].label, got_c);
fail++;
}
if (!have_ww) continue;
/* wwstage RC */
total++;
int got_w = build_and_run(wdrv, &rows[i], i);
if (got_w != 0) {
fprintf(stderr,
"alias_chain[wwstage][%s]: exit=%d want=0\n",
rows[i].label, got_w);
fail++;
}
/* byte-id cstage vs wwstage */
total++;
char cs_path[160], ws_path[160];
if (emit_s(cw6c, &rows[i], i, cs_path, sizeof cs_path) != 0
|| emit_s(ww6c, &rows[i], i, ws_path, sizeof ws_path) != 0) {
/* legs split: on a first-leg failure ws_path is never
* filled in, so it must never reach unlink. */
if (emit_s(cw6c, &rows[i], i, cs_path, sizeof cs_path) != 0) {
fprintf(stderr,
"alias_chain[byte-id][%s]: emit failed\n",
rows[i].label);
fail++;
unlink(cs_path); unlink(ws_path);
continue;
}
if (emit_s(ww6c, &rows[i], i, ws_path, sizeof ws_path) != 0) {
fprintf(stderr,
"alias_chain[byte-id][%s]: emit failed\n",
rows[i].label);
fail++;
if (unlink(cs_path) != 0 && errno != ENOENT) {
perror(cs_path);
cleanfail = 1;
}
continue;
}
char cmpcmd[512];
@@ -254,7 +217,21 @@ main(void)
rows[i].label);
fail++;
}
unlink(cs_path); unlink(ws_path);
if (unlink(cs_path) != 0 && errno != ENOENT) {
perror(cs_path);
cleanfail = 1;
}
if (unlink(ws_path) != 0 && errno != ENOENT) {
perror(ws_path);
cleanfail = 1;
}
}
/* a cleanup failure must not pass silently, but must not mask a
* real assertion failure's own report either. */
if (cleanfail && fail == 0) {
fprintf(stderr, "alias_chain: cleanup failed\n");
return 1;
}
if (fail) {

View File

@@ -27,9 +27,9 @@
* combines lib/rt/malloc.ww into the fixture and would always supply
* the @symbol decl, masking the regression.
*
* The runtime `rows` below run the generated binary under cstage and
* (when present) wwstage; the exit code is the regression-catching
* gate. The cstage row is the cross-check oracle.
* The runtime rows moved to the test/wcc/data/r75_alloc_{str_singleton,
* i32_then_str,str_then_i32,two_str,f64_str_i32,str_readback} fixtures;
* this carrier retains only the asm-line checks below.
*
* Full asm byte-identity is intentionally NOT checked here — the
* `(BX)` (cstage txt.c omits zero displacement) vs `0(BX)` (wwstage
@@ -41,6 +41,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -54,119 +55,6 @@ runwait(const char *cmd)
return -1;
}
struct row { const char *label; const char *src; int want; };
static const struct row rows[] = {
/* Singleton str field. Pre-fix p.s.len stayed 0; post-fix 5. */
{ "alloc_str_singleton",
"package main;\n"
"import rt;\n"
"type holder = struct { s: str };\n"
"fn main() i32 = {\n"
" let p: *holder = alloc(holder { s = \"hello\" })!;\n"
" return p.s.len;\n"
"};\n",
5 },
/* i32 field before str. Verifies field iteration restarts cleanly
* after the str-field branch and that the i32 store path is
* unaffected. Pre-fix: 100 + 0 = 100; post-fix: 100 + 2 = 102. */
{ "alloc_i32_then_str",
"package main;\n"
"import rt;\n"
"type holder = struct { n: i32, s: str };\n"
"fn main() i32 = {\n"
" let p: *holder = alloc(holder { n = 100, s = \"hi\" })!;\n"
" return p.n + p.s.len;\n"
"};\n",
102 },
/* str field before i32. Verifies foff>0 routing for the trailing
* i32 store after the str-field branch. Pre-fix: 0 + 7 = 7;
* post-fix: 5 + 7 = 12. */
{ "alloc_str_then_i32",
"package main;\n"
"import rt;\n"
"type holder = struct { s: str, n: i32 };\n"
"fn main() i32 = {\n"
" let p: *holder = alloc(holder { s = \"world\", n = 7 })!;\n"
" return p.s.len + p.n;\n"
"};\n",
12 },
/* Two str fields. Pre-fix both lens stayed 0; post-fix 3 + 6 = 9.
* Pins that `fi = nil` advances correctly between str fields. */
{ "alloc_two_str",
"package main;\n"
"import rt;\n"
"type holder = struct { a: str, b: str };\n"
"fn main() i32 = {\n"
" let p: *holder = alloc(holder { a = \"foo\", b = \"barbaz\" })!;\n"
" return p.a.len + p.b.len;\n"
"};\n",
9 },
/* Float-then-str-then-int. Pre-fix str.len=0 so result is
* 0 + 10 = 10; post-fix 3 + 10 = 13. Confirms the str-field
* branch slots between the existing float and integer branches
* without disrupting either. */
{ "alloc_f64_str_i32",
"package main;\n"
"import rt;\n"
"type holder = struct { f: f64, s: str, n: i32 };\n"
"fn main() i32 = {\n"
" let p: *holder = alloc(holder { f = 1.5f64, s = \"abc\", n = 10 })!;\n"
" return p.s.len + p.n;\n"
"};\n",
13 },
/* Read-back via p.s[i]: confirms str.ptr survived the store
* (foff=0 case) while still pinning str.len. Pre-fix len=0 so
* the loop body never executes; post-fix sum is 'h'+'i' = 209.
* Indexes the string by hand to avoid pulling in stdlib. */
{ "alloc_str_readback",
"package main;\n"
"import rt;\n"
"type holder = struct { s: str };\n"
"fn main() i32 = {\n"
" let p: *holder = alloc(holder { s = \"hi\" })!;\n"
" let sum: i32 = 0;\n"
" let i: i32 = 0;\n"
" for (i < p.s.len) {\n"
" sum += p.s[i]: i32;\n"
" i += 1;\n"
" };\n"
" return sum;\n"
"};\n",
209 },
};
/* run_driver — compile r->src via the given driver and exec; return
* the process exit code. Mirror of 703/704. */
static int
run_driver(const char *driver, const struct row *r, int i)
{
char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wcas_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/wcas_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/wcas_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s build -o %s %s", driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
runwait(rmcmd);
return -1;
}
int got = runwait(outbin);
runwait(rmcmd);
return got;
}
/* asm_row — fixture for the ffiresolve CALL-line check. `want_sym`
* is the unqualified symbol expected inside `CALL\t<sym>(SB)` from
* both stages' .s output. Single-file `w6c` compile (no `ww build`
@@ -287,15 +175,15 @@ asm_call_check(const char *bin, const struct asm_row *r, int i)
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "asm[%s]: w6c errored\n", r->label);
unlink(src);
return -1;
rc = -1;
goto cleanup;
}
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
bin, ws, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "asm[%s]: w6c_ww errored\n", r->label);
unlink(src); unlink(cs);
return -1;
rc = -1;
goto cleanup;
}
char want[64];
@@ -351,7 +239,25 @@ asm_call_check(const char *bin, const struct asm_row *r, int i)
}
if (fc) fclose(fc);
if (fw) fclose(fw);
unlink(src); unlink(cs); unlink(ws);
cleanup:;
/* a failing compile can still leave a partial .s — ENOENT is the
* only tolerable unlink error on the never-created legs. */
int cleanfail = 0;
if (unlink(src) != 0 && errno != ENOENT) {
perror(src);
cleanfail = 1;
}
if (unlink(cs) != 0 && errno != ENOENT) {
perror(cs);
cleanfail = 1;
}
if (unlink(ws) != 0 && errno != ENOENT) {
perror(ws);
cleanfail = 1;
}
if (cleanfail && rc == 0)
rc = -1;
return rc;
}
@@ -398,15 +304,15 @@ asm_disp_check(const char *bin, const struct asm_disp_row *r, int i)
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "disp[%s]: w6c errored\n", r->label);
unlink(src);
return -1;
rc = -1;
goto cleanup;
}
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
bin, ws, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "disp[%s]: w6c_ww errored\n", r->label);
unlink(src); unlink(cs);
return -1;
rc = -1;
goto cleanup;
}
int chit = file_contains(cs, r->want_line);
@@ -419,7 +325,24 @@ asm_disp_check(const char *bin, const struct asm_disp_row *r, int i)
rc = -1;
}
unlink(src); unlink(cs); unlink(ws);
cleanup:;
/* a failing compile can still leave a partial .s — ENOENT is the
* only tolerable unlink error on the never-created legs. */
int cleanfail = 0;
if (unlink(src) != 0 && errno != ENOENT) {
perror(src);
cleanfail = 1;
}
if (unlink(cs) != 0 && errno != ENOENT) {
perror(cs);
cleanfail = 1;
}
if (unlink(ws) != 0 && errno != ENOENT) {
perror(ws);
cleanfail = 1;
}
if (cleanfail && rc == 0)
rc = -1;
return rc;
}
@@ -436,41 +359,8 @@ main(void)
bin = absbin;
}
char cdrv[1024];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
char wdrv[1024];
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
struct { const char *name; const char *path; int gated_on_existence; }
drivers[] = {
{ "cstage", cdrv, 0 },
{ "wwstage", wdrv, 1 },
{ NULL, NULL, 0 },
};
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
for (int d = 0; drivers[d].name; d++) {
if (drivers[d].gated_on_existence
&& access(drivers[d].path, X_OK) != 0) {
fprintf(stderr, "cgalloc_str_field: skip %s (no %s)\n",
drivers[d].name, drivers[d].path);
continue;
}
for (int i = 0; i < n; i++) {
int got = run_driver(drivers[d].path, &rows[i], i);
total++;
if (got != rows[i].want) {
fprintf(stderr,
"cgalloc_str_field[%s][%s]: exit=%d want=%d\n",
drivers[d].name, rows[i].label,
got, rows[i].want);
fail++;
}
}
}
/* Asm CALL-line check via direct w6c / w6c_ww. Gated on w6c_ww
* existence — when wwstage isn't built yet the cstage half alone
* can't catch the divergence. */

View File

@@ -23,12 +23,8 @@
* folded value, so the emitted asm is byte-identical.
*
* Coverage:
* 1. EXEC (cstage `ww`, + wwstage `ww_ww` if built): i32 → int (a
* negative value, to pin sign-extension across the widen) and
* u64 → size (a value above the u32 range, to pin the full 8-byte
* slot) — each read back at a use site to prove the DATA row links
* and carries the right value.
* 2. BYTE-ID: cstage w6c vs wwstage w6c_ww `.s` for every exec row
* 1. The r76_def_widen_* fixtures own symmetric build/runtime semantics.
* 2. BYTE-ID: cstage w6c vs wwstage w6c_ww `.s` for every fixture row
* (proves the const-fold stamp is bit-identical across stages).
* 3. FAIL-LOUD (rule 7), cstage only: an out-of-range narrowing
* def-ref (`def S: i16 = BIG;` where BIG overflows i16) must still
@@ -53,81 +49,17 @@ runwait(const char *cmd)
return -1;
}
/* ---- 1. EXEC rows (compile+link+run) -------------------------------- */
struct row { const char *label; const char *src; int want; };
struct row { const char *label; const char *path; };
static const struct row exec_rows[] = {
/* i32 → int widening of a negative value: `def INT_MIN`-shaped.
* I32_MIN = -2147483648; read back as int it must sign-extend to
* the full 8-byte slot. main returns 42 iff the value round-trips. */
{ "i32-to-int-neg",
"def A_I32: i32 = -2147483648;\n"
"def A_INT: int = A_I32;\n"
"fn main() i32 = {\n"
"\tlet x: int = A_INT;\n"
"\tif (x == -2147483648) { return 42; };\n"
"\treturn 1;\n"
"};\n",
42 },
/* small negative i32 → int, to pin -7 specifically (the task's
* worked example). */
"test/wcc/data/r76_def_widen_i32_int_neg/case.ww" },
{ "i32-to-int-small",
"def A_I32: i32 = -7;\n"
"def A_INT: int = A_I32;\n"
"fn main() i32 = {\n"
"\tlet x: int = A_INT;\n"
"\tif (x == -7) { return 42; };\n"
"\treturn 1;\n"
"};\n",
42 },
/* u64 → size widening of a value above the u32 range: pins the
* full 8-byte slot survives the widen (0x1_0000_0003 = 4294967299). */
"test/wcc/data/r76_def_widen_i32_int_small/case.ww" },
{ "u64-to-size",
"def BIG_U64: u64 = 4294967299;\n"
"def S_SIZE: size = BIG_U64;\n"
"fn main() i32 = {\n"
"\tlet x: size = S_SIZE;\n"
"\tif (x == 4294967299) { return 42; };\n"
"\treturn 1;\n"
"};\n",
42 },
"test/wcc/data/r76_def_widen_u64_size/case.ww" },
};
static int
run_driver(const char *driver, const struct row *r, int i)
{
char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/dwc_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/dwc_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/dwc_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
wwtest_fputs(r->src, f);
fclose(f);
/* timeout 180 per repo convention; test/run does not bound
* individual binaries, so an unguarded hang would stall make test. */
snprintf(cmd, sizeof cmd, "timeout 180 %s build -o %s %s 2>/dev/null",
driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
runwait(rmcmd);
return -1;
}
int got = runwait(outbin);
runwait(rmcmd);
return got;
}
/* ---- shared .s emit -------------------------------------------------- */
static int
@@ -143,30 +75,23 @@ emit_s(const char *w6c, const char *src, char *out_s, size_t cap)
/* ---- 2. byte-identity of cstage vs wwstage .s ----------------------- */
static int
asm_byte_identical(const char *bin, const char *src, const char *label, int i)
asm_byte_identical(const char *bin, const struct row *r, int i)
{
char wwsrc[64], cs[64], ws[64];
snprintf(wwsrc, sizeof wwsrc, "/tmp/dwc_bi_%d_%d.ww", getpid(), i);
char cs[64], ws[64];
snprintf(cs, sizeof cs, "/tmp/dwc_bi_%d_%d_c.s", getpid(), i);
snprintf(ws, sizeof ws, "/tmp/dwc_bi_%d_%d_w.s", getpid(), i);
FILE *f = fopen(wwsrc, "wb");
if (!f) return -1;
wwtest_fputs(src, f);
fclose(f);
char w6c[640], w6c_ww[640];
snprintf(w6c, sizeof w6c, "%s/w6c", bin);
snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin);
if (emit_s(w6c, wwsrc, cs, sizeof cs) != 0) {
fprintf(stderr, "row[%s]: w6c errored\n", label);
unlink(wwsrc);
if (emit_s(w6c, r->path, cs, sizeof cs) != 0) {
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
return -1;
}
if (emit_s(w6c_ww, wwsrc, ws, sizeof ws) != 0) {
fprintf(stderr, "row[%s]: w6c_ww errored\n", label);
unlink(wwsrc); unlink(cs);
if (emit_s(w6c_ww, r->path, ws, sizeof ws) != 0) {
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
unlink(cs);
return -1;
}
@@ -187,8 +112,8 @@ asm_byte_identical(const char *bin, const char *src, const char *label, int i)
if (fw) fclose(fw);
if (rc != 0)
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
label);
unlink(wwsrc); unlink(cs); unlink(ws);
r->label);
unlink(cs); unlink(ws);
return rc;
}
@@ -246,48 +171,19 @@ main(void)
bin = absbin;
}
char cdrv[1024], wdrv[1024], w6c[1024], w6c_ww[1024];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
char w6c[1024], w6c_ww[1024];
snprintf(w6c, sizeof w6c, "%s/w6c", bin);
snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin);
int have_ww = (access(w6c_ww, X_OK) == 0);
int total = 0, fail = 0;
/* 1. exec via cstage `ww` (+ wwstage `ww_ww` if built) */
struct { const char *name; const char *path; int gated; }
drivers[] = {
{ "cstage", cdrv, 0 },
{ "wwstage", wdrv, 1 },
{ NULL, NULL, 0 },
};
int nexec = (int)(sizeof exec_rows / sizeof exec_rows[0]);
for (int d = 0; drivers[d].name; d++) {
if (drivers[d].gated && access(drivers[d].path, X_OK) != 0) {
fprintf(stderr, "def_widen_const: skip %s (no %s)\n",
drivers[d].name, drivers[d].path);
continue;
}
for (int i = 0; i < nexec; i++) {
int got = run_driver(drivers[d].path, &exec_rows[i], i);
total++;
if (got != exec_rows[i].want) {
fprintf(stderr,
"def_widen_const[%s][%s]: exit=%d want=%d\n",
drivers[d].name, exec_rows[i].label,
got, exec_rows[i].want);
fail++;
}
}
}
/* 2. byte-id on every exec row (only when wwstage is built) */
/* Runtime rows moved to r76_def_widen_*; retain byte-id and cstage reject. */
if (have_ww) {
for (int i = 0; i < nexec; i++) {
total++;
if (asm_byte_identical(bin, exec_rows[i].src,
exec_rows[i].label, i) != 0)
if (asm_byte_identical(bin, &exec_rows[i], i) != 0)
fail++;
}
}

View File

@@ -40,16 +40,13 @@
* 5. fn-with-tuple-return — (i64, str) return shape
* 6. cross-module — `let f = &pkg.fn` (Phase 1 = FINE)
*
* Gates per row:
* a. cstage builds + runs (exit 0); proves the LEAQ emits and
* frame layout survives the AX-store (pre-fix this was junk
* data, not necessarily a segfault — the segfault only fired
* on the deref-call).
* b. cstage .s contains the expected `LEAQ <fnname>(SB)` line —
* Runtime for the five symmetric rows now lives in r764_* fixtures.
* This artifact wrapper retains:
* a. cstage .s contains the expected `LEAQ <fnname>(SB)` line —
* pre-fix that line is absent (silent drop).
* c. wwstage builds + runs, if wwstage driver present.
* d. cstage .s == wwstage .s byte-identical — symmetry gate per
* b. cstage .s == wwstage .s byte-identical — symmetry gate per
* CLAUDE.md rule 10.
* c. the cross-module C-stage-only runtime gate pending #184.
*
* GATE POLARITY: must stay GREEN. A red here means either the cgen
* TK_AMP IDENT TY_FN arm regressed, or stage symmetry drifted.
@@ -57,6 +54,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -79,6 +77,8 @@ runwait(const char *cmd)
struct row {
const char *label;
/* Symmetric rows use their corpus source as the sole fixture body. */
const char *fixture;
/* Source written into <tmpdir>/<basename>.ww; for cross-mod
* (row 6) the secondary module lives at <tmpdir>/<modname>/
* <modname>.ww. modname/modsrc NULL for single-file rows. */
@@ -94,51 +94,32 @@ struct row {
static const struct row rows[] = {
{ "minimal",
"fn add1(x: i32) i32 = { return x + 1; };\n"
"export fn main() i32 = {\n"
" let f = &add1;\n"
" return 0;\n"
"};\n",
"test/wcc/data/r764_amp_fn_ident_minimal/case.ww",
NULL,
NULL, NULL,
"LEAQ\tmain.add1(SB)",
STAGE_CS | STAGE_WW },
{ "branched_callee",
"fn aa(x: i32) i32 = { return x; };\n"
"fn bb(x: i32) i32 = { return x + 1; };\n"
"export fn main() i32 = {\n"
" let pick: i32 = 1;\n"
" let f = &aa;\n"
" if (pick != 0) { f = &bb; };\n"
" return 0;\n"
"};\n",
"test/wcc/data/r764_amp_fn_ident_branched/case.ww",
NULL,
NULL, NULL,
"LEAQ\tmain.bb(SB)",
STAGE_CS | STAGE_WW },
{ "alias_chain",
"fn add1(x: i32) i32 = { return x + 1; };\n"
"export fn main() i32 = {\n"
" let f = &add1;\n"
" let g = f;\n"
" return 0;\n"
"};\n",
"test/wcc/data/r764_amp_fn_ident_alias_chain/case.ww",
NULL,
NULL, NULL,
"LEAQ\tmain.add1(SB)",
STAGE_CS | STAGE_WW },
{ "fn_with_args",
"fn many(a: i32, b: i64, p: *i32) i64 = { return b; };\n"
"export fn main() i32 = {\n"
" let f = &many;\n"
" return 0;\n"
"};\n",
"test/wcc/data/r764_amp_fn_ident_args/case.ww",
NULL,
NULL, NULL,
"LEAQ\tmain.many(SB)",
STAGE_CS | STAGE_WW },
{ "fn_tuple_return",
"fn pair() (i64, str) = { return (7: i64, \"x\"); };\n"
"export fn main() i32 = {\n"
" let f = &pair;\n"
" return 0;\n"
"};\n",
"test/wcc/data/r764_amp_fn_ident_tuple_return/case.ww",
NULL,
NULL, NULL,
"LEAQ\tmain.pair(SB)",
STAGE_CS | STAGE_WW },
@@ -151,6 +132,7 @@ static const struct row rows[] = {
* &-of hint routes through use_hint and the LEAQ matches the
* mangled definition. */
{ "cross_module",
NULL,
"import wcamffn764mod;\n"
"export fn main() i32 = {\n"
" let f = &wcamffn764mod.somefn;\n"
@@ -163,73 +145,125 @@ static const struct row rows[] = {
STAGE_CS },
};
/* write_sources — writes the row's main source plus, for cross-mod
* rows, the secondary module file. Returns the basename of the main
* source (without .ww) via *base_out. */
static int cleanup_sources(const struct row *, const char *, const char *, int);
/* write_sources — copies a migrated fixture into an isolated package-main
* root, or writes the one asymmetric cross-module row into that tree. */
static int
write_sources(const struct row *r, char *src, size_t srcsz,
char *tmpdir, size_t tdsz, int seq, char *base_out, size_t basz)
{
snprintf(tmpdir, tdsz, "/tmp/wcamffn_%d_d_%d", getpid(), seq);
(void)seq;
int moddir_owned = 0;
snprintf(tmpdir, tdsz, "/tmp/wcamffn_XXXXXX");
if (mkdtemp(tmpdir) == NULL) return -1;
snprintf(src, srcsz, "%s/main764.ww", tmpdir);
snprintf(base_out, basz, "main764");
mkdir(tmpdir, 0755);
if (r->fixture != NULL) {
FILE *in = fopen(r->fixture, "rb");
if (in == NULL) goto fail;
if (fseek(in, 0, SEEK_END) != 0) { fclose(in); goto fail; }
long n = ftell(in);
if (n < 0 || fseek(in, 0, SEEK_SET) != 0) {
fclose(in);
goto fail;
}
char *body = malloc((size_t)n + 1);
if (body == NULL) { fclose(in); goto fail; }
size_t got = fread(body, 1, (size_t)n, in);
int readerr = ferror(in);
fclose(in);
if (got != (size_t)n || readerr) { free(body); goto fail; }
body[got] = '\0';
FILE *out = fopen(src, "wb");
if (out == NULL) { free(body); goto fail; }
wwtest_fputs(body, out);
int wr = ferror(out) ? -1 : 0;
free(body);
if (fclose(out) != 0) wr = -1;
if (wr != 0) goto fail;
return 0;
}
if (r->modname != NULL) {
char moddir[256], modfile[256];
snprintf(moddir, sizeof moddir, "%s/%s", tmpdir, r->modname);
snprintf(modfile, sizeof modfile, "%s/%s.ww",
moddir, r->modname);
mkdir(moddir, 0755);
if (mkdir(moddir, 0755) != 0) goto fail;
moddir_owned = 1;
FILE *mf = fopen(modfile, "wb");
if (!mf) return -1;
if (!mf) goto fail;
wwtest_fputs(r->modsrc, mf);
fclose(mf);
int wr = ferror(mf) ? -1 : 0;
if (fclose(mf) != 0) wr = -1;
if (wr != 0) goto fail;
}
FILE *f = fopen(src, "wb");
if (!f) return -1;
if (!f) goto fail;
wwtest_fputs(r->src, f);
fclose(f);
int wr = ferror(f) ? -1 : 0;
if (fclose(f) != 0) wr = -1;
if (wr != 0) goto fail;
return 0;
fail:
if (cleanup_sources(r, tmpdir, "main764", moddir_owned) != 0)
fprintf(stderr, "amp_fn_ident: setup cleanup failed for %s\n", tmpdir);
return -1;
}
static void
cleanup_sources(const struct row *r, const char *tmpdir, const char *base)
static int
cleanup_sources(const struct row *r, const char *tmpdir, const char *base,
int moddir_owned)
{
char p[512];
snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "%s/%s.s", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "%s/%s.o", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "%s/%s", tmpdir, base); unlink(p);
/* #93: the sep scratch dir + the tmpdir-pinned pkgcache. */
snprintf(p, sizeof p, "rm -rf %s/%s.sepwork %s/pkgc", tmpdir, base, tmpdir);
if (system(p) != 0) { /* best-effort */ }
if (r->modname != NULL) {
int rc = 0;
snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base);
if (unlink(p) != 0 && errno != ENOENT) rc = -1;
snprintf(p, sizeof p, "%s/%s", tmpdir, base);
if (unlink(p) != 0 && errno != ENOENT) rc = -1;
/* #93: the sep scratch dir + the tmpdir-owned outputs. */
snprintf(p, sizeof p, "rm -rf %s/%s.sepwork", tmpdir, base);
if (runwait(p) != 0) rc = -1;
if (moddir_owned) {
char moddir[256];
snprintf(moddir, sizeof moddir, "%s/%s", tmpdir, r->modname);
snprintf(p, sizeof p, "%s/%s.ww", moddir, r->modname); unlink(p);
rmdir(moddir);
snprintf(p, sizeof p, "%s/%s.ww", moddir, r->modname);
if (unlink(p) != 0 && errno != ENOENT) rc = -1;
if (rmdir(moddir) != 0 && errno != ENOENT) rc = -1;
}
rmdir(tmpdir);
if (rmdir(tmpdir) != 0) rc = -1;
return rc;
}
/* compile_via_driver — runs compiler-only driver output in <tmpdir>; returns
* the compiler exit code without assembling or linking. */
static int
compile_via_driver(const char *driver, const char *tmpdir, const char *src)
{
/* #93 sep layout: `-o <src-stem>` emits the asm to
* <stem>.sepwork/__root.s (the post-flip layout); all outputs stay
* under tmpdir. */
char cmd[2048];
snprintf(cmd, sizeof cmd,
"cd %s && timeout 180 %s build -S -o %s/main764 %s "
"2>/dev/null", tmpdir, driver, tmpdir, src);
return runwait(cmd);
}
/* build_via_driver — runs `<driver> build <src>` in <tmpdir>; returns
* the build exit code. */
static int
build_via_driver(const char *driver, const char *tmpdir, const char *src)
{
/* #93 sep layout: `--sep -o <src-stem>` emits the asm to
* <stem>.sepwork/__root.s (the post-flip layout); WW_PKGCACHE is
* pinned under tmpdir so the shared out/.pkgcache is untouched and
* the cache dies with the tmpdir. */
char stem[512], cmd[1280];
snprintf(stem, sizeof stem, "%s", src);
char *dot = strrchr(stem, '.');
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
snprintf(cmd, sizeof cmd,
"cd %s && WW_PKGCACHE=%s/pkgc timeout 180 %s build --sep -o %s %s "
"2>/dev/null", tmpdir, tmpdir, driver, stem, src);
"cd %s && timeout 180 %s build -o %s %s "
"2>/dev/null", tmpdir, driver, stem, src);
return runwait(cmd);
}
@@ -247,7 +281,10 @@ run_row(const char *driver, const struct row *r, int seq)
snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base);
if (runwait(outbin) == 0) rc = 0;
}
cleanup_sources(r, tmpdir, base);
if (cleanup_sources(r, tmpdir, base, r->modname != NULL) != 0) {
fprintf(stderr, "amp_fn_ident[%s]: cleanup failed\n", r->label);
rc = -1;
}
return rc;
}
@@ -277,11 +314,14 @@ check_leaq(const char *driver, const struct row *r, int seq)
seq, base, sizeof base) != 0)
return -1;
int rc = -1;
if (build_via_driver(driver, tmpdir, src) == 0) {
if (compile_via_driver(driver, tmpdir, src) == 0) {
snprintf(asmf, sizeof asmf, "%s/%s.sepwork/__root.s", tmpdir, base);
if (file_contains(asmf, r->expected_leaq)) rc = 0;
}
cleanup_sources(r, tmpdir, base);
if (cleanup_sources(r, tmpdir, base, r->modname != NULL) != 0) {
fprintf(stderr, "amp_fn_ident[%s]: cleanup failed\n", r->label);
rc = -1;
}
return rc;
}
@@ -295,17 +335,17 @@ asm_byte_identical(const char *cdrv, const char *wdrv,
seq, base, sizeof base) != 0)
return -1;
int rc = -1;
if (build_via_driver(cdrv, tdc, src) != 0) goto out;
int have_tdw = 0;
if (compile_via_driver(cdrv, tdc, src) != 0) goto out;
snprintf(cs, sizeof cs, "%s/%s.sepwork/__root.s", tdc, base);
/* Build a parallel tree for wwstage so we don't clobber the
* cstage .s. ww_ww writes intermediates next to the .ww source
* (filed bug per CLAUDE.md rule 14 phase split). */
/* Build a parallel tree so each driver's explicitly retained
* <stem>.sepwork/__root.s remains available for comparison. */
if (write_sources(r, src, sizeof src, tdw, sizeof tdw,
seq + 100000, base, sizeof base) != 0)
goto out;
if (build_via_driver(wdrv, tdw, src) != 0) {
cleanup_sources(r, tdw, base);
have_tdw = 1;
if (compile_via_driver(wdrv, tdw, src) != 0) {
goto out;
}
snprintf(ws, sizeof ws, "%s/%s.sepwork/__root.s", tdw, base);
@@ -323,9 +363,16 @@ asm_byte_identical(const char *cdrv, const char *wdrv,
}
if (fc) fclose(fc);
if (fw) fclose(fw);
cleanup_sources(r, tdw, base);
out:
cleanup_sources(r, tdc, base);
if (have_tdw
&& cleanup_sources(r, tdw, base, r->modname != NULL) != 0) {
fprintf(stderr, "amp_fn_ident[%s]: ww cleanup failed\n", r->label);
rc = -1;
}
if (cleanup_sources(r, tdc, base, r->modname != NULL) != 0) {
fprintf(stderr, "amp_fn_ident[%s]: c cleanup failed\n", r->label);
rc = -1;
}
return rc;
}
@@ -353,7 +400,10 @@ main(void)
int seq = 0;
for (int i = 0; i < n; i++) {
if (rows[i].stage_mask & STAGE_CS) {
/* Symmetric runtime rows are owned by r764_* fixtures. Keep
* only the cross-module C-stage-only runtime evidence here. */
if ((rows[i].stage_mask & STAGE_CS)
&& !(rows[i].stage_mask & STAGE_WW)) {
total++;
if (run_row(cdrv, &rows[i], seq++) != 0) {
fprintf(stderr,
@@ -361,6 +411,8 @@ main(void)
rows[i].label);
fail++;
}
}
if (rows[i].stage_mask & STAGE_CS) {
total++;
if (check_leaq(cdrv, &rows[i], seq++) != 0) {
fprintf(stderr,
@@ -370,13 +422,6 @@ main(void)
}
}
if (wwpresent && (rows[i].stage_mask & STAGE_WW)) {
total++;
if (run_row(wdrv, &rows[i], seq++) != 0) {
fprintf(stderr,
"amp_fn_ident[wwstage run][%s]: build/run failed\n",
rows[i].label);
fail++;
}
total++;
if (asm_byte_identical(cdrv, wdrv, &rows[i], seq++) != 0) {
fprintf(stderr,

View File

@@ -57,6 +57,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -146,16 +147,24 @@ write_source(const char *src_path, const char *src)
FILE *f = fopen(src_path, "wb");
if (!f) return -1;
wwtest_fputs(src, f);
fclose(f);
return 0;
int rc = ferror(f) ? -1 : 0;
if (fclose(f) != 0) rc = -1;
return rc;
}
static void
static int
cleanup_tmp(const char *tmpdir)
{
char p[512];
snprintf(p, sizeof p, "rm -rf %s", tmpdir);
runwait(p);
int rc = 0;
snprintf(p, sizeof p, "%s/main765.ww", tmpdir);
if (unlink(p) != 0 && errno != ENOENT) rc = -1;
snprintf(p, sizeof p, "%s/main765", tmpdir);
if (unlink(p) != 0 && errno != ENOENT) rc = -1;
snprintf(p, sizeof p, "rm -rf %s/main765.sepwork", tmpdir);
if (runwait(p) != 0) rc = -1;
if (rmdir(tmpdir) != 0) rc = -1;
return rc;
}
static int
@@ -172,19 +181,23 @@ build_via_driver(const char *driver, const char *outbin, const char *src)
static int
run_row(const char *driver, const struct row *r, int seq)
{
char tmpdir[256], src[256], outbin[512];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wcsfd_%d_d_%d", getpid(), seq);
(void)seq;
char tmpdir[] = "/tmp/wcsfd_XXXXXX";
char src[256], outbin[512];
if (mkdtemp(tmpdir) == NULL) return -1;
snprintf(src, sizeof src, "%s/main765.ww", tmpdir);
snprintf(outbin, sizeof outbin, "%s/main765", tmpdir);
mkdir(tmpdir, 0755);
if (write_source(src, r->src) != 0) {
cleanup_tmp(tmpdir);
return -1;
}
int rc = -1;
if (write_source(src, r->src) != 0) {
goto out;
}
if (build_via_driver(driver, outbin, src) == 0)
rc = runwait(outbin);
cleanup_tmp(tmpdir);
out:
if (cleanup_tmp(tmpdir) != 0) {
fprintf(stderr, "star_fn_deref[%s]: cleanup failed\n", r->label);
rc = -1;
}
return rc;
}

View File

@@ -1,96 +1,27 @@
/*
* 771_widen_transitive — project #199 (α): cstage type_assignable +
* wwstage isassignable must REJECT transitive nested-tagged widen.
* Cgen has no wrapped-slot layout (#199b deferred future-work); the
* pre-α recursive `type_assignable(p->type, src)` walk admitted
* `let r: (size|io.eof|io.error) = u` for u: io.underread, then cgen
* silently miscompiled (cg_tag_for_variant returned -1 → tag=0 → r
* reads as variant 0 = size at runtime).
* 771_widen_transitive — residual artifact owner.
*
* Fix (cmd/wcc/type.c:308 + selfhost/cmd/wcc/check.ww:3063): the
* tagged-non-tagged arm now matches direct variants ONLY. NAMED-tagged
* wrapper variants (vu.kind == TY_TAGGED) compare nominally (type_eq /
* typeeqast); other variants keep the leaf recursion for primitive
* compat / untyped resolution. wwstage additionally preserves the
* spread-recursion path: `...wrapper` variants (op == TK_ELLIPSIS) drill
* through because the AST list hasn't pre-flattened (cstage flattens at
* resolve_type, wwstage stays AST-keyed).
*
* Restores SSoT inside the checker pair: `is` / `as` / `match` arm
* lookup is already non-recursive (#198 sibling), and the LET-init /
* return / assign arms now agree. Aligns DOWN to the leaner side
* (rule-10 stage symmetry). ww-stricter than Hare; harec keeps the
* drill at ref/harec/src/types.c:702-739 (#199b — wrapped-slot layout
* port deferred per drew).
*
* Coverage (5 rows):
* 1. reject_transitive_widen — `let r: (size|wrapper) = u` with
* u: underread, wrapper = !(underread | nomem). BOTH stages
* MUST reject at the checker. The repro.
* 2. spread_alt_widen — `let r: (size|...wrapper) = u;`. The
* `...` spread inlines wrapper's variants into the parent flat
* set, so underread becomes a DIRECT variant; widen succeeds.
* CS-only runtime: wwstage's `is`/`match` cross-spread variant
* lookup is open-bug #190/#198 (wwstage walks original AST list,
* doesn't see expanded variants); byte-id still gates the asm.
* 3. direct_flat_variant — `let r: (size|underread|nomem) = u`
* with the underlying variants flat in the parent (no wrapper).
* Regression gate: direct widen unchanged by the fix.
* 4. branched_callee_widen — fn returns (size|underread|nomem),
* runtime picks between two flat-variant returns. Verifies the
* cg_widen_tagged_store scalar branch's tag synthesis is
* unchanged for direct widens.
* 5. wrapper_typed_widen — `let r: wrapper = u` with u:
* underread (DIRECT variant of wrapper). Regression gate: the
* single-level wrapper widen is the supported escape hatch and
* MUST continue working.
*
* Per-row gates: cstage compile + runtime, wwstage compile + runtime,
* cs.s == ww.s byte-identical (rule-10). Row 1 uses
* expected_exit = -2 to indicate "build must fail" — the driver
* invocation is expected to return non-zero from the checker.
* Symmetric runtime and rejection coverage lives in wwfixture under
* test/wcc/data/r771_*. This wrapper retains only compiler-output byte
* identity plus the intentionally C-stage-only spread runtime row.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.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;
}
#define STAGE_CS 1
#define STAGE_WW 2
#define EXPECT_REJECT (-2)
struct row {
const char *label;
const char *src;
int expected_exit; /* EXPECT_REJECT means build must fail */
int stage_mask;
int byte_id; /* run cs.s == ww.s diff (works rows only) */
const char *fixture;
int byte_id;
int cstage_run;
int expected_exit;
};
static const struct row rows[] = {
{ "reject_transitive_widen",
"package main;\n"
"type underread = !i32;\n"
"type nomem = !void;\n"
"type wrapper = !(underread | nomem);\n"
"export fn main() i32 = {\n"
" let u: underread;\n"
" let r: (size | wrapper) = u;\n"
" return 0;\n"
"};\n",
EXPECT_REJECT,
STAGE_CS | STAGE_WW, 0 },
{ "spread_alt_widen",
"package main;\n"
"type underread = !i32;\n"
@@ -102,145 +33,143 @@ static const struct row rows[] = {
" if (r is underread) { return 70; };\n"
" return 99;\n"
"};\n",
70,
STAGE_CS, 0 },
NULL, 0, 1, 70 },
{ "direct_flat_variant",
"package main;\n"
"type underread = !i32;\n"
"type nomem = !void;\n"
"export fn main() i32 = {\n"
" let u: underread;\n"
" let r: (size | underread | nomem) = u;\n"
" if (r is underread) { return 71; };\n"
" return 99;\n"
"};\n",
71,
STAGE_CS | STAGE_WW, 1 },
NULL, "test/wcc/data/r771_direct_flat_variant/case.ww",
1, 0, 71 },
{ "branched_callee_widen",
"package main;\n"
"type underread = !i32;\n"
"type nomem = !void;\n"
"fn pick(b: bool) (size | underread | nomem) = {\n"
" if (b) { let u: underread; return u; };\n"
" return 42: size;\n"
"};\n"
"export fn main() i32 = {\n"
" let r: (size | underread | nomem) = pick(true);\n"
" if (r is underread) { return 72; };\n"
" return 99;\n"
"};\n",
72,
STAGE_CS | STAGE_WW, 1 },
NULL, "test/wcc/data/r771_branched_callee_widen/case.ww",
1, 0, 72 },
{ "wrapper_typed_widen",
"package main;\n"
"type underread = !i32;\n"
"type nomem = !void;\n"
"type wrapper = !(underread | nomem);\n"
"export fn main() i32 = {\n"
" let u: underread;\n"
" let r: wrapper = u;\n"
" if (r is underread) { return 73; };\n"
" return 99;\n"
"};\n",
73,
STAGE_CS | STAGE_WW, 1 },
NULL, "test/wcc/data/r771_wrapper_typed_widen/case.ww",
1, 0, 73 },
};
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
static int
write_source(const char *path, const char *src)
{
FILE *f = fopen(path, "wb");
if (!f) return -1;
fputs(src, f);
fclose(f);
return 0;
}
static void
cleanup_tmp(const char *tmpdir, const char *base)
{
char p[512];
snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "%s/%s.o", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "%s/%s", tmpdir, base); unlink(p);
/* #93: the sep scratch dir + the tmpdir-pinned pkgcache. */
snprintf(p, sizeof p, "rm -rf %s/%s.sepwork %s/pkgc", tmpdir, base, tmpdir);
if (system(p)) {} /* best-effort */
rmdir(tmpdir);
int rc = fputs(src, f) == EOF ? -1 : 0;
if (fclose(f) != 0) rc = -1;
return rc;
}
static int
build_via_driver(const char *driver, const char *tmpdir, const char *src)
materialize_source(const char *cwd, const struct row *r, const char *dst)
{
char cmd[1024];
/* #93 sep layout: emit asm to <src-stem>.sepwork/__root.s; pin
* WW_PKGCACHE under tmpdir so out/.pkgcache is untouched. */
if (r->src) return write_source(dst, r->src);
char path[1024], buf[4096];
snprintf(path, sizeof path, "%s/%s", cwd, r->fixture);
FILE *in = fopen(path, "rb");
if (!in) return -1;
FILE *out = fopen(dst, "wb");
if (!out) { fclose(in); return -1; }
int rc = 0;
for (;;) {
size_t n = fread(buf, 1, sizeof buf, in);
if (n != 0 && fwrite(buf, 1, n, out) != n) { rc = -1; break; }
if (n < sizeof buf) {
if (ferror(in)) rc = -1;
break;
}
}
if (fclose(out) != 0) rc = -1;
fclose(in);
return rc;
}
static int
cleanup_tmp(const char *tmpdir, const char *base)
{
char p[640];
int rc = 0;
snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base);
if (unlink(p) != 0 && errno != ENOENT) rc = -1;
snprintf(p, sizeof p, "%s/%s", tmpdir, base);
if (unlink(p) != 0 && errno != ENOENT) rc = -1;
snprintf(p, sizeof p, "rm -rf %s/%s.sepwork", tmpdir, base);
if (runwait(p) != 0) rc = -1;
if (rmdir(tmpdir) != 0) rc = -1;
return rc;
}
static int
build_via_driver(const char *driver, const char *tmpdir, const char *cwd,
const char *src, int asm_only)
{
char cmd[2048];
(void)cwd;
snprintf(cmd, sizeof cmd,
"cd %s && b='%s'; WW_PKGCACHE=%s/pkgc timeout 180 %s build --sep "
"cd %s && b='%s'; timeout 180 %s build %s"
"-o \"${b%%.ww}\" \"$b\" 2>/dev/null",
tmpdir, src, tmpdir, driver);
tmpdir, src, driver, asm_only ? "-S " : "");
return runwait(cmd);
}
static int
run_row(const char *driver, const struct row *r, int seq)
run_cstage(const char *driver, const char *cwd, const struct row *r, int seq)
{
char tmpdir[256], src[512], base[64], outbin[768];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wt_%d_d_%d", getpid(), seq);
snprintf(src, sizeof src, "%s/main771.ww", tmpdir);
snprintf(base, sizeof base, "main771");
mkdir(tmpdir, 0755);
if (write_source(src, r->src) != 0) {
cleanup_tmp(tmpdir, base);
return -1;
(void)seq;
char td[] = "/tmp/r771_r_XXXXXX";
char src[512], base[64], exe[768];
if (mkdtemp(td) == NULL) return -1;
snprintf(base, sizeof base, "main771");
snprintf(src, sizeof src, "%s/%s.ww", td, base);
int rc = -1;
if (materialize_source(cwd, r, src) != 0) goto out;
rc = build_via_driver(driver, td, cwd, src, 0);
if (rc == 0) {
snprintf(exe, sizeof exe, "%s/%s", td, base);
rc = runwait(exe);
}
int rc;
int br = build_via_driver(driver, tmpdir, src);
if (r->expected_exit == EXPECT_REJECT) {
/* Build must fail with a non-zero exit (checker error). */
rc = (br != 0) ? r->expected_exit : 0;
} else if (br == 0) {
snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base);
rc = runwait(outbin);
} else {
out:
if (cleanup_tmp(td, base) != 0) {
fprintf(stderr, "widen_transitive[%s]: run cleanup failed\n",
r->label);
rc = -1;
}
cleanup_tmp(tmpdir, base);
return rc;
}
/* asm_byte_identical — diff cstage vs wwstage .s. Parallel trees so
* ww_ww writing intermediates next to the source doesn't clobber the
* cstage .s (CLAUDE.md rule 14 phase split). */
static int
asm_byte_identical(const char *cdrv, const char *wdrv,
asm_byte_identical(const char *cdrv, const char *wdrv, const char *cwd,
const struct row *r, int seq)
{
char src[512], tdc[256], tdw[256], base[64], cs[512], ws[512];
snprintf(tdc, sizeof tdc, "/tmp/wt_%d_c_%d", getpid(), seq);
snprintf(tdw, sizeof tdw, "/tmp/wt_%d_w_%d", getpid(), seq);
(void)seq;
char tc[] = "/tmp/r771_c_XXXXXX";
char tw[] = "/tmp/r771_w_XXXXXX";
char src[512], base[64], cs[512], ws[512];
snprintf(base, sizeof base, "main771");
mkdir(tdc, 0755);
mkdir(tdw, 0755);
snprintf(src, sizeof src, "%s/main771.ww", tdc);
if (write_source(src, r->src) != 0) { cleanup_tmp(tdc, base); cleanup_tmp(tdw, base); return -1; }
if (mkdtemp(tc) == NULL) return -1;
if (mkdtemp(tw) == NULL) {
if (cleanup_tmp(tc, base) != 0)
fprintf(stderr, "widen_transitive[%s]: setup cleanup failed\n",
r->label);
return -1;
}
int rc = -1;
if (build_via_driver(cdrv, tdc, src) != 0) goto out;
snprintf(cs, sizeof cs, "%s/%s.sepwork/__root.s", tdc, base);
snprintf(src, sizeof src, "%s/main771.ww", tdw);
if (write_source(src, r->src) != 0) goto out;
if (build_via_driver(wdrv, tdw, src) != 0) goto out;
snprintf(ws, sizeof ws, "%s/%s.sepwork/__root.s", tdw, base);
FILE *fc = fopen(cs, "rb");
FILE *fw = fopen(ws, "rb");
snprintf(src, sizeof src, "%s/%s.ww", tc, base);
if (materialize_source(cwd, r, src) != 0) goto out;
if (build_via_driver(cdrv, tc, cwd, src, 1) != 0) goto out;
snprintf(cs, sizeof cs, "%s/%s.sepwork/__root.s", tc, base);
snprintf(src, sizeof src, "%s/%s.ww", tw, base);
if (materialize_source(cwd, r, src) != 0) goto out;
if (build_via_driver(wdrv, tw, cwd, src, 1) != 0) goto out;
snprintf(ws, sizeof ws, "%s/%s.sepwork/__root.s", tw, base);
FILE *fc = fopen(cs, "rb"), *fw = fopen(ws, "rb");
if (fc && fw) {
rc = 0;
for (;;) {
int a = fgetc(fc);
int b = fgetc(fw);
int a = fgetc(fc), b = fgetc(fw);
if (a != b) { rc = -1; break; }
if (a == EOF) break;
}
@@ -248,73 +177,58 @@ asm_byte_identical(const char *cdrv, const char *wdrv,
if (fc) fclose(fc);
if (fw) fclose(fw);
out:
cleanup_tmp(tdc, base);
cleanup_tmp(tdw, base);
if (cleanup_tmp(tc, base) != 0) {
fprintf(stderr, "widen_transitive[%s]: c cleanup failed\n", r->label);
rc = -1;
}
if (cleanup_tmp(tw, base) != 0) {
fprintf(stderr, "widen_transitive[%s]: ww cleanup failed\n", r->label);
rc = -1;
}
return rc;
}
int
main(void)
{
char cwd[512];
if (!getcwd(cwd, sizeof cwd)) return 1;
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
char absbin[512];
char absbin[1024];
if (bin[0] != '/') {
char cwd[256];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
char cdrv[640], wdrv[640];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
char cdrv[1200], wdrv[1200];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
int wwpresent = (access(wdrv, X_OK) == 0);
int seq = 0;
for (int i = 0; i < n; i++) {
if (rows[i].stage_mask & STAGE_CS) {
int fail = 0, total = 0, seq = 0;
int wwpresent = access(wdrv, X_OK) == 0;
for (size_t i = 0; i < sizeof rows / sizeof rows[0]; i++) {
if (rows[i].cstage_run) {
total++;
int got = run_row(cdrv, &rows[i], seq++);
int got = run_cstage(cdrv, cwd, &rows[i], seq++);
if (got != rows[i].expected_exit) {
fprintf(stderr,
"widen_transitive[cstage run][%s]: exit=%d want=%d\n",
fprintf(stderr, "widen_transitive[cstage-only run][%s]: exit=%d want=%d\n",
rows[i].label, got, rows[i].expected_exit);
fail++;
}
}
if (wwpresent && (rows[i].stage_mask & STAGE_WW)) {
if (wwpresent && rows[i].byte_id) {
total++;
int got = run_row(wdrv, &rows[i], seq++);
if (got != rows[i].expected_exit) {
fprintf(stderr,
"widen_transitive[wwstage run][%s]: exit=%d want=%d\n",
rows[i].label, got, rows[i].expected_exit);
if (asm_byte_identical(cdrv, wdrv, cwd, &rows[i], seq++) != 0) {
fprintf(stderr, "widen_transitive[byte-id][%s]: assembly differs\n",
rows[i].label);
fail++;
}
if (rows[i].byte_id) {
total++;
if (asm_byte_identical(cdrv, wdrv, &rows[i], seq++) != 0) {
fprintf(stderr,
"widen_transitive[byte-id][%s]: cstage vs wwstage asm differs\n",
rows[i].label);
fail++;
}
}
}
}
if (!wwpresent)
fprintf(stderr, "widen_transitive: skip wwstage (no %s)\n", wdrv);
if (!wwpresent) fprintf(stderr, "widen_transitive: skip byte-id (no %s)\n", wdrv);
if (fail) {
fprintf(stderr, "widen_transitive: %d/%d fixtures failed\n",
fail, total);
fprintf(stderr, "widen_transitive: %d/%d residual gates failed\n", fail, total);
return 1;
}
printf("widen_transitive: %d/%d ok\n", total, total);
printf("widen_transitive: %d/%d residual gates ok\n", total, total);
return 0;
}

View File

@@ -87,6 +87,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -189,25 +190,27 @@ write_source(const char *path, const char *src)
{
FILE *f = fopen(path, "wb");
if (!f) return -1;
fputs(src, f);
fclose(f);
return 0;
int bad = fputs(src, f) == EOF || ferror(f);
if (fclose(f) != 0) bad = 1;
return bad ? -1 : 0;
}
/* Per-row tmpdir cleanup. ww_ww writes intermediates next to the
* source (filed task #15), so each row's build leaves
* <src>.{combined.ww,s,o} + bare exe alongside. Sweep all then
* rmdir. Mirror of 778's cleanup_tmp. */
static void
/* Per-row tmpdir cleanup. `ww build` retains its exact
* <base>.sepwork tree; remove that tree, the source, and the bare
* executable before removing the invocation-owned directory. */
static int
cleanup_tmp(const char *tmpdir, const char *base)
{
char p[640];
snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "%s/%s.s", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "%s/%s.o", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "%s/%s", tmpdir, base); unlink(p);
rmdir(tmpdir);
int rc = 0;
snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base);
if (unlink(p) != 0 && errno != ENOENT) rc = -1;
snprintf(p, sizeof p, "rm -rf -- %s/%s.sepwork", tmpdir, base);
if (runwait(p) != 0) rc = -1;
snprintf(p, sizeof p, "%s/%s", tmpdir, base);
if (unlink(p) != 0 && errno != ENOENT) rc = -1;
if (rmdir(tmpdir) != 0) rc = -1;
return rc;
}
static int
@@ -228,8 +231,12 @@ run_row(const char *driver, const char *cwd, const struct row *r, int seq)
snprintf(tmpdir, sizeof tmpdir, "/tmp/lvs_%d_d_%d", getpid(), seq);
snprintf(base, sizeof base, "main779");
snprintf(src, sizeof src, "%s/%s.ww", tmpdir, base);
mkdir(tmpdir, 0755);
if (write_source(src, r->src) != 0) { cleanup_tmp(tmpdir, base); return -1; }
if (mkdir(tmpdir, 0755) != 0) return -1;
if (write_source(src, r->src) != 0) {
if (cleanup_tmp(tmpdir, base) != 0)
fprintf(stderr, "row[%s]: setup cleanup failed\n", r->label);
return -1;
}
int rc;
int br = build_via_driver(driver, tmpdir, cwd, src);
if (br == 0) {
@@ -243,13 +250,12 @@ run_row(const char *driver, const char *cwd, const struct row *r, int seq)
} else {
rc = -1;
}
cleanup_tmp(tmpdir, base);
if (cleanup_tmp(tmpdir, base) != 0) return -1;
return rc;
}
/* asm_byte_identical — diff cstage vs wwstage .s. Parallel trees so
* ww_ww writing intermediates next to the source doesn't clobber the
* cstage .s (CLAUDE.md rule 14 phase split). Mirror of 778's. Unused
/* asm_byte_identical — diff cstage vs wwstage .s. Parallel trees keep
* each driver's retained .sepwork output separate. Unused
* for fold-e5 — every row is STAGE_CS-only per #226/#227 (#209, the
* checker bail, is closed) — but kept scaffolded for when those cgen
* folds land and byte-id graduates. */
@@ -261,18 +267,28 @@ asm_byte_identical(const char *cdrv, const char *wdrv, const char *cwd,
snprintf(tdc, sizeof tdc, "/tmp/lvs_%d_c_%d", getpid(), seq);
snprintf(tdw, sizeof tdw, "/tmp/lvs_%d_w_%d", getpid(), seq);
snprintf(base, sizeof base, "main779");
mkdir(tdc, 0755);
mkdir(tdw, 0755);
if (mkdir(tdc, 0755) != 0) return -1;
if (mkdir(tdw, 0755) != 0) {
if (cleanup_tmp(tdc, base) != 0)
fprintf(stderr, "row[%s]: setup cleanup failed\n", r->label);
return -1;
}
snprintf(src, sizeof src, "%s/%s.ww", tdc, base);
if (write_source(src, r->src) != 0) { cleanup_tmp(tdc, base); cleanup_tmp(tdw, base); return -1; }
if (write_source(src, r->src) != 0) {
int clean_c = cleanup_tmp(tdc, base);
int clean_w = cleanup_tmp(tdw, base);
if (clean_c != 0 || clean_w != 0)
fprintf(stderr, "row[%s]: setup cleanup failed\n", r->label);
return -1;
}
int rc = -1;
if (build_via_driver(cdrv, tdc, cwd, src) != 0) goto out;
snprintf(cs, sizeof cs, "%s/%s.s", tdc, base);
snprintf(cs, sizeof cs, "%s/%s.sepwork/__root.s", tdc, base);
snprintf(src, sizeof src, "%s/%s.ww", tdw, base);
if (write_source(src, r->src) != 0) goto out;
if (build_via_driver(wdrv, tdw, cwd, src) != 0) goto out;
snprintf(ws, sizeof ws, "%s/%s.s", tdw, base);
snprintf(ws, sizeof ws, "%s/%s.sepwork/__root.s", tdw, base);
FILE *fc = fopen(cs, "rb");
FILE *fw = fopen(ws, "rb");
@@ -288,8 +304,8 @@ asm_byte_identical(const char *cdrv, const char *wdrv, const char *cwd,
if (fc) fclose(fc);
if (fw) fclose(fw);
out:
cleanup_tmp(tdc, base);
cleanup_tmp(tdw, base);
if (cleanup_tmp(tdc, base) != 0) rc = -1;
if (cleanup_tmp(tdw, base) != 0) rc = -1;
return rc;
}

View File

@@ -20,22 +20,22 @@
* hint through the bottom-up expression checker.
*
* ROW POLARITY:
* POSITIVE rows build + run on BOTH stages and assert cs.s == ww.s
* (rule-10). `let_call` and `structlit_build` pin the let-binding and
* struct-literal-field-init gate sites. `fieldstore_dispatch` pins the
* eFinal SHIPPING shape: a bare &fn field-STORE into one slot of the
* three-slot io.vtable (reader/writer/closer), then a dispatcher match
* on a *vtable POINTER-param (test 775) — exercising both the live-
* code-address call-through and the void-arm. It is byte-id-clean: the
* POSITIVE execution assertions now live in the declarative compiler
* corpus. Their residual rows here compile BOTH stages and assert
* cs.s == ww.s (rule-10). `let_call` and `structlit_build` pin the
* let-binding and struct-literal-field-init gate sites.
* `fieldstore_dispatch` pins the eFinal SHIPPING shape: a bare &fn
* field-STORE into one slot of the three-slot io.vtable
* (reader/writer/closer), then a dispatcher match on a *vtable
* POINTER-param. It remains byte-id-clean: the
* single-slot local-composite zero-init divergence (project #213,
* reproduces with the cast form and a pure `(i32|void)` field — NOT a
* #206 regression) is a single-field artifact the real three-slot
* io.vtable does not hit.
*
* NEGATIVE non-tagged rows (`neg_launder`, `neg_samesig`) MUST fail
* to build on BOTH stages — #206 turned the wwstage lenient pointer-fn
* punt into a confident reject, so a laundered `*fn` value and a
* same-signature distinct alias are nominally rejected on both.
* The symmetric non-tagged rejection rows (`neg_launder`,
* `neg_samesig`) live in the declarative compiler corpus. Both checker
* paths report the common diagnostic fragment "not assignable".
*
* NEGATIVE tagged-slot rows (`neg_ambiguous_tagged`,
* `neg_launder_tagged`) are pinned CSTAGE-ONLY. cstage REJECTS both.
@@ -47,11 +47,11 @@
* when #214 closes. Mirrors the 777/780/781/782 STAGE_CS carve-out.
*
* GATE POLARITY: must stay GREEN. Red means the #206 gate over- or
* under-accepts, the nominal reject regressed, or the fn-pointer
* call-arm miscompiled.
* under-accepts or the stage output diverged.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -70,39 +70,28 @@ runwait(const char *cmd)
struct row {
const char *label;
const char *src;
int want_exit; /* expected program exit (build+run rows) */
const char *src; /* local source for asymmetric controls */
const char *fixture; /* declarative corpus source for byte rows */
int stage_mask;
int byte_id; /* assert cs.s == ww.s */
int expect_fail; /* 1 = build MUST fail; want_exit ignored */
int expect_fail; /* 1 = build MUST fail */
};
static const struct row rows[] = {
/* POSITIVE byte-id: bare &fn into a *<fn-alias> let, then call. */
{ "let_call",
"package main;\n"
"type reader = fn(x: i32) i32;\n"
"fn rd(x: i32) i32 = { return x + 1; };\n"
"fn main() i32 = {\n"
" let p: *reader = &rd;\n"
" return (*p)(41);\n"
"};\n",
42, STAGE_CS | STAGE_WW, 1, 0 },
NULL,
"test/wcc/data/r78_amp_fn_assign_let_call/case.ww",
STAGE_CS | STAGE_WW, 1, 0 },
/* POSITIVE byte-id: struct-literal field-init of a (*alias|void)
* slot with bare &fn (the Hare `vtable{reader=&fn}` shape). Build
* only — no match (project #212 blocks struct-lit-tagged + match
* on wwstage cgen); the checker gate acceptance is what this pins. */
{ "structlit_build",
"package main;\n"
"type reader = fn(x: i32) i32;\n"
"type vtable = struct { r: (*reader | void) };\n"
"fn rd(x: i32) i32 = { return x + 1; };\n"
"fn main() i32 = {\n"
" let v = vtable { r = &rd };\n"
" return 0;\n"
"};\n",
0, STAGE_CS | STAGE_WW, 1, 0 },
NULL,
"test/wcc/data/r78_amp_fn_assign_structlit/case.ww",
STAGE_CS | STAGE_WW, 1, 0 },
/* POSITIVE byte-id: bare &fn FIELD-STORE into a (*reader|void) slot
* of the THREE-slot vtable (the exact io.vtable reader/writer/closer
@@ -113,63 +102,9 @@ static const struct row rows[] = {
* slot local-composite zero-init divergence (#213) is a single-field
* artifact that the real three-slot io.vtable does not hit. */
{ "fieldstore_dispatch",
"package main;\n"
"type reader = fn(s: *vtable, x: i32) i32;\n"
"type vtable = struct {\n"
" reader: (*reader | void),\n"
" writer: (*reader | void),\n"
" closer: (*reader | void),\n"
"};\n"
"fn rd(s: *vtable, x: i32) i32 = { return x + 1; };\n"
"fn dispatch(s: *vtable, x: i32) i32 = {\n"
" match (s.reader) {\n"
" case void => { return -1; };\n"
" case let f: *reader => { return (*f)(s, x); };\n"
" };\n"
"};\n"
"fn main() i32 = {\n"
" let a: vtable;\n"
" a.reader = &rd;\n"
" let b: vtable;\n"
" b.reader = void;\n"
" return dispatch(&a, 41) + dispatch(&b, 0) + 11;\n"
"};\n",
52, STAGE_CS | STAGE_WW, 1, 0 },
/* NEGATIVE both stages: a materialized *fn value laundered into a
* *reader slot. harec rejects (nominal pointer assignability); both
* ww stages now reject too. */
{ "neg_launder",
"package main;\n"
"type reader = fn(x: i32) i32;\n"
"fn rd(x: i32) i32 = { return x + 1; };\n"
"fn main() i32 = {\n"
" let p = &rd;\n"
" let s: *reader = p;\n"
" return 0;\n"
"};\n",
0, STAGE_CS | STAGE_WW, 0, 1 },
/* NEGATIVE both stages: distinct same-signature aliases. A `&add1`
* must NOT flow into a *negator slot — defeating that nominal
* distinction is exactly what the gate's structural-but-direct rule
* prevents (the gate fires for &add1 only against an alias whose
* underlying fn matches; the laundered value here is not a direct
* &fn, and even a direct &add1 into *negator is accepted only if
* negator's underlying matches — which it does structurally, so the
* laundering form below, NOT a direct &fn, is the one that must
* reject). */
{ "neg_samesig",
"package main;\n"
"type adder = fn(x: i32) i32;\n"
"type negator = fn(x: i32) i32;\n"
"fn add1(x: i32) i32 = { return x + 1; };\n"
"fn main() i32 = {\n"
" let p = &add1;\n"
" let n: *negator = p;\n"
" return 0;\n"
"};\n",
0, STAGE_CS | STAGE_WW, 0, 1 },
NULL,
"test/wcc/data/r78_amp_fn_assign_fieldstore_dispatch/case.ww",
STAGE_CS | STAGE_WW, 1, 0 },
/* NEGATIVE cstage-only (#214 wwstage void-variant over-acceptance):
* two same-signature ptr-to-fn variants in the tagged dst — a direct
@@ -183,7 +118,8 @@ static const struct row rows[] = {
" let x: (*reader | *writer | void) = &myfn;\n"
" return 0;\n"
"};\n",
0, STAGE_CS, 0, 1 },
NULL,
STAGE_CS, 0, 1 },
/* NEGATIVE cstage-only (#214): laundering a materialized *fn into a
* (*reader|void) tagged slot. cstage rejects nominally; wwstage's
@@ -197,106 +133,121 @@ static const struct row rows[] = {
" let s: (*reader | void) = p;\n"
" return 0;\n"
"};\n",
0, STAGE_CS, 0, 1 },
NULL,
STAGE_CS, 0, 1 },
};
static void
static int
cleanup_tmp(const char *tmpdir, const char *base)
{
char p[1024];
snprintf(p, sizeof p, "%s/%s", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "%s/%s.o", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base); unlink(p);
/* #93: the sep scratch dir + the tmpdir-pinned pkgcache. */
snprintf(p, sizeof p, "rm -rf %s/%s.sepwork %s/pkgc", tmpdir, base, tmpdir);
if (system(p)) {} /* best-effort */
rmdir(tmpdir);
int rc = 0;
snprintf(p, sizeof p, "%s/%s", tmpdir, base);
if (unlink(p) != 0 && errno != ENOENT) rc = -1;
snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base);
if (unlink(p) != 0 && errno != ENOENT) rc = -1;
/* #93: the sep scratch dir + the tmpdir-owned outputs. */
snprintf(p, sizeof p, "rm -rf %s/%s.sepwork", tmpdir, base);
if (runwait(p) != 0) rc = -1;
if (rmdir(tmpdir) != 0) rc = -1;
return rc;
}
static int
write_source(const char *path, const char *src)
write_source(const char *path, const struct row *r)
{
FILE *in = NULL;
if (r->fixture != NULL) {
in = fopen(r->fixture, "rb");
if (in == NULL) return -1;
}
FILE *f = fopen(path, "wb");
if (!f) return -1;
fputs(src, f);
fclose(f);
return 0;
if (!f) {
if (in != NULL) fclose(in);
return -1;
}
if (in != NULL) {
int ioerr = 0;
for (;;) {
int ch = fgetc(in);
if (ch == EOF) break;
if (fputc(ch, f) == EOF) { ioerr = 1; break; }
}
if (ferror(in)) ioerr = 1;
if (fclose(in) != 0) ioerr = 1;
if (fclose(f) != 0) ioerr = 1;
return ioerr ? -1 : 0;
} else {
if (fputs(r->src, f) == EOF) {
fclose(f);
return -1;
}
}
return fclose(f) == 0 ? 0 : -1;
}
static int
build_via_driver(const char *driver, const char *tmpdir, const char *src)
{
char cmd[2048];
/* #93 sep layout: `--sep -o <src-stem>` relocates artifacts to
* <stem>.sepwork/; WW_PKGCACHE is pinned under tmpdir so the shared
* out/.pkgcache is untouched. expect_fail rows still fail: a checker
/* #93 sep layout: `-o <src-stem>` relocates artifacts to
* <stem>.sepwork/ under tmpdir. expect_fail rows still fail: a checker
* error makes w6c return nonzero regardless of artifact placement. */
snprintf(cmd, sizeof cmd,
"cd %s && b='%s'; WW_PKGCACHE=%s/pkgc timeout 180 %s build --sep "
"cd %s && b='%s'; timeout 180 %s build "
"-o \"${b%%.ww}\" \"$b\" 2>/dev/null",
tmpdir, src, tmpdir, driver);
tmpdir, src, driver);
return runwait(cmd);
}
/* run_row — build via driver, run the binary, return exit (or -1 on
* build failure). */
static int
run_row(const char *driver, const struct row *r, int seq)
{
char tmpdir[256], src[512], base[64], outbin[768];
snprintf(tmpdir, sizeof tmpdir, "/tmp/afa_%d_d_%d", getpid(), seq);
snprintf(base, sizeof base, "main783");
snprintf(src, sizeof src, "%s/%s.ww", tmpdir, base);
mkdir(tmpdir, 0755);
if (write_source(src, r->src) != 0) { cleanup_tmp(tmpdir, base); return -1; }
int rc;
if (build_via_driver(driver, tmpdir, src) == 0) {
snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base);
rc = runwait(outbin);
} else {
rc = -1;
}
cleanup_tmp(tmpdir, base);
return rc;
}
/* build_ok — 1 iff the build succeeds (used by expect_fail rows). */
static int
build_ok(const char *driver, const struct row *r, int seq)
{
char tmpdir[256], src[512], base[64];
snprintf(tmpdir, sizeof tmpdir, "/tmp/afa_%d_nf_%d", getpid(), seq);
(void)seq;
char tmpdir[] = "/tmp/afa_nf_XXXXXX";
char src[512], base[64];
if (mkdtemp(tmpdir) == NULL) return -1;
snprintf(base, sizeof base, "main783");
snprintf(src, sizeof src, "%s/%s.ww", tmpdir, base);
mkdir(tmpdir, 0755);
if (write_source(src, r->src) != 0) { cleanup_tmp(tmpdir, base); return -1; }
if (write_source(src, r) != 0) {
if (cleanup_tmp(tmpdir, base) != 0)
fprintf(stderr, "amp_fn_assign[%s]: setup cleanup failed\n", r->label);
return -1;
}
int br = build_via_driver(driver, tmpdir, src);
cleanup_tmp(tmpdir, base);
if (cleanup_tmp(tmpdir, base) != 0) {
fprintf(stderr, "amp_fn_assign[%s]: cleanup failed\n", r->label);
return -1;
}
return br == 0;
}
/* asm_byte_identical — diff cstage vs wwstage .s. Parallel trees so
* ww_ww writing intermediates next to the source doesn't clobber the
* cstage .s (CLAUDE.md rule 14 phase split). */
/* asm_byte_identical — diff cstage vs wwstage .s. Parallel owned trees
* keep each driver's retained <stem>.sepwork/__root.s distinct. */
static int
asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r,
int seq)
{
char src[512], tdc[256], tdw[256], base[64], cs[512], ws[512];
snprintf(tdc, sizeof tdc, "/tmp/afa_%d_c_%d", getpid(), seq);
snprintf(tdw, sizeof tdw, "/tmp/afa_%d_w_%d", getpid(), seq);
(void)seq;
char src[512], base[64], cs[512], ws[512];
char tdc[] = "/tmp/afa_c_XXXXXX";
char tdw[] = "/tmp/afa_w_XXXXXX";
snprintf(base, sizeof base, "main783");
mkdir(tdc, 0755);
mkdir(tdw, 0755);
if (mkdtemp(tdc) == NULL) return -1;
if (mkdtemp(tdw) == NULL) {
if (cleanup_tmp(tdc, base) != 0)
fprintf(stderr, "amp_fn_assign[%s]: setup cleanup failed\n", r->label);
return -1;
}
snprintf(src, sizeof src, "%s/%s.ww", tdc, base);
if (write_source(src, r->src) != 0) { cleanup_tmp(tdc, base); cleanup_tmp(tdw, base); return -1; }
int rc = -1;
if (write_source(src, r) != 0) goto out;
if (build_via_driver(cdrv, tdc, src) != 0) goto out;
snprintf(cs, sizeof cs, "%s/%s.sepwork/__root.s", tdc, base);
snprintf(src, sizeof src, "%s/%s.ww", tdw, base);
if (write_source(src, r->src) != 0) goto out;
if (write_source(src, r) != 0) goto out;
if (build_via_driver(wdrv, tdw, src) != 0) goto out;
snprintf(ws, sizeof ws, "%s/%s.sepwork/__root.s", tdw, base);
@@ -314,8 +265,14 @@ asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r,
if (fc) fclose(fc);
if (fw) fclose(fw);
out:
cleanup_tmp(tdc, base);
cleanup_tmp(tdw, base);
if (cleanup_tmp(tdc, base) != 0) {
fprintf(stderr, "amp_fn_assign[%s]: c cleanup failed\n", r->label);
rc = -1;
}
if (cleanup_tmp(tdw, base) != 0) {
fprintf(stderr, "amp_fn_assign[%s]: ww cleanup failed\n", r->label);
rc = -1;
}
return rc;
}
@@ -343,47 +300,40 @@ main(void)
for (int i = 0; i < n; i++) {
const struct row *r = &rows[i];
if (r->stage_mask & STAGE_CS) {
if ((r->stage_mask & STAGE_CS) && r->expect_fail) {
total++;
if (r->expect_fail) {
if (build_ok(cdrv, r, seq++)) {
int got = build_ok(cdrv, r, seq++);
if (got != 0) {
if (got > 0)
fprintf(stderr, "amp_fn_assign[cs][%s]: built but expected reject\n",
r->label);
fail++;
}
} else {
int got = run_row(cdrv, r, seq++);
if (got != r->want_exit) {
fprintf(stderr, "amp_fn_assign[cs][%s]: exit=%d want=%d\n",
r->label, got, r->want_exit);
fail++;
}
else
fprintf(stderr, "amp_fn_assign[cs][%s]: setup or cleanup failed\n",
r->label);
fail++;
}
}
if (wwpresent && (r->stage_mask & STAGE_WW)) {
total++;
if (r->expect_fail) {
if (build_ok(wdrv, r, seq++)) {
fprintf(stderr, "amp_fn_assign[ww][%s]: built but expected reject\n",
total++;
int got = build_ok(wdrv, r, seq++);
if (got != 0) {
if (got > 0)
fprintf(stderr, "amp_fn_assign[ww][%s]: built but expected reject\n",
r->label);
else
fprintf(stderr, "amp_fn_assign[ww][%s]: setup or cleanup failed\n",
r->label);
fail++;
}
} else if (r->byte_id) {
total++;
if (asm_byte_identical(cdrv, wdrv, r, seq++) != 0) {
fprintf(stderr, "amp_fn_assign[byte-id][%s]: cstage vs wwstage asm differs\n",
r->label);
fail++;
}
} else {
int got = run_row(wdrv, r, seq++);
if (got != r->want_exit) {
fprintf(stderr, "amp_fn_assign[ww][%s]: exit=%d want=%d\n",
r->label, got, r->want_exit);
fail++;
}
if (r->byte_id) {
total++;
if (asm_byte_identical(cdrv, wdrv, r, seq++) != 0) {
fprintf(stderr, "amp_fn_assign[byte-id][%s]: cstage vs wwstage asm differs\n",
r->label);
fail++;
}
}
}
}
}

View File

@@ -54,6 +54,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -80,7 +81,9 @@ slurp_eq(const char *a, const char *b)
if (ca != cb) { rc = -1; break; }
if (ca == EOF) break;
}
fclose(fa); fclose(fb);
if (ferror(fa) || ferror(fb)) rc = -1;
if (fclose(fa) != 0) rc = -1;
if (fclose(fb) != 0) rc = -1;
return rc;
}
@@ -260,15 +263,17 @@ run_scenario(const char *bin, const char *cdrv, const char *wdrv,
FILE *f = fopen(path, "wb");
if (!f) { fprintf(stderr, "784[%s]: write %s\n", sc->label,
sc->files[i].name); rc = -1; goto done; }
fputs(sc->files[i].src, f);
fclose(f);
int bad = fputs(sc->files[i].src, f) == EOF;
if (fclose(f) != 0) bad = 1;
if (bad) { fprintf(stderr, "784[%s]: write %s\n", sc->label,
sc->files[i].name); rc = -1; goto done; }
}
/* #94 sep layout: cstage --sep build + run pins runtime; pin
* WW_PKGCACHE under the scratch dir so out/.pkgcache is untouched. */
/* #94 sep layout: cstage build + run pins runtime; pin
* all outputs stay under the scratch dir. */
snprintf(cmd, sizeof cmd,
"cd %s && WW_PKGCACHE=%s/pkgc_c %s build --sep -I %s -o %s/main %s/main.ww",
dir, dir, cdrv, dir, dir, dir);
"cd %s && %s build -I %s -o %s/main %s/main.ww",
dir, cdrv, dir, dir, dir);
if (runwait(cmd) != 0) {
fprintf(stderr, "784[%s]: cstage build failed\n", sc->label);
rc = -1; goto done;
@@ -281,24 +286,36 @@ run_scenario(const char *bin, const char *cdrv, const char *wdrv,
rc = -1;
}
/* Byte-id net (the #223 discriminator): the wwstage driver's --sep
/* Byte-id net (the #223 discriminator): the wwstage driver's
* build emits per-package w6c_ww asm; pre-fix the dispatcher diverges.
* The root + each imported pkg compile to separate <stem>.sepwork/<pkg>.s;
* concat (sorted glob, identical set both stages) for the compare. */
char cs_s[1024], ws_s[1024];
snprintf(cmd, sizeof cmd,
"cd %s && WW_PKGCACHE=%s/pkgc_w %s build --sep -I %s -o %s/mainww %s/main.ww",
dir, dir, wdrv, dir, dir, dir);
"cd %s && %s build -I %s -o %s/mainww %s/main.ww",
dir, wdrv, dir, dir, dir);
if (runwait(cmd) != 0) {
fprintf(stderr, "784[%s]: ww_ww build failed\n", sc->label);
rc = -1; goto done;
}
snprintf(cs_s, sizeof cs_s, "%s/all_cs.s", dir);
snprintf(ws_s, sizeof ws_s, "%s/all_ww.s", dir);
snprintf(cmd, sizeof cmd, "cat %s/main.sepwork/*.s > %s 2>/dev/null",
dir, cs_s); if (system(cmd)) {}
snprintf(cmd, sizeof cmd, "cat %s/mainww.sepwork/*.s > %s 2>/dev/null",
dir, ws_s); if (system(cmd)) {}
snprintf(cmd, sizeof cmd,
"cat %s/main.sepwork/*.s > %s 2>/dev/null && test -s %s",
dir, cs_s, cs_s);
if (runwait(cmd) != 0) {
fprintf(stderr, "784[%s]: cstage asm aggregation failed\n",
sc->label);
rc = -1; goto done;
}
snprintf(cmd, sizeof cmd,
"cat %s/mainww.sepwork/*.s > %s 2>/dev/null && test -s %s",
dir, ws_s, ws_s);
if (runwait(cmd) != 0) {
fprintf(stderr, "784[%s]: wwstage asm aggregation failed\n",
sc->label);
rc = -1; goto done;
}
if (slurp_eq(cs_s, ws_s) != 0) {
fprintf(stderr, "784[%s]: cs.s/ww.s DIFFER (rule-10 byte-id "
"violation — #223 regression)\n", sc->label);
@@ -306,9 +323,33 @@ run_scenario(const char *bin, const char *cdrv, const char *wdrv,
}
done:
/* Best-effort cleanup of the private dir. */
snprintf(cmd, sizeof cmd, "rm -rf %s", dir);
(void)runwait(cmd);
snprintf(cmd, sizeof cmd, "rm -rf %s/main.sepwork", dir);
if (runwait(cmd) != 0) { fprintf(stderr, "784[%s]: cleanup main.sepwork\n",
sc->label); rc = -1; }
snprintf(cmd, sizeof cmd, "rm -rf %s/mainww.sepwork", dir);
if (runwait(cmd) != 0) { fprintf(stderr, "784[%s]: cleanup mainww.sepwork\n",
sc->label); rc = -1; }
for (int i = 0; sc->files[i].name; i++) {
snprintf(path, sizeof path, "%s/%s", dir, sc->files[i].name);
if (unlink(path) != 0 && errno != ENOENT) {
fprintf(stderr, "784[%s]: cleanup %s\n", sc->label,
sc->files[i].name);
rc = -1;
}
}
const char *children[] = { "main", "mainww", "all_cs.s", "all_ww.s", NULL };
for (int i = 0; children[i]; i++) {
snprintf(path, sizeof path, "%s/%s", dir, children[i]);
if (unlink(path) != 0 && errno != ENOENT) {
fprintf(stderr, "784[%s]: cleanup %s\n", sc->label,
children[i]);
rc = -1;
}
}
if (rmdir(dir) != 0) {
fprintf(stderr, "784[%s]: cleanup directory\n", sc->label);
rc = -1;
}
return rc;
}

View File

@@ -47,6 +47,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -111,17 +112,28 @@ static const struct row rows[] = {
22, STAGE_CS | STAGE_WW, 1 },
};
static void
/* nonzero on any cleanup failure; ENOENT tolerated because legs that
* fail early never create the later artifacts. */
static int
cleanup_tmp(const char *tmpdir, const char *base)
{
char p[1024];
snprintf(p, sizeof p, "%s/%s", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "rm -rf %s/%s.sepwork %s/pkgc", tmpdir, base, tmpdir);
if (system(p)) {} /* best-effort */
snprintf(p, sizeof p, "%s/%s.o", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base); unlink(p);
rmdir(tmpdir);
int bad = 0;
snprintf(p, sizeof p, "%s/%s", tmpdir, base);
if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; }
snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base);
if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; }
snprintf(p, sizeof p, "rm -rf %s/%s.sepwork", tmpdir, base);
if (runwait(p) != 0) {
fprintf(stderr, "cleanup failed: %s\n", p);
bad = 1;
}
snprintf(p, sizeof p, "%s/%s.o", tmpdir, base);
if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; }
snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base);
if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; }
if (rmdir(tmpdir) != 0) { perror(tmpdir); bad = 1; }
return bad;
}
static int
@@ -129,8 +141,11 @@ write_source(const char *path, const char *src)
{
FILE *f = fopen(path, "wb");
if (!f) return -1;
fputs(src, f);
fclose(f);
if (fputs(src, f) == EOF) {
fclose(f);
return -1;
}
if (fclose(f) != 0) return -1;
return 0;
}
@@ -139,36 +154,14 @@ build_via_driver(const char *driver, const char *tmpdir, const char *src)
{
char cmd[2048];
/* #93 sep layout: emit asm to <src-stem>.sepwork/__root.s; pin
* WW_PKGCACHE under tmpdir so out/.pkgcache is untouched. */
* all outputs stay under tmpdir. */
snprintf(cmd, sizeof cmd,
"cd %s && b='%s'; WW_PKGCACHE=%s/pkgc timeout 180 %s build --sep "
"cd %s && b='%s'; timeout 180 %s build -S "
"-o \"${b%%.ww}\" \"$b\" 2>/dev/null",
tmpdir, src, tmpdir, driver);
tmpdir, src, driver);
return runwait(cmd);
}
/* run_row — build via driver, run the binary, return exit (or -1 on
* build failure). */
static int
run_row(const char *driver, const struct row *r, int seq)
{
char tmpdir[256], src[512], base[64], outbin[768];
snprintf(tmpdir, sizeof tmpdir, "/tmp/svlu_%d_d_%d", getpid(), seq);
snprintf(base, sizeof base, "main785");
snprintf(src, sizeof src, "%s/%s.ww", tmpdir, base);
mkdir(tmpdir, 0755);
if (write_source(src, r->src) != 0) { cleanup_tmp(tmpdir, base); return -1; }
int rc;
if (build_via_driver(driver, tmpdir, src) == 0) {
snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base);
rc = runwait(outbin);
} else {
rc = -1;
}
cleanup_tmp(tmpdir, base);
return rc;
}
/* asm_byte_identical — diff cstage vs wwstage .s. Parallel trees so
* ww_ww writing intermediates next to the source doesn't clobber the
* cstage .s (CLAUDE.md rule 14 phase split). */
@@ -180,11 +173,18 @@ asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r,
snprintf(tdc, sizeof tdc, "/tmp/svlu_%d_c_%d", getpid(), seq);
snprintf(tdw, sizeof tdw, "/tmp/svlu_%d_w_%d", getpid(), seq);
snprintf(base, sizeof base, "main785");
mkdir(tdc, 0755);
mkdir(tdw, 0755);
int rc = -1, tdw_owned = 0, cleanfail = 0;
if (mkdir(tdc, 0755) != 0) {
perror(tdc);
return -1;
}
if (mkdir(tdw, 0755) != 0) {
perror(tdw);
goto out;
}
tdw_owned = 1;
snprintf(src, sizeof src, "%s/%s.ww", tdc, base);
if (write_source(src, r->src) != 0) { cleanup_tmp(tdc, base); cleanup_tmp(tdw, base); return -1; }
int rc = -1;
if (write_source(src, r->src) != 0) goto out;
if (build_via_driver(cdrv, tdc, src) != 0) goto out;
snprintf(cs, sizeof cs, "%s/%s.sepwork/__root.s", tdc, base);
@@ -207,8 +207,13 @@ asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r,
if (fc) fclose(fc);
if (fw) fclose(fw);
out:
cleanup_tmp(tdc, base);
cleanup_tmp(tdw, base);
cleanfail = cleanup_tmp(tdc, base);
if (tdw_owned && cleanup_tmp(tdw, base) != 0)
cleanfail = 1;
/* a leaked tree fails an otherwise-green row; a real diff result
* (rc == -1) is never overwritten (110's cleanfail shape). */
if (cleanfail && rc == 0)
rc = -1;
return rc;
}
@@ -236,33 +241,14 @@ main(void)
for (int i = 0; i < n; i++) {
const struct row *r = &rows[i];
if (r->stage_mask & STAGE_CS) {
if (wwpresent && r->byte_id) {
total++;
int got = run_row(cdrv, r, seq++);
if (got != r->want_exit) {
fprintf(stderr, "structvariant[cs][%s]: exit=%d want=%d\n",
r->label, got, r->want_exit);
if (asm_byte_identical(cdrv, wdrv, r, seq++) != 0) {
fprintf(stderr, "structvariant[byte-id][%s]: cstage vs wwstage asm differs\n",
r->label);
fail++;
}
}
if (wwpresent && (r->stage_mask & STAGE_WW)) {
total++;
int got = run_row(wdrv, r, seq++);
if (got != r->want_exit) {
fprintf(stderr, "structvariant[ww][%s]: exit=%d want=%d\n",
r->label, got, r->want_exit);
fail++;
}
if (r->byte_id) {
total++;
if (asm_byte_identical(cdrv, wdrv, r, seq++) != 0) {
fprintf(stderr, "structvariant[byte-id][%s]: cstage vs wwstage asm differs\n",
r->label);
fail++;
}
}
}
}
if (fail) {

View File

@@ -44,6 +44,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -111,17 +112,28 @@ static const struct row rows[] = {
42, STAGE_CS | STAGE_WW, 1 },
};
static void
/* nonzero on any cleanup failure; ENOENT tolerated because legs that
* fail early never create the later artifacts. */
static int
cleanup_tmp(const char *tmpdir, const char *base)
{
char p[1024];
snprintf(p, sizeof p, "%s/%s", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "rm -rf %s/%s.sepwork %s/pkgc", tmpdir, base, tmpdir);
if (system(p)) {} /* best-effort */
snprintf(p, sizeof p, "%s/%s.o", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base); unlink(p);
rmdir(tmpdir);
int bad = 0;
snprintf(p, sizeof p, "%s/%s", tmpdir, base);
if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; }
snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base);
if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; }
snprintf(p, sizeof p, "rm -rf %s/%s.sepwork", tmpdir, base);
if (runwait(p) != 0) {
fprintf(stderr, "cleanup failed: %s\n", p);
bad = 1;
}
snprintf(p, sizeof p, "%s/%s.o", tmpdir, base);
if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; }
snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base);
if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; }
if (rmdir(tmpdir) != 0) { perror(tmpdir); bad = 1; }
return bad;
}
static int
@@ -129,8 +141,11 @@ write_source(const char *path, const char *src)
{
FILE *f = fopen(path, "wb");
if (!f) return -1;
fputs(src, f);
fclose(f);
if (fputs(src, f) == EOF) {
fclose(f);
return -1;
}
if (fclose(f) != 0) return -1;
return 0;
}
@@ -139,36 +154,14 @@ build_via_driver(const char *driver, const char *tmpdir, const char *src)
{
char cmd[2048];
/* #93 sep layout: emit asm to <src-stem>.sepwork/__root.s; pin
* WW_PKGCACHE under tmpdir so out/.pkgcache is untouched. */
* all outputs stay under tmpdir. */
snprintf(cmd, sizeof cmd,
"cd %s && b='%s'; WW_PKGCACHE=%s/pkgc timeout 180 %s build --sep "
"cd %s && b='%s'; timeout 180 %s build -S "
"-o \"${b%%.ww}\" \"$b\" 2>/dev/null",
tmpdir, src, tmpdir, driver);
tmpdir, src, driver);
return runwait(cmd);
}
/* run_row — build via driver, run the binary, return exit (or -1 on
* build failure). */
static int
run_row(const char *driver, const struct row *r, int seq)
{
char tmpdir[256], src[512], base[64], outbin[768];
snprintf(tmpdir, sizeof tmpdir, "/tmp/nas_%d_d_%d", getpid(), seq);
snprintf(base, sizeof base, "main786");
snprintf(src, sizeof src, "%s/%s.ww", tmpdir, base);
mkdir(tmpdir, 0755);
if (write_source(src, r->src) != 0) { cleanup_tmp(tmpdir, base); return -1; }
int rc;
if (build_via_driver(driver, tmpdir, src) == 0) {
snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base);
rc = runwait(outbin);
} else {
rc = -1;
}
cleanup_tmp(tmpdir, base);
return rc;
}
/* asm_byte_identical — diff cstage vs wwstage .s. Parallel trees so
* ww_ww writing intermediates next to the source doesn't clobber the
* cstage .s (CLAUDE.md rule 14 phase split). */
@@ -180,11 +173,18 @@ asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r,
snprintf(tdc, sizeof tdc, "/tmp/nas_%d_c_%d", getpid(), seq);
snprintf(tdw, sizeof tdw, "/tmp/nas_%d_w_%d", getpid(), seq);
snprintf(base, sizeof base, "main786");
mkdir(tdc, 0755);
mkdir(tdw, 0755);
int rc = -1, tdw_owned = 0, cleanfail = 0;
if (mkdir(tdc, 0755) != 0) {
perror(tdc);
return -1;
}
if (mkdir(tdw, 0755) != 0) {
perror(tdw);
goto out;
}
tdw_owned = 1;
snprintf(src, sizeof src, "%s/%s.ww", tdc, base);
if (write_source(src, r->src) != 0) { cleanup_tmp(tdc, base); cleanup_tmp(tdw, base); return -1; }
int rc = -1;
if (write_source(src, r->src) != 0) goto out;
if (build_via_driver(cdrv, tdc, src) != 0) goto out;
snprintf(cs, sizeof cs, "%s/%s.sepwork/__root.s", tdc, base);
@@ -207,8 +207,13 @@ asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r,
if (fc) fclose(fc);
if (fw) fclose(fw);
out:
cleanup_tmp(tdc, base);
cleanup_tmp(tdw, base);
cleanfail = cleanup_tmp(tdc, base);
if (tdw_owned && cleanup_tmp(tdw, base) != 0)
cleanfail = 1;
/* a leaked tree fails an otherwise-green row; a real diff result
* (rc == -1) is never overwritten (110's cleanfail shape). */
if (cleanfail && rc == 0)
rc = -1;
return rc;
}
@@ -236,33 +241,14 @@ main(void)
for (int i = 0; i < n; i++) {
const struct row *r = &rows[i];
if (r->stage_mask & STAGE_CS) {
if (wwpresent && r->byte_id) {
total++;
int got = run_row(cdrv, r, seq++);
if (got != r->want_exit) {
fprintf(stderr, "narrow_alias[cs][%s]: exit=%d want=%d\n",
r->label, got, r->want_exit);
if (asm_byte_identical(cdrv, wdrv, r, seq++) != 0) {
fprintf(stderr, "narrow_alias[byte-id][%s]: cstage vs wwstage asm differs\n",
r->label);
fail++;
}
}
if (wwpresent && (r->stage_mask & STAGE_WW)) {
total++;
int got = run_row(wdrv, r, seq++);
if (got != r->want_exit) {
fprintf(stderr, "narrow_alias[ww][%s]: exit=%d want=%d\n",
r->label, got, r->want_exit);
fail++;
}
if (r->byte_id) {
total++;
if (asm_byte_identical(cdrv, wdrv, r, seq++) != 0) {
fprintf(stderr, "narrow_alias[byte-id][%s]: cstage vs wwstage asm differs\n",
r->label);
fail++;
}
}
}
}
if (fail) {

View File

@@ -56,6 +56,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -82,7 +83,9 @@ slurp_eq(const char *a, const char *b)
if (ca != cb) { rc = -1; break; }
if (ca == EOF) break;
}
fclose(fa); fclose(fb);
if (ferror(fa) || ferror(fb)) rc = -1;
if (fclose(fa) != 0) rc = -1;
if (fclose(fb) != 0) rc = -1;
return rc;
}
@@ -241,28 +244,30 @@ run_scenario(const char *bin, const char *cdrv, const char *wdrv,
FILE *f = fopen(path, "wb");
if (!f) { fprintf(stderr, "787[%s]: write %s\n", sc->label,
sc->files[i].name); rc = -1; goto done; }
fputs(sc->files[i].src, f);
fclose(f);
int bad = fputs(sc->files[i].src, f) == EOF;
if (fclose(f) != 0) bad = 1;
if (bad) { fprintf(stderr, "787[%s]: write %s\n", sc->label,
sc->files[i].name); rc = -1; goto done; }
}
if (sc->expect_reject) {
/* #94 sep layout: there is no combined unit to re-check. The
* checker reject now fires inside each driver's --sep w6c pass,
* checker reject now fires inside each driver's w6c pass,
* so BOTH driver builds MUST fail. w6c_ww (via ww_ww) accepting
* here is the #13 false-ACCEPT regression. */
snprintf(cmd, sizeof cmd,
"cd %s && WW_PKGCACHE=%s/pkgc_c %s build --sep -I %s "
"cd %s && %s build -I %s "
"-o %s/main %s/main.ww >/dev/null 2>&1",
dir, dir, cdrv, dir, dir, dir);
dir, cdrv, dir, dir, dir);
if (runwait(cmd) == 0) {
fprintf(stderr, "787[%s]: cstage build SUCCEEDED, "
"expected reject\n", sc->label);
rc = -1; goto done;
}
snprintf(cmd, sizeof cmd,
"cd %s && WW_PKGCACHE=%s/pkgc_w %s build --sep -I %s "
"cd %s && %s build -I %s "
"-o %s/mainww %s/main.ww >/dev/null 2>&1",
dir, dir, wdrv, dir, dir, dir);
dir, wdrv, dir, dir, dir);
if (runwait(cmd) == 0) {
fprintf(stderr, "787[%s]: ww_ww ACCEPTED foreign variant "
"(#13 false-accept), expected reject\n", sc->label);
@@ -271,11 +276,11 @@ run_scenario(const char *bin, const char *cdrv, const char *wdrv,
goto done;
}
/* cstage driver --sep build + run: pins runtime routing. Pin
* WW_PKGCACHE under the scratch dir so out/.pkgcache is untouched. */
/* cstage driver build + run: pins runtime routing. Pin
* all outputs stay under the scratch dir. */
snprintf(cmd, sizeof cmd,
"cd %s && WW_PKGCACHE=%s/pkgc_c %s build --sep -I %s -o %s/main %s/main.ww",
dir, dir, cdrv, dir, dir, dir);
"cd %s && %s build -I %s -o %s/main %s/main.ww",
dir, cdrv, dir, dir, dir);
if (runwait(cmd) != 0) {
fprintf(stderr, "787[%s]: cstage build failed\n", sc->label);
rc = -1; goto done;
@@ -288,15 +293,15 @@ run_scenario(const char *bin, const char *cdrv, const char *wdrv,
rc = -1;
}
/* The #13 discriminator: the wwstage driver's --sep build runs the
/* The #13 discriminator: the wwstage driver's build runs the
* wwstage checker over the same units. Pre-fix it REJECTED the
* cross-module variant arms (build fails); post-fix it accepts AND
* its per-package w6c_ww asm is byte-id with cstage's. The root +
* each imported pkg compile to separate <stem>.sepwork/<pkg>.s;
* concat (sorted glob, identical set both stages) for the compare. */
snprintf(cmd, sizeof cmd,
"cd %s && WW_PKGCACHE=%s/pkgc_w %s build --sep -I %s -o %s/mainww %s/main.ww",
dir, dir, wdrv, dir, dir, dir);
"cd %s && %s build -I %s -o %s/mainww %s/main.ww",
dir, wdrv, dir, dir, dir);
if (runwait(cmd) != 0) {
fprintf(stderr, "787[%s]: ww_ww build failed "
"(#13 cross-module variant reject?)\n", sc->label);
@@ -305,10 +310,22 @@ run_scenario(const char *bin, const char *cdrv, const char *wdrv,
char cs_s[1024], ws_s[1024];
snprintf(cs_s, sizeof cs_s, "%s/all_cs.s", dir);
snprintf(ws_s, sizeof ws_s, "%s/all_ww.s", dir);
snprintf(cmd, sizeof cmd, "cat %s/main.sepwork/*.s > %s 2>/dev/null",
dir, cs_s); if (system(cmd)) {}
snprintf(cmd, sizeof cmd, "cat %s/mainww.sepwork/*.s > %s 2>/dev/null",
dir, ws_s); if (system(cmd)) {}
snprintf(cmd, sizeof cmd,
"cat %s/main.sepwork/*.s > %s 2>/dev/null && test -s %s",
dir, cs_s, cs_s);
if (runwait(cmd) != 0) {
fprintf(stderr, "787[%s]: cstage asm aggregation failed\n",
sc->label);
rc = -1; goto done;
}
snprintf(cmd, sizeof cmd,
"cat %s/mainww.sepwork/*.s > %s 2>/dev/null && test -s %s",
dir, ws_s, ws_s);
if (runwait(cmd) != 0) {
fprintf(stderr, "787[%s]: wwstage asm aggregation failed\n",
sc->label);
rc = -1; goto done;
}
if (slurp_eq(cs_s, ws_s) != 0) {
fprintf(stderr, "787[%s]: cs.s/ww.s DIFFER (rule-10 byte-id "
"violation)\n", sc->label);
@@ -316,8 +333,33 @@ run_scenario(const char *bin, const char *cdrv, const char *wdrv,
}
done:
snprintf(cmd, sizeof cmd, "rm -rf %s", dir);
(void)runwait(cmd);
snprintf(cmd, sizeof cmd, "rm -rf %s/main.sepwork", dir);
if (runwait(cmd) != 0) { fprintf(stderr, "787[%s]: cleanup main.sepwork\n",
sc->label); rc = -1; }
snprintf(cmd, sizeof cmd, "rm -rf %s/mainww.sepwork", dir);
if (runwait(cmd) != 0) { fprintf(stderr, "787[%s]: cleanup mainww.sepwork\n",
sc->label); rc = -1; }
for (int i = 0; sc->files[i].name; i++) {
snprintf(path, sizeof path, "%s/%s", dir, sc->files[i].name);
if (unlink(path) != 0 && errno != ENOENT) {
fprintf(stderr, "787[%s]: cleanup %s\n", sc->label,
sc->files[i].name);
rc = -1;
}
}
const char *children[] = { "main", "mainww", "all_cs.s", "all_ww.s", NULL };
for (int i = 0; children[i]; i++) {
snprintf(path, sizeof path, "%s/%s", dir, children[i]);
if (unlink(path) != 0 && errno != ENOENT) {
fprintf(stderr, "787[%s]: cleanup %s\n", sc->label,
children[i]);
rc = -1;
}
}
if (rmdir(dir) != 0) {
fprintf(stderr, "787[%s]: cleanup directory\n", sc->label);
rc = -1;
}
return rc;
}

View File

@@ -31,17 +31,18 @@
* | (= *vtable) variant of (file|stream), | |
* | summed 10x via the stream match arm | |
* | (pre-fix took the file arm → 86) | |
* degenerate_ambig | (sa | sb), both = *vtable; a bare | both | (reject
* | *vtable widen matches BOTH NAMED |reject| net)
* | variants → guard must LOUD hard-error | |
* | on BOTH stages (drew proviso lock) | |
*
* GATE POLARITY: must stay GREEN. Red on handle_widen means the
* NAMED-ptr-alias-variant widen regressed (wrong tag); red on
* degenerate_ambig means the >=2 ambiguity guard stopped firing.
* NAMED-ptr-alias-variant widen regressed (wrong tag).
*
* The symmetric degenerate_ambig rejection now lives in
* test/wcc/data/r78_named_ptr_alias_degenerate_ambig/case.ww, where the
* shared C/WW diagnostic pins the >=2 ambiguity guard directly. This
* wrapper retains the positive runtime and assembly-byte-identity evidence.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -64,7 +65,6 @@ struct row {
int want_exit;
int stage_mask;
int byte_id;
int expect_reject; /* 1 = BOTH stages must hard-error */
};
static const struct row rows[] = {
@@ -90,33 +90,31 @@ static const struct row rows[] = {
" if (sum == 40u64) { return 42; };\n"
" return (sum: i32);\n"
"};\n",
42, STAGE_CS | STAGE_WW, 1, 0 },
/* GUARD lock (drew proviso): a bare *vtable widened into a union of
* TWO NAMED *vtable aliases is ambiguous → BOTH stages hard-error. */
{ "degenerate_ambig",
"package main;\n"
"type vtable = struct { x: i32 };\n"
"type sa = *vtable;\n"
"type sb = *vtable;\n"
"type h = (sa | sb);\n"
"fn w(x: h) i32 = { match (x) { case sa => return 1; case sb => return 2; }; };\n"
"fn emit(v: *vtable) i32 = { return w(v); };\n"
"export fn main() i32 = { let vt: vtable; vt.x = 0; return emit(&vt); };\n",
0, STAGE_CS | STAGE_WW, 0, 1 },
42, STAGE_CS | STAGE_WW, 1 },
};
static void
/* nonzero on any cleanup failure; ENOENT tolerated because legs that
* fail early never create the later artifacts. */
static int
cleanup_tmp(const char *tmpdir, const char *base)
{
char p[1024];
snprintf(p, sizeof p, "%s/%s", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "rm -rf %s/%s.sepwork %s/pkgc", tmpdir, base, tmpdir);
if (system(p)) {} /* best-effort */
snprintf(p, sizeof p, "%s/%s.o", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base); unlink(p);
rmdir(tmpdir);
int bad = 0;
snprintf(p, sizeof p, "%s/%s", tmpdir, base);
if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; }
snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base);
if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; }
snprintf(p, sizeof p, "rm -rf %s/%s.sepwork", tmpdir, base);
if (runwait(p) != 0) {
fprintf(stderr, "cleanup failed: %s\n", p);
bad = 1;
}
snprintf(p, sizeof p, "%s/%s.o", tmpdir, base);
if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; }
snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base);
if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; }
if (rmdir(tmpdir) != 0) { perror(tmpdir); bad = 1; }
return bad;
}
static int
@@ -124,8 +122,11 @@ write_source(const char *path, const char *src)
{
FILE *f = fopen(path, "wb");
if (!f) return -1;
fputs(src, f);
fclose(f);
if (fputs(src, f) == EOF) {
fclose(f);
return -1;
}
if (fclose(f) != 0) return -1;
return 0;
}
@@ -134,49 +135,14 @@ build_via_driver(const char *driver, const char *tmpdir, const char *src)
{
char cmd[2048];
/* #93 sep layout: emit asm to <src-stem>.sepwork/__root.s; pin
* WW_PKGCACHE under tmpdir so out/.pkgcache is untouched. */
* all outputs stay under tmpdir. */
snprintf(cmd, sizeof cmd,
"cd %s && b='%s'; WW_PKGCACHE=%s/pkgc timeout 180 %s build --sep "
"cd %s && b='%s'; timeout 180 %s build -S "
"-o \"${b%%.ww}\" \"$b\" 2>/dev/null",
tmpdir, src, tmpdir, driver);
tmpdir, src, driver);
return runwait(cmd);
}
static int
run_row(const char *driver, const struct row *r, int seq)
{
char tmpdir[256], src[512], base[64], outbin[768];
snprintf(tmpdir, sizeof tmpdir, "/tmp/npav_%d_d_%d", getpid(), seq);
snprintf(base, sizeof base, "main789");
snprintf(src, sizeof src, "%s/%s.ww", tmpdir, base);
mkdir(tmpdir, 0755);
if (write_source(src, r->src) != 0) { cleanup_tmp(tmpdir, base); return -1; }
int rc;
if (build_via_driver(driver, tmpdir, src) == 0) {
snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base);
rc = runwait(outbin);
} else {
rc = -1;
}
cleanup_tmp(tmpdir, base);
return rc;
}
/* build_fails — 1 iff the driver build FAILS (used by expect_reject). */
static int
build_fails(const char *driver, const struct row *r, int seq)
{
char tmpdir[256], src[512], base[64];
snprintf(tmpdir, sizeof tmpdir, "/tmp/npav_%d_x_%d", getpid(), seq);
snprintf(base, sizeof base, "main789");
snprintf(src, sizeof src, "%s/%s.ww", tmpdir, base);
mkdir(tmpdir, 0755);
if (write_source(src, r->src) != 0) { cleanup_tmp(tmpdir, base); return 0; }
int br = build_via_driver(driver, tmpdir, src);
cleanup_tmp(tmpdir, base);
return br != 0;
}
static int
asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r,
int seq)
@@ -185,11 +151,18 @@ asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r,
snprintf(tdc, sizeof tdc, "/tmp/npav_%d_c_%d", getpid(), seq);
snprintf(tdw, sizeof tdw, "/tmp/npav_%d_w_%d", getpid(), seq);
snprintf(base, sizeof base, "main789");
mkdir(tdc, 0755);
mkdir(tdw, 0755);
int rc = -1, tdw_owned = 0, cleanfail = 0;
if (mkdir(tdc, 0755) != 0) {
perror(tdc);
return -1;
}
if (mkdir(tdw, 0755) != 0) {
perror(tdw);
goto out;
}
tdw_owned = 1;
snprintf(src, sizeof src, "%s/%s.ww", tdc, base);
if (write_source(src, r->src) != 0) { cleanup_tmp(tdc, base); cleanup_tmp(tdw, base); return -1; }
int rc = -1;
if (write_source(src, r->src) != 0) goto out;
if (build_via_driver(cdrv, tdc, src) != 0) goto out;
snprintf(cs, sizeof cs, "%s/%s.sepwork/__root.s", tdc, base);
snprintf(src, sizeof src, "%s/%s.ww", tdw, base);
@@ -210,8 +183,13 @@ asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r,
if (fc) fclose(fc);
if (fw) fclose(fw);
out:
cleanup_tmp(tdc, base);
cleanup_tmp(tdw, base);
cleanfail = cleanup_tmp(tdc, base);
if (tdw_owned && cleanup_tmp(tdw, base) != 0)
cleanfail = 1;
/* a leaked tree fails an otherwise-green row; a real diff result
* (rc == -1) is never overwritten (110's cleanfail shape). */
if (cleanfail && rc == 0)
rc = -1;
return rc;
}
@@ -239,47 +217,12 @@ main(void)
for (int i = 0; i < n; i++) {
const struct row *r = &rows[i];
if (r->stage_mask & STAGE_CS) {
if (wwpresent && r->byte_id) {
total++;
if (r->expect_reject) {
if (!build_fails(cdrv, r, seq++)) {
fprintf(stderr, "npav[cs][%s]: built but expected reject\n",
r->label);
fail++;
}
} else {
int got = run_row(cdrv, r, seq++);
if (got != r->want_exit) {
fprintf(stderr, "npav[cs][%s]: exit=%d want=%d\n",
r->label, got, r->want_exit);
fail++;
}
}
}
if (wwpresent && (r->stage_mask & STAGE_WW)) {
total++;
if (r->expect_reject) {
if (!build_fails(wdrv, r, seq++)) {
fprintf(stderr, "npav[ww][%s]: built but expected reject\n",
r->label);
fail++;
}
} else {
int got = run_row(wdrv, r, seq++);
if (got != r->want_exit) {
fprintf(stderr, "npav[ww][%s]: exit=%d want=%d\n",
r->label, got, r->want_exit);
fail++;
}
if (r->byte_id) {
total++;
if (asm_byte_identical(cdrv, wdrv, r, seq++) != 0) {
fprintf(stderr, "npav[byte-id][%s]: cstage vs wwstage asm differs\n",
r->label);
fail++;
}
}
if (asm_byte_identical(cdrv, wdrv, r, seq++) != 0) {
fprintf(stderr, "npav[byte-id][%s]: cstage vs wwstage asm differs\n",
r->label);
fail++;
}
}
}

View File

@@ -35,6 +35,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -91,17 +92,29 @@ static const struct row rows[] = {
0, STAGE_CS | STAGE_WW, 1 },
};
static void
/* nonzero on any cleanup failure; ENOENT tolerated because legs that
* fail early never create the later artifacts (a -S build also never
* produces the base binary / .o / .combined.ww). */
static int
cleanup_tmp(const char *tmpdir, const char *base)
{
char p[1024];
snprintf(p, sizeof p, "%s/%s", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "rm -rf %s/%s.sepwork %s/pkgc", tmpdir, base, tmpdir);
if (system(p)) {} /* best-effort */
snprintf(p, sizeof p, "%s/%s.o", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base); unlink(p);
rmdir(tmpdir);
int bad = 0;
snprintf(p, sizeof p, "%s/%s", tmpdir, base);
if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; }
snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base);
if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; }
snprintf(p, sizeof p, "rm -rf %s/%s.sepwork", tmpdir, base);
if (runwait(p) != 0) {
fprintf(stderr, "cleanup failed: %s\n", p);
bad = 1;
}
snprintf(p, sizeof p, "%s/%s.o", tmpdir, base);
if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; }
snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base);
if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; }
if (rmdir(tmpdir) != 0) { perror(tmpdir); bad = 1; }
return bad;
}
static int
@@ -109,8 +122,11 @@ write_source(const char *path, const char *src)
{
FILE *f = fopen(path, "wb");
if (!f) return -1;
fputs(src, f);
fclose(f);
if (fputs(src, f) == EOF) {
fclose(f);
return -1;
}
if (fclose(f) != 0) return -1;
return 0;
}
@@ -119,34 +135,14 @@ build_via_driver(const char *driver, const char *tmpdir, const char *src)
{
char cmd[2048];
/* #93 sep layout: emit asm to <src-stem>.sepwork/__root.s; pin
* WW_PKGCACHE under tmpdir so out/.pkgcache is untouched. */
* all outputs stay under tmpdir. */
snprintf(cmd, sizeof cmd,
"cd %s && b='%s'; WW_PKGCACHE=%s/pkgc timeout 180 %s build --sep "
"cd %s && b='%s'; timeout 180 %s build -S "
"-o \"${b%%.ww}\" \"$b\" 2>/dev/null",
tmpdir, src, tmpdir, driver);
tmpdir, src, driver);
return runwait(cmd);
}
static int
run_row(const char *driver, const struct row *r, int seq)
{
char tmpdir[256], src[512], base[64], outbin[768];
snprintf(tmpdir, sizeof tmpdir, "/tmp/sfsz_%d_d_%d", getpid(), seq);
snprintf(base, sizeof base, "main790");
snprintf(src, sizeof src, "%s/%s.ww", tmpdir, base);
mkdir(tmpdir, 0755);
if (write_source(src, r->src) != 0) { cleanup_tmp(tmpdir, base); return -1; }
int rc;
if (build_via_driver(driver, tmpdir, src) == 0) {
snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base);
rc = runwait(outbin);
} else {
rc = -1;
}
cleanup_tmp(tmpdir, base);
return rc;
}
static int
asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r,
int seq)
@@ -155,11 +151,18 @@ asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r,
snprintf(tdc, sizeof tdc, "/tmp/sfsz_%d_c_%d", getpid(), seq);
snprintf(tdw, sizeof tdw, "/tmp/sfsz_%d_w_%d", getpid(), seq);
snprintf(base, sizeof base, "main790");
mkdir(tdc, 0755);
mkdir(tdw, 0755);
int rc = -1, tdw_owned = 0, cleanfail = 0;
if (mkdir(tdc, 0755) != 0) {
perror(tdc);
return -1;
}
if (mkdir(tdw, 0755) != 0) {
perror(tdw);
goto out;
}
tdw_owned = 1;
snprintf(src, sizeof src, "%s/%s.ww", tdc, base);
if (write_source(src, r->src) != 0) { cleanup_tmp(tdc, base); cleanup_tmp(tdw, base); return -1; }
int rc = -1;
if (write_source(src, r->src) != 0) goto out;
if (build_via_driver(cdrv, tdc, src) != 0) goto out;
snprintf(cs, sizeof cs, "%s/%s.sepwork/__root.s", tdc, base);
snprintf(src, sizeof src, "%s/%s.ww", tdw, base);
@@ -180,8 +183,13 @@ asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r,
if (fc) fclose(fc);
if (fw) fclose(fw);
out:
cleanup_tmp(tdc, base);
cleanup_tmp(tdw, base);
cleanfail = cleanup_tmp(tdc, base);
if (tdw_owned && cleanup_tmp(tdw, base) != 0)
cleanfail = 1;
/* a leaked tree fails an otherwise-green row; a real diff result
* (rc == -1) is never overwritten (110's cleanfail shape). */
if (cleanfail && rc == 0)
rc = -1;
return rc;
}
@@ -209,33 +217,14 @@ main(void)
for (int i = 0; i < n; i++) {
const struct row *r = &rows[i];
if (r->stage_mask & STAGE_CS) {
if (wwpresent && r->byte_id) {
total++;
int got = run_row(cdrv, r, seq++);
if (got != r->want_exit) {
fprintf(stderr, "sfsz[cs][%s]: exit=%d want=%d\n",
r->label, got, r->want_exit);
if (asm_byte_identical(cdrv, wdrv, r, seq++) != 0) {
fprintf(stderr, "sfsz[byte-id][%s]: cstage vs wwstage asm differs\n",
r->label);
fail++;
}
}
if (wwpresent && (r->stage_mask & STAGE_WW)) {
total++;
int got = run_row(wdrv, r, seq++);
if (got != r->want_exit) {
fprintf(stderr, "sfsz[ww][%s]: exit=%d want=%d\n",
r->label, got, r->want_exit);
fail++;
}
if (r->byte_id) {
total++;
if (asm_byte_identical(cdrv, wdrv, r, seq++) != 0) {
fprintf(stderr, "sfsz[byte-id][%s]: cstage vs wwstage asm differs\n",
r->label);
fail++;
}
}
}
}
if (fail) {

View File

@@ -39,13 +39,14 @@
* combined_t16 | {u64,u16} 16B | 24B | recv+field+push| 114
* recvpush_t12 | {u32,u32,u32} 12B | 24B | recv + push | 6
*
* cstage `ww build --sep` + run pins runtime; the wwstage binary is run
* cstage `ww build` + run pins runtime; the wwstage binary is run
* too (the bug WAS a wrong wwstage runtime value); raw cs.s vs ww.s cmp
* over the driver-produced per-package asm pins rule-10 byte-id (the
* discriminating net — pre-fix __root.s diverges at the push/recv/field).
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -72,7 +73,9 @@ slurp_eq(const char *a, const char *b)
if (ca != cb) { rc = -1; break; }
if (ca == EOF) break;
}
fclose(fa); fclose(fb);
if (ferror(fa) || ferror(fb)) rc = -1;
if (fclose(fa) != 0) rc = -1;
if (fclose(fb) != 0) rc = -1;
return rc;
}
@@ -201,15 +204,17 @@ run_scenario(const char *cdrv, const char *wdrv, const struct scenario *sc)
FILE *f = fopen(path, "wb");
if (!f) { fprintf(stderr, "793[%s]: write %s\n", sc->label,
sc->files[i].name); rc = -1; goto done; }
fputs(sc->files[i].src, f);
fclose(f);
int bad = fputs(sc->files[i].src, f) == EOF;
if (fclose(f) != 0) bad = 1;
if (bad) { fprintf(stderr, "793[%s]: write %s\n", sc->label,
sc->files[i].name); rc = -1; goto done; }
}
/* cstage --sep build + run pins runtime; pin WW_PKGCACHE under the
* scratch dir so out/.pkgcache is untouched. */
/* cstage build + run pins runtime; all outputs remain under the
* scratch dir. */
snprintf(cmd, sizeof cmd,
"cd %s && WW_PKGCACHE=%s/pkgc_c %s build --sep -I %s -o %s/main %s/main.ww",
dir, dir, cdrv, dir, dir, dir);
"cd %s && %s build -I %s -o %s/main %s/main.ww",
dir, cdrv, dir, dir, dir);
if (runwait(cmd) != 0) {
fprintf(stderr, "793[%s]: cstage build failed\n", sc->label);
rc = -1; goto done;
@@ -222,11 +227,11 @@ run_scenario(const char *cdrv, const char *wdrv, const struct scenario *sc)
rc = -1;
}
/* wwstage --sep build + run: the bug WAS a wrong wwstage runtime
/* wwstage build + run: the bug WAS a wrong wwstage runtime
* value (a dropped/over-copied field), so run the wwstage binary too. */
snprintf(cmd, sizeof cmd,
"cd %s && WW_PKGCACHE=%s/pkgc_w %s build --sep -I %s -o %s/mainww %s/main.ww",
dir, dir, wdrv, dir, dir, dir);
"cd %s && %s build -I %s -o %s/mainww %s/main.ww",
dir, wdrv, dir, dir, dir);
if (runwait(cmd) != 0) {
fprintf(stderr, "793[%s]: ww_ww build failed\n", sc->label);
rc = -1; goto done;
@@ -245,10 +250,22 @@ run_scenario(const char *cdrv, const char *wdrv, const struct scenario *sc)
char cs_s[1024], ws_s[1024];
snprintf(cs_s, sizeof cs_s, "%s/all_cs.s", dir);
snprintf(ws_s, sizeof ws_s, "%s/all_ww.s", dir);
snprintf(cmd, sizeof cmd, "cat %s/main.sepwork/*.s > %s 2>/dev/null",
dir, cs_s); if (system(cmd)) {}
snprintf(cmd, sizeof cmd, "cat %s/mainww.sepwork/*.s > %s 2>/dev/null",
dir, ws_s); if (system(cmd)) {}
snprintf(cmd, sizeof cmd,
"cat %s/main.sepwork/*.s > %s 2>/dev/null && test -s %s",
dir, cs_s, cs_s);
if (runwait(cmd) != 0) {
fprintf(stderr, "793[%s]: cstage asm aggregation failed\n",
sc->label);
rc = -1; goto done;
}
snprintf(cmd, sizeof cmd,
"cat %s/mainww.sepwork/*.s > %s 2>/dev/null && test -s %s",
dir, ws_s, ws_s);
if (runwait(cmd) != 0) {
fprintf(stderr, "793[%s]: wwstage asm aggregation failed\n",
sc->label);
rc = -1; goto done;
}
if (slurp_eq(cs_s, ws_s) != 0) {
fprintf(stderr, "793[%s]: cs.s/ww.s DIFFER (rule-10 byte-id "
"violation — #21/#224 regression)\n", sc->label);
@@ -256,8 +273,33 @@ run_scenario(const char *cdrv, const char *wdrv, const struct scenario *sc)
}
done:
snprintf(cmd, sizeof cmd, "rm -rf %s", dir);
(void)runwait(cmd);
snprintf(cmd, sizeof cmd, "rm -rf %s/main.sepwork", dir);
if (runwait(cmd) != 0) { fprintf(stderr, "793[%s]: cleanup main.sepwork\n",
sc->label); rc = -1; }
snprintf(cmd, sizeof cmd, "rm -rf %s/mainww.sepwork", dir);
if (runwait(cmd) != 0) { fprintf(stderr, "793[%s]: cleanup mainww.sepwork\n",
sc->label); rc = -1; }
for (int i = 0; sc->files[i].name; i++) {
snprintf(path, sizeof path, "%s/%s", dir, sc->files[i].name);
if (unlink(path) != 0 && errno != ENOENT) {
fprintf(stderr, "793[%s]: cleanup %s\n", sc->label,
sc->files[i].name);
rc = -1;
}
}
const char *children[] = { "main", "mainww", "all_cs.s", "all_ww.s", NULL };
for (int i = 0; children[i]; i++) {
snprintf(path, sizeof path, "%s/%s", dir, children[i]);
if (unlink(path) != 0 && errno != ENOENT) {
fprintf(stderr, "793[%s]: cleanup %s\n", sc->label,
children[i]);
rc = -1;
}
}
if (rmdir(dir) != 0) {
fprintf(stderr, "793[%s]: cleanup directory\n", sc->label);
rc = -1;
}
return rc;
}

View File

@@ -51,6 +51,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -119,18 +120,20 @@ main(void)
FILE *f = fopen(path, "wb");
if (!f) { fprintf(stderr, "794: write %s\n", files[i].name);
rc = 1; goto done; }
fputs(files[i].src, f);
fclose(f);
int bad = fputs(files[i].src, f) == EOF;
if (fclose(f) != 0) bad = 1;
if (bad) { fprintf(stderr, "794: write %s\n", files[i].name);
rc = 1; goto done; }
}
/* #94 sep layout: cstage's --sep build (prefer is correct) compiles
* the units and links the binary; pin WW_PKGCACHE under the scratch
* dir so out/.pkgcache is untouched. NO cs==ww byte-id here — the #55
/* #94 sep layout: cstage's build (prefer is correct) compiles
* the units and links the binary under the scratch dir. NO cs==ww
* byte-id here — the #55
* checker fix has an open cgen-side bare-leaf sibling that diverges the
* asm independently; the sound discriminator is the wwstage ACCEPT. */
snprintf(cmd, sizeof cmd,
"cd %s && WW_PKGCACHE=%s/pkgc_c %s build --sep -I %s -o %s/main %s/main.ww",
dir, dir, cdrv, dir, dir, dir);
"cd %s && %s build -I %s -o %s/main %s/main.ww",
dir, cdrv, dir, dir, dir);
if (runwait(cmd) != 0) {
fprintf(stderr, "794: cstage build failed\n");
rc = 1; goto done;
@@ -143,14 +146,14 @@ main(void)
rc = 1; goto done;
}
/* The #55 discriminator: the wwstage driver's --sep build runs the
/* The #55 discriminator: the wwstage driver's build runs the
* wwstage checker over the same units and must ACCEPT. PRE-fix it
* rejected (bare `v` -> main.v: i64 -> return mismatch); POST-fix it
* prefers curmod=aa -> aa.v: i32 and accepts. */
snprintf(cmd, sizeof cmd,
"cd %s && WW_PKGCACHE=%s/pkgc_w %s build --sep -I %s -o %s/mainww %s/main.ww "
"cd %s && %s build -I %s -o %s/mainww %s/main.ww "
">/dev/null 2>&1",
dir, dir, wdrv, dir, dir, dir);
dir, wdrv, dir, dir, dir);
if (runwait(cmd) != 0) {
fprintf(stderr, "794: ww_ww REJECTED the units (#55: bare "
"value-ident resolved the wrong module's same-leaf symbol)\n");
@@ -158,8 +161,31 @@ main(void)
}
done:
snprintf(cmd, sizeof cmd, "rm -rf %s", dir);
(void)runwait(cmd);
snprintf(cmd, sizeof cmd, "rm -rf %s/main.sepwork", dir);
if (runwait(cmd) != 0) { fprintf(stderr, "794: cleanup main.sepwork\n");
rc = 1; }
snprintf(cmd, sizeof cmd, "rm -rf %s/mainww.sepwork", dir);
if (runwait(cmd) != 0) { fprintf(stderr, "794: cleanup mainww.sepwork\n");
rc = 1; }
for (int i = 0; files[i].name; i++) {
snprintf(path, sizeof path, "%s/%s", dir, files[i].name);
if (unlink(path) != 0 && errno != ENOENT) {
fprintf(stderr, "794: cleanup %s\n", files[i].name);
rc = 1;
}
}
const char *children[] = { "main", "mainww", NULL };
for (int i = 0; children[i]; i++) {
snprintf(path, sizeof path, "%s/%s", dir, children[i]);
if (unlink(path) != 0 && errno != ENOENT) {
fprintf(stderr, "794: cleanup %s\n", children[i]);
rc = 1;
}
}
if (rmdir(dir) != 0) {
fprintf(stderr, "794: cleanup directory\n");
rc = 1;
}
if (rc) {
fprintf(stderr, "794 xmod_ident_prefer: FAILED\n");
return 1;

View File

@@ -1,7 +1,7 @@
/*
* 795_xmod_valglobal_run — project #1 close, the cgen residual of #55.
* Runtime + cs==ww byte-id net for the cross-module bare value-GLOBAL
* load miscompile. Sibling of the CHECKER test 794_xmod_ident_prefer,
* Assembly byte-id net for the cross-module bare value-GLOBAL load
* miscompile. Sibling of the CHECKER test 794_xmod_ident_prefer,
* which deliberately omitted a byte-id assertion because THIS cgen bug
* would diverge the asm independently (see 794's NOTE). With #1 fixed,
* the byte-id now holds and is asserted here.
@@ -29,13 +29,8 @@
* exported aa.v stays `v`, the private main.v stays `main.v`, no
* collision.
*
* EACH ROW CARRIES BOTH DIMENSIONS (953_f64crossmod_run model):
* (a) cstage `ww build` (in a /tmp scratch dir) + run, asserting the
* exit code — pins that the converged asm is runtime-correct.
* (b) w6c vs w6c_ww `.s` cmp — FAILS if the stages diverge. Necessary
* because the bug was byte-id-blind (both stages were wrong the
* same way); a green byte-id alone never caught it, so the runtime
* row (a) is the real net and (b) guards rule-10 going forward.
* Runtime ownership moved to the matching r795_* fixtures. This carrier
* retains the w6c vs w6c_ww `.s` comparison required by rule 10.
*
* Single-file multi-package form (like 953): `package aa; ... package
* main; import aa; ...` in one source, so w6c/w6c_ww see the cross-
@@ -148,37 +143,30 @@ main(void)
* rm -rf at the end reclaims everything. */
char tmpdir[64];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwxmv_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
char src[128], outbin[128], rmcmd[160];
snprintf(src, sizeof src, "%s/wwxmv_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/wwxmv_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (f == NULL) { fail++; runwait(rmcmd); continue; }
fputs(rows[i].src, f);
fclose(f);
/* (a) cstage build + run. */
char cmd[2048];
snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s", bin, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: cstage build failed\n",
rows[i].label);
/* an unowned dir must never be rm -rf'd below */
if (mkdir(tmpdir, 0755) != 0) {
perror(tmpdir);
fail++;
runwait(rmcmd);
continue;
}
int got = runwait(outbin);
if (got != rows[i].want_exit) {
fprintf(stderr, "row[%s]: cstage exit %d, want %d\n",
rows[i].label, got, rows[i].want_exit);
fail++;
}
char src[128], rmcmd[160];
snprintf(src, sizeof src, "%s/wwxmv_%d_%d.ww", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
/* (b) cs==ww byte-id gate. */
FILE *f = fopen(src, "wb");
if (f == NULL) {
fail++;
if (runwait(rmcmd) != 0)
fprintf(stderr, "row[%s]: cleanup failed: %s\n",
rows[i].label, rmcmd);
continue;
}
fputs(rows[i].src, f);
fclose(f);
char cmd[2048];
/* Runtime ownership is r79_xmod_valglobal_*; retain byte-id. */
char cs_s[128], ws_s[128];
snprintf(cs_s, sizeof cs_s, "%s/wwxmv_%d_%d_cs.s", tmpdir, getpid(), i);
snprintf(ws_s, sizeof ws_s, "%s/wwxmv_%d_%d_ww.s", tmpdir, getpid(), i);
@@ -186,26 +174,42 @@ main(void)
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, cs_s, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label);
fail++; runwait(rmcmd); continue;
fail++;
if (runwait(rmcmd) != 0)
fprintf(stderr, "row[%s]: cleanup failed: %s\n",
rows[i].label, rmcmd);
continue;
}
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
w6c_ww, ws_s, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww failed\n", rows[i].label);
fail++; runwait(rmcmd); continue;
fail++;
if (runwait(rmcmd) != 0)
fprintf(stderr, "row[%s]: cleanup failed: %s\n",
rows[i].label, rmcmd);
continue;
}
if (slurp_eq(cs_s, ws_s) != 0) {
int rowdiff = slurp_eq(cs_s, ws_s) != 0;
if (rowdiff) {
fprintf(stderr, "row[%s]: cstage/wwstage .s DIFFER "
"(rule-10 byte-id violation)\n", rows[i].label);
fail++;
}
runwait(rmcmd);
/* a leaked tmpdir fails an otherwise-green row without
* masking the byte-id verdict. */
if (runwait(rmcmd) != 0) {
fprintf(stderr, "row[%s]: cleanup failed: %s\n",
rows[i].label, rmcmd);
if (!rowdiff)
fail++;
}
}
if (fail) {
fprintf(stderr, "%d/%d xmod value-global tests failed\n", fail, n);
return 1;
}
printf("xmod_valglobal: %d/%d ok (cstage run + cs==ww byte-id)\n", n, n);
printf("xmod_valglobal: %d/%d ok (cs==ww byte-id)\n", n, n);
return 0;
}

View File

@@ -1,7 +1,7 @@
/*
* 796_xmod_valglobal_dot_run — project #229, the N_DOT-base read + addr-of
* sibling of #1 (test 795). Runtime + cs==ww byte-id net for the cross-
* module DOTTED value-global miscompile.
* sibling of #1 (test 795). Assembly byte-id net for the cross-module
* DOTTED value-global miscompile.
*
* THE BUG (cgen-only, both stages emit IDENTICAL wrong asm → byte-id was
* BLIND to it): #1 fixed the DATA def-site and the bare-ident load mangle,
@@ -19,15 +19,10 @@
* lhs.str / basenm). The TY_FN branches beside each site already use this
* dotted polarity via mafn/emitfnname; value globals now match.
*
* EACH ROW CARRIES BOTH DIMENSIONS (795 model):
* (a) cstage `ww build` + run, asserting the exit — pins the converged
* asm is runtime-correct.
* (b) w6c vs w6c_ww `.s` cmp — FAILS if the stages diverge. Necessary
* because the bug was byte-id-blind.
* Runtime ownership moved to the matching r796_* fixtures. This carrier
* retains the w6c vs w6c_ww `.s` comparison required by rule 10.
*
* GATE POLARITY: must stay GREEN. A wrong exit means the dotted value-
* global mangle regressed; a byte-id FAIL means the stages diverged
* (rule-10 violation).
* GATE POLARITY: a byte-id failure means the stages diverged.
*/
#include <stdio.h>
#include <stdlib.h>
@@ -109,56 +104,65 @@ main(void)
for (int i = 0; rows[i].src; i++, n++) {
char tmpdir[64];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwxmvd_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
/* an unowned dir must never be rm -rf'd below */
if (mkdir(tmpdir, 0755) != 0) {
perror(tmpdir);
fail++;
continue;
}
char src[128], outbin[128], cs_s[128], ws_s[128], rmcmd[160];
char src[128], cs_s[128], ws_s[128], rmcmd[160];
snprintf(src, sizeof src, "%s/wwxmvd_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/wwxmvd_%d_%d", tmpdir, getpid(), i);
snprintf(cs_s, sizeof cs_s, "%s/wwxmvd_%d_%d_cs.s", tmpdir, getpid(), i);
snprintf(ws_s, sizeof ws_s, "%s/wwxmvd_%d_%d_ww.s", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (f == NULL) { fail++; runwait(rmcmd); continue; }
if (f == NULL) {
fail++;
if (runwait(rmcmd) != 0)
fprintf(stderr, "row[%s]: cleanup failed: %s\n",
rows[i].label, rmcmd);
continue;
}
fputs(rows[i].src, f);
fclose(f);
/* (a) cstage build + run. */
char cmd[2048];
snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s", bin, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: cstage build failed\n",
rows[i].label);
fail++;
runwait(rmcmd);
continue;
}
int got = runwait(outbin);
if (got != rows[i].want_exit) {
fprintf(stderr, "row[%s]: cstage exit %d, want %d\n",
rows[i].label, got, rows[i].want_exit);
fail++;
}
/* (b) cs==ww byte-id gate. */
/* Runtime ownership is r79_xmod_valglobal_dot_*; retain byte-id. */
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, cs_s, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label);
fail++; runwait(rmcmd); continue;
fail++;
if (runwait(rmcmd) != 0)
fprintf(stderr, "row[%s]: cleanup failed: %s\n",
rows[i].label, rmcmd);
continue;
}
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
w6c_ww, ws_s, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww failed\n", rows[i].label);
fail++; runwait(rmcmd); continue;
fail++;
if (runwait(rmcmd) != 0)
fprintf(stderr, "row[%s]: cleanup failed: %s\n",
rows[i].label, rmcmd);
continue;
}
if (slurp_eq(cs_s, ws_s) != 0) {
int rowdiff = slurp_eq(cs_s, ws_s) != 0;
if (rowdiff) {
fprintf(stderr, "row[%s]: cstage/wwstage .s DIFFER "
"(rule-10 byte-id violation)\n", rows[i].label);
fail++;
}
runwait(rmcmd);
/* a leaked tmpdir fails an otherwise-green row without
* masking the byte-id verdict. */
if (runwait(rmcmd) != 0) {
fprintf(stderr, "row[%s]: cleanup failed: %s\n",
rows[i].label, rmcmd);
if (!rowdiff)
fail++;
}
}
if (fail) {
@@ -166,7 +170,7 @@ main(void)
fail, n);
return 1;
}
printf("xmod_valglobal_dot: %d/%d ok (cstage run + cs==ww byte-id)\n",
printf("xmod_valglobal_dot: %d/%d ok (cs==ww byte-id)\n",
n, n);
return 0;
}

View File

@@ -1,5 +1,5 @@
/*
* 794_xmod_struct_field_layout_collide_run — project #31 (RULING R2,
* 797_xmod_struct_field_layout_collide_run — project #31 (RULING R2,
* commit-1: the OFFSET/SIZE align-up arms) runtime + byte-id net. The
* direct continuation of 793 (#21): #21 type-keyed the THREE caller-side
* sites the 793 collision drove (push/recv/field-read); #31 closes the
@@ -59,7 +59,7 @@
*
* Each row reddens under an INDEPENDENT revert of its arm (verified
* impl-side: R1 ww=101, C1 cs!=ww OOB, A1/A2 ww=8, W1/W2 ww=107 cs!=ww).
* cstage `ww build --sep` + run pins runtime; the wwstage binary is run too
* cstage `ww build` + run pins runtime; the wwstage binary is run too
* (the bug WAS a wrong wwstage runtime value / OOB); raw cs.s vs ww.s over
* the driver-produced per-package asm pins rule-10 byte-id.
*
@@ -72,6 +72,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -98,7 +99,9 @@ slurp_eq(const char *a, const char *b)
if (ca != cb) { rc = -1; break; }
if (ca == EOF) break;
}
fclose(fa); fclose(fb);
if (ferror(fa) || ferror(fb)) rc = -1;
if (fclose(fa) != 0) rc = -1;
if (fclose(fb) != 0) rc = -1;
return rc;
}
@@ -303,9 +306,9 @@ static const struct scenario scenarios[] = {
static int
run_scenario(const char *cdrv, const char *wdrv, const struct scenario *sc)
{
char dir[] = "/tmp/ww794_XXXXXX";
char dir[] = "/tmp/ww797_XXXXXX";
if (mkdtemp(dir) == NULL) {
fprintf(stderr, "794[%s]: mkdtemp failed\n", sc->label);
fprintf(stderr, "797[%s]: mkdtemp failed\n", sc->label);
return -1;
}
@@ -315,38 +318,40 @@ run_scenario(const char *cdrv, const char *wdrv, const struct scenario *sc)
for (int i = 0; sc->files[i].name; i++) {
snprintf(path, sizeof path, "%s/%s", dir, sc->files[i].name);
FILE *f = fopen(path, "wb");
if (!f) { fprintf(stderr, "794[%s]: write %s\n", sc->label,
if (!f) { fprintf(stderr, "797[%s]: write %s\n", sc->label,
sc->files[i].name); rc = -1; goto done; }
int bad = fputs(sc->files[i].src, f) == EOF;
if (fclose(f) != 0) bad = 1;
if (bad) { fprintf(stderr, "797[%s]: write %s\n", sc->label,
sc->files[i].name); rc = -1; goto done; }
fputs(sc->files[i].src, f);
fclose(f);
}
snprintf(cmd, sizeof cmd,
"cd %s && WW_PKGCACHE=%s/pkgc_c %s build --sep -I %s -o %s/main %s/main.ww",
dir, dir, cdrv, dir, dir, dir);
"cd %s && %s build -I %s -o %s/main %s/main.ww",
dir, cdrv, dir, dir, dir);
if (runwait(cmd) != 0) {
fprintf(stderr, "794[%s]: cstage build failed\n", sc->label);
fprintf(stderr, "797[%s]: cstage build failed\n", sc->label);
rc = -1; goto done;
}
snprintf(path, sizeof path, "%s/main", dir);
int gotc = runwait(path);
if (gotc != sc->want_exit) {
fprintf(stderr, "794[%s]: cstage exit %d, want %d\n",
fprintf(stderr, "797[%s]: cstage exit %d, want %d\n",
sc->label, gotc, sc->want_exit);
rc = -1;
}
snprintf(cmd, sizeof cmd,
"cd %s && WW_PKGCACHE=%s/pkgc_w %s build --sep -I %s -o %s/mainww %s/main.ww",
dir, dir, wdrv, dir, dir, dir);
"cd %s && %s build -I %s -o %s/mainww %s/main.ww",
dir, wdrv, dir, dir, dir);
if (runwait(cmd) != 0) {
fprintf(stderr, "794[%s]: ww_ww build failed\n", sc->label);
fprintf(stderr, "797[%s]: ww_ww build failed\n", sc->label);
rc = -1; goto done;
}
snprintf(path, sizeof path, "%s/mainww", dir);
int gotw = runwait(path);
if (gotw != sc->want_exit) {
fprintf(stderr, "794[%s]: wwstage exit %d, want %d\n",
fprintf(stderr, "797[%s]: wwstage exit %d, want %d\n",
sc->label, gotw, sc->want_exit);
rc = -1;
}
@@ -354,19 +359,56 @@ run_scenario(const char *cdrv, const char *wdrv, const struct scenario *sc)
char cs_s[1024], ws_s[1024];
snprintf(cs_s, sizeof cs_s, "%s/all_cs.s", dir);
snprintf(ws_s, sizeof ws_s, "%s/all_ww.s", dir);
snprintf(cmd, sizeof cmd, "cat %s/main.sepwork/*.s > %s 2>/dev/null",
dir, cs_s); if (system(cmd)) {}
snprintf(cmd, sizeof cmd, "cat %s/mainww.sepwork/*.s > %s 2>/dev/null",
dir, ws_s); if (system(cmd)) {}
snprintf(cmd, sizeof cmd,
"cat %s/main.sepwork/*.s > %s 2>/dev/null && test -s %s",
dir, cs_s, cs_s);
if (runwait(cmd) != 0) {
fprintf(stderr, "797[%s]: cstage asm aggregation failed\n",
sc->label);
rc = -1; goto done;
}
snprintf(cmd, sizeof cmd,
"cat %s/mainww.sepwork/*.s > %s 2>/dev/null && test -s %s",
dir, ws_s, ws_s);
if (runwait(cmd) != 0) {
fprintf(stderr, "797[%s]: wwstage asm aggregation failed\n",
sc->label);
rc = -1; goto done;
}
if (slurp_eq(cs_s, ws_s) != 0) {
fprintf(stderr, "794[%s]: cs.s/ww.s DIFFER (rule-10 byte-id "
fprintf(stderr, "797[%s]: cs.s/ww.s DIFFER (rule-10 byte-id "
"violation — #31 regression)\n", sc->label);
rc = -1;
}
done:
snprintf(cmd, sizeof cmd, "rm -rf %s", dir);
(void)runwait(cmd);
snprintf(cmd, sizeof cmd, "rm -rf %s/main.sepwork", dir);
if (runwait(cmd) != 0) { fprintf(stderr, "797[%s]: cleanup main.sepwork\n",
sc->label); rc = -1; }
snprintf(cmd, sizeof cmd, "rm -rf %s/mainww.sepwork", dir);
if (runwait(cmd) != 0) { fprintf(stderr, "797[%s]: cleanup mainww.sepwork\n",
sc->label); rc = -1; }
for (int i = 0; sc->files[i].name; i++) {
snprintf(path, sizeof path, "%s/%s", dir, sc->files[i].name);
if (unlink(path) != 0 && errno != ENOENT) {
fprintf(stderr, "797[%s]: cleanup %s\n", sc->label,
sc->files[i].name);
rc = -1;
}
}
const char *children[] = { "main", "mainww", "all_cs.s", "all_ww.s", NULL };
for (int i = 0; children[i]; i++) {
snprintf(path, sizeof path, "%s/%s", dir, children[i]);
if (unlink(path) != 0 && errno != ENOENT) {
fprintf(stderr, "797[%s]: cleanup %s\n", sc->label,
children[i]);
rc = -1;
}
}
if (rmdir(dir) != 0) {
fprintf(stderr, "797[%s]: cleanup directory\n", sc->label);
rc = -1;
}
return rc;
}
@@ -387,7 +429,7 @@ main(void)
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
if (access(wdrv, X_OK) != 0) {
fprintf(stderr, "794: ww_ww missing — cannot run the cs==ww "
fprintf(stderr, "797: ww_ww missing — cannot run the cs==ww "
"byte-id gate (the whole point of this test)\n");
return 1;
}
@@ -400,7 +442,7 @@ main(void)
}
if (fail) {
fprintf(stderr, "794 xmod_struct_field_layout_collide: %d/%d "
fprintf(stderr, "797 xmod_struct_field_layout_collide: %d/%d "
"scenarios failed\n", fail, n);
return 1;
}

View File

@@ -37,14 +37,17 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
return rc;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
struct row { const char *label; const char *src; };
@@ -120,28 +123,39 @@ main(void)
char cs_s[64], ws_s[64], cmd[2048];
snprintf(cs_s, sizeof cs_s, "/tmp/wwtsc_%d_%d_cs.s", getpid(), i);
snprintf(ws_s, sizeof ws_s, "/tmp/wwtsc_%d_%d_ww.s", getpid(), i);
int rowfail = 0;
/* (a) cstage must COMPILE the over-cap tuple callee (no loud-stop). */
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, cs_s, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c loud-stopped (Fold A regressed)\n",
rows[i].label);
fail++; unlink(src); continue;
rowfail = 1;
goto rowcleanup;
}
/* (a') wwstage must compile it too. */
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c_ww, ws_s, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww loud-stopped (Fold A regressed)\n",
rows[i].label);
fail++; unlink(src); unlink(cs_s); continue;
rowfail = 1;
goto rowcleanup;
}
/* (b) cs==ww byte-id gate. */
if (slurp_eq(cs_s, ws_s) != 0) {
fprintf(stderr, "row[%s]: cstage/wwstage .s DIFFER "
"(rule-10 byte-id violation)\n", rows[i].label);
fail++;
rowfail = 1;
}
unlink(src); unlink(cs_s); unlink(ws_s);
rowcleanup:
/* a failed leg may still have written a partial .s; ENOENT is
* the leg-never-wrote case, any other cleanup failure must not
* silently leak. */
if (unlink(src) != 0 && errno != ENOENT) { perror(src); rowfail = 1; }
if (unlink(cs_s) != 0 && errno != ENOENT) { perror(cs_s); rowfail = 1; }
if (unlink(ws_s) != 0 && errno != ENOENT) { perror(ws_s); rowfail = 1; }
if (rowfail)
fail++;
}
if (fail) {

View File

@@ -134,6 +134,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -1326,24 +1327,31 @@ errlog_has(const char *path, const char *needle)
static int
run_driver(const char *driver, const struct row *r, int i)
{
char tmpdir[64], src[128], outbin[128], errlog[128], rmcmd[160];
char tmpdir[] = "/tmp/plad_XXXXXX";
char src[128], outbin[128], errlog[128], scratch[160], rmcmd[192];
char cmd[1200];
snprintf(tmpdir, sizeof tmpdir, "/tmp/plad_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
int result = -2;
if (mkdtemp(tmpdir) == NULL) {
fprintf(stderr, "row[%s]: workspace acquisition failed\n", r->label);
return result;
}
snprintf(src, sizeof src, "%s/plad_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/plad_%d_%d", tmpdir, getpid(), i);
snprintf(errlog, sizeof errlog, "%s/err", tmpdir);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
snprintf(scratch, sizeof scratch, "%s.sepwork", outbin);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", scratch);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
if (!f) goto done;
fputs(r->src, f);
fclose(f);
int werr = ferror(f);
if (fclose(f) != 0) werr = 1;
if (werr) goto done;
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>%s",
driver, outbin, src, errlog);
if (runwait(cmd) != 0) {
int rc = -1;
result = -1;
if (r->want != BUILD_FAIL) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
@@ -1352,16 +1360,29 @@ run_driver(const char *driver, const struct row *r, int i)
fprintf(stderr, "row[%s]: %s build failed without "
"expected diagnostic \"%s\"\n",
r->label, driver, r->expect_err);
rc = -3; /* failed, but for the wrong reason */
result = -3; /* failed, but for the wrong reason */
}
runwait(rmcmd);
return rc;
goto done;
}
int got = runwait(outbin);
result = runwait(outbin);
runwait(rmcmd);
return got;
done:
{
int cleanfail = 0;
if (runwait(rmcmd) != 0) cleanfail = 1;
if (unlink(errlog) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(tmpdir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "row[%s]: workspace cleanup failed\n", r->label);
int passed = r->want == BUILD_FAIL
? result == -1 : result == r->want;
if (passed) result = -2;
}
}
return result;
}
/* asm_byte_identical — generate .s via cstage's w6c and wwstage's
@@ -1371,33 +1392,37 @@ run_driver(const char *driver, const struct row *r, int i)
static int
asm_byte_identical(const char *bin, const struct row *r, int i)
{
char src[64], cs[64], ws[64], cmd[1024];
snprintf(src, sizeof src, "/tmp/plad_asm_%d_%d.ww", getpid(), i);
snprintf(cs, sizeof cs, "/tmp/plad_asm_%d_%d_c.s", getpid(), i);
snprintf(ws, sizeof ws, "/tmp/plad_asm_%d_%d_w.s", getpid(), i);
(void)i;
char dir[] = "/tmp/plad_asm_XXXXXX";
char src[128], cs[128], ws[128], cmd[1024];
int rc = -1;
if (mkdtemp(dir) == NULL) return -1;
snprintf(src, sizeof src, "%s/input.ww", dir);
snprintf(cs, sizeof cs, "%s/c.s", dir);
snprintf(ws, sizeof ws, "%s/w.s", dir);
FILE *f = fopen(src, "wb");
if (!f) return -1;
if (!f) goto done;
fputs(r->src, f);
fclose(f);
int werr = ferror(f);
if (fclose(f) != 0) werr = 1;
if (werr) goto done;
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
unlink(src);
return -1;
goto done;
}
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
bin, ws, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
unlink(src); unlink(cs);
return -1;
goto done;
}
FILE *fc = fopen(cs, "rb");
FILE *fw = fopen(ws, "rb");
int rc = 0;
rc = 0;
if (!fc || !fw) {
rc = -1;
} else {
@@ -1413,7 +1438,20 @@ asm_byte_identical(const char *bin, const struct row *r, int i)
if (rc != 0)
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
r->label);
unlink(src); unlink(cs); unlink(ws);
done:
{
int cleanfail = 0;
if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(cs) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(ws) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(dir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "row[%s]: asm workspace cleanup failed\n",
r->label);
rc = -1;
}
}
return rc;
}

View File

@@ -90,6 +90,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -806,24 +807,31 @@ errlog_has(const char *path, const char *needle)
static int
run_driver(const char *driver, const struct row *r, int i)
{
char tmpdir[64], src[128], outbin[128], errlog[160];
char rmcmd[160], cmd[1200];
snprintf(tmpdir, sizeof tmpdir, "/tmp/applp_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
char tmpdir[] = "/tmp/applp_XXXXXX";
char src[128], outbin[128], errlog[160], scratch[192];
char rmcmd[224], cmd[1200];
int result = -2;
if (mkdtemp(tmpdir) == NULL) {
fprintf(stderr, "row[%s]: workspace acquisition failed\n", r->label);
return result;
}
snprintf(src, sizeof src, "%s/applp_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/applp_%d_%d", tmpdir, getpid(), i);
snprintf(errlog, sizeof errlog, "%s/err", tmpdir);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
snprintf(scratch, sizeof scratch, "%s.sepwork", outbin);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", scratch);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
if (!f) goto done;
fputs(r->src, f);
fclose(f);
int werr = ferror(f);
if (fclose(f) != 0) werr = 1;
if (werr) goto done;
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>%s",
driver, outbin, src, errlog);
if (runwait(cmd) != 0) {
int rc = -1;
result = -1;
if (r->want != BUILD_FAIL) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
@@ -832,16 +840,29 @@ run_driver(const char *driver, const struct row *r, int i)
fprintf(stderr, "row[%s]: %s build failed without "
"expected diagnostic \"%s\"\n",
r->label, driver, r->expect_err);
rc = -3; /* failed, but for the wrong reason */
result = -3; /* failed, but for the wrong reason */
}
runwait(rmcmd);
return rc;
goto done;
}
int got = runwait(outbin);
result = runwait(outbin);
runwait(rmcmd);
return got;
done:
{
int cleanfail = 0;
if (runwait(rmcmd) != 0) cleanfail = 1;
if (unlink(errlog) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(tmpdir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "row[%s]: workspace cleanup failed\n", r->label);
int passed = r->want == BUILD_FAIL
? result == -1 : result == r->want;
if (passed) result = -2;
}
}
return result;
}
/* asm_byte_identical — generate .s via cstage's w6c and wwstage's
@@ -851,33 +872,37 @@ run_driver(const char *driver, const struct row *r, int i)
static int
asm_byte_identical(const char *bin, const struct row *r, int i)
{
char src[64], cs[64], ws[64], cmd[1024];
snprintf(src, sizeof src, "/tmp/applp_asm_%d_%d.ww", getpid(), i);
snprintf(cs, sizeof cs, "/tmp/applp_asm_%d_%d_c.s", getpid(), i);
snprintf(ws, sizeof ws, "/tmp/applp_asm_%d_%d_w.s", getpid(), i);
(void)i;
char dir[] = "/tmp/applp_asm_XXXXXX";
char src[128], cs[128], ws[128], cmd[1024];
int rc = -1;
if (mkdtemp(dir) == NULL) return -1;
snprintf(src, sizeof src, "%s/input.ww", dir);
snprintf(cs, sizeof cs, "%s/c.s", dir);
snprintf(ws, sizeof ws, "%s/w.s", dir);
FILE *f = fopen(src, "wb");
if (!f) return -1;
if (!f) goto done;
fputs(r->src, f);
fclose(f);
int werr = ferror(f);
if (fclose(f) != 0) werr = 1;
if (werr) goto done;
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
unlink(src);
return -1;
goto done;
}
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
bin, ws, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
unlink(src); unlink(cs);
return -1;
goto done;
}
FILE *fc = fopen(cs, "rb");
FILE *fw = fopen(ws, "rb");
int rc = 0;
rc = 0;
if (!fc || !fw) {
rc = -1;
} else {
@@ -893,7 +918,20 @@ asm_byte_identical(const char *bin, const struct row *r, int i)
if (rc != 0)
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
r->label);
unlink(src); unlink(cs); unlink(ws);
done:
{
int cleanfail = 0;
if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(cs) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(ws) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(dir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "row[%s]: asm workspace cleanup failed\n",
r->label);
rc = -1;
}
}
return rc;
}

View File

@@ -17,6 +17,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -129,16 +130,33 @@ main(void)
int n = 0, fail = 0;
for (int i = 0; rows[i].src; i++, n++) {
char tmpdir[80], src[128], outbin[128], rmcmd[160];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwdyn_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
char tmpdir[] = "/tmp/wwdyn_XXXXXX";
char src[128], outbin[128], scratch[160], rmcmd[192];
int rowfail = 0;
if (mkdtemp(tmpdir) == NULL) {
fprintf(stderr, "dyn row %d: mkdtemp failed\n", i);
fail++;
continue;
}
snprintf(src, sizeof src, "%s/wwdyn_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/wwdyn_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
snprintf(scratch, sizeof scratch, "%s.sepwork", outbin);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", scratch);
FILE *f = fopen(src, "wb");
if (f == NULL) {
fprintf(stderr, "dyn row %d: source create failed\n", i);
rowfail = 1;
goto row_done;
}
wwtest_fputs(rows[i].src, f);
fclose(f);
int werr = ferror(f);
if (fclose(f) != 0) werr = 1;
if (werr) {
fprintf(stderr, "dyn row %d: source write failed\n", i);
rowfail = 1;
goto row_done;
}
char cmd[1024];
snprintf(cmd, sizeof cmd,
@@ -146,18 +164,35 @@ main(void)
bin, outbin, src, libdir);
if (runwait(cmd) != 0) {
fprintf(stderr, "dyn row %d: build failed\n", i);
fail++;
runwait(rmcmd);
continue;
rowfail = 1;
goto row_done;
}
int got = runwait(outbin);
if (got != rows[i].want_exit) {
fprintf(stderr, "dyn row %d: exit %d, want %d\n",
i, got, rows[i].want_exit);
fail++;
rowfail = 1;
}
runwait(rmcmd);
row_done:
if (runwait(rmcmd) != 0) {
fprintf(stderr, "dyn row %d: scratch cleanup failed\n", i);
rowfail = 1;
}
if (unlink(outbin) != 0 && errno != ENOENT) {
fprintf(stderr, "dyn row %d: output cleanup failed\n", i);
rowfail = 1;
}
if (unlink(src) != 0 && errno != ENOENT) {
fprintf(stderr, "dyn row %d: source cleanup failed\n", i);
rowfail = 1;
}
if (rmdir(tmpdir) != 0) {
fprintf(stderr, "dyn row %d: workspace cleanup failed\n", i);
rowfail = 1;
}
if (rowfail) fail++;
}
if (fail) {
fprintf(stderr, "%d/%d dyn tests failed\n", fail, n);

View File

@@ -1,402 +1,99 @@
/*
* 815_fmt_int_run — #6 fmt.formattable widen (bare int / uint). Pins
* that lib/fmt/fmt.ww now renders bare `int` and `uint` as REAL union
* members (formattable = (i64|str|bool|rune|f64|int|uint), int=5,
* uint=6 appended last). Before #6 cstage LOUD-rejected a bare int arg
* to the fmt family (no `int` arm to widen into) while wwstage leniently
* accepted via the #128 size/name-keyed int->i64 coercion — cs!=ww.
* Making int/uint real members makes BOTH accept by membership; this
* test is the cstage-now-accepts + correct-render + cs==ww byte-id gate.
*
* row | what it pins
* ---------------+--------------------------------------------------
* bare_int | fprint(int 42) over the writeone path -> "42"
* bare_int_neg | fprint(int -7) -> "-7" (signed i64dec path)
* bare_uint | fprint(uint 42) -> "42"
* uint_highbit | fprint(uint 2^63+1) -> "9223372036854775809"
* | LOAD-BEARING: proves the strconv.u64tos UNSIGNED
* | route (len 19, first byte '9' not '-') — i64dec
* | would render this negative ("-9223372036854775807",
* | len 20). Also proves wwstage tags it uint (6), not
* | i64, under the widened union.
* int_printf | bsprintf("{}", int 42) -> "42" (formatfield ->
* | formatone -> formatraw placeholder path)
* unaffected_i64 | fprint(42i64) -> "42" (existing arm regression guard)
* unaffected_str | fprint("hi") -> "hi" (existing arm regression guard)
* mixed | fprint(1:int, 2i64, true) -> "1 2 true": int(tag5)
* | + i64(tag0) + bool(tag2) coexist in one call,
* | distinct tags, ww auto-space separator.
*
* Each row builds + runs through BOTH `ww` (cstage) and `ww_ww`
* (wwstage) via the driver (-I lib for the fmt import), then a byte-id
* pass builds the combined.ww via the cstage driver and diffs the asm
* w6c emits against w6c_ww — any cs!=ww tag/render divergence reds here.
*
* BOOTSTRAP-EMBED: fmt is NOT embedded in any selfhost combined.ww —
* w6c/wwdump main.ww never reach wcc/err.ww (the lone `import fmt`), so
* neither selfhost/cmd/{w6c,wwdump}/main.combined.ww carries an fmt fn
* (grep-verified: 0 putbytes/writeone/formatraw; the 2 "formattable"
* hits are cgen comment text). #6 therefore rides NO combined.ww regen.
* This test covers the user-program path, which is the only fmt consumer.
*
* GATE POLARITY: must stay GREEN. A red means the int/uint arm dropped,
* uint mis-routed through the signed renderer (highbit prints negative),
* the wwstage mis-tagged uint as i64, or an existing i64/str arm
* regressed.
* 815: residual separated-package cstage/wwstage assembly identity.
* Runtime is owned by r815_*; -S stops both driver legs before assembly,
* archive creation, and linking. The sorted package-assembly concatenation
* remains the byte oracle.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <string.h>
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
struct row {
const char *label;
const char *src;
int want_exit;
};
struct row { const char *label, *path; };
static const struct row rows[] = {
{ "bare_int",
"package main;\n"
"import os;\n"
"import fmt;\n"
"import io;\n"
"import memio;\n"
"export fn main() i32 = {\n"
" let buf: [32]u8;\n"
" let st: memio.stream = memio.fixed(buf[0:32]);\n"
" let vs: io.stream = &st.vt;\n"
" let x: int = 42;\n"
" match (fmt.fprint(vs, x)) { case size => {}; case io.error => { return 91; }; };\n"
" let v: str = memio.string(&st);\n"
" if (v.len != 2) { return 92; };\n"
" if (v[0] != 52u8 || v[1] != 50u8) { return 93; };\n"
" return 50;\n"
"};\n",
50 },
{ "bare_int_neg",
"package main;\n"
"import os;\n"
"import fmt;\n"
"import io;\n"
"import memio;\n"
"export fn main() i32 = {\n"
" let buf: [32]u8;\n"
" let st: memio.stream = memio.fixed(buf[0:32]);\n"
" let vs: io.stream = &st.vt;\n"
" let x: int = -7;\n"
" match (fmt.fprint(vs, x)) { case size => {}; case io.error => { return 91; }; };\n"
" let v: str = memio.string(&st);\n"
" if (v.len != 2) { return 92; };\n"
" if (v[0] != 45u8 || v[1] != 55u8) { return 93; };\n"
" return 51;\n"
"};\n",
51 },
{ "bare_uint",
"package main;\n"
"import os;\n"
"import fmt;\n"
"import io;\n"
"import memio;\n"
"export fn main() i32 = {\n"
" let buf: [32]u8;\n"
" let st: memio.stream = memio.fixed(buf[0:32]);\n"
" let vs: io.stream = &st.vt;\n"
" let x: uint = 42u64: uint;\n"
" match (fmt.fprint(vs, x)) { case size => {}; case io.error => { return 91; }; };\n"
" let v: str = memio.string(&st);\n"
" if (v.len != 2) { return 92; };\n"
" if (v[0] != 52u8 || v[1] != 50u8) { return 93; };\n"
" return 52;\n"
"};\n",
52 },
{ "uint_highbit",
"package main;\n"
"import os;\n"
"import fmt;\n"
"import io;\n"
"import memio;\n"
"export fn main() i32 = {\n"
" let buf: [32]u8;\n"
" let st: memio.stream = memio.fixed(buf[0:32]);\n"
" let vs: io.stream = &st.vt;\n"
" let x: uint = 9223372036854775809u64: uint;\n"
" match (fmt.fprint(vs, x)) { case size => {}; case io.error => { return 91; }; };\n"
" let v: str = memio.string(&st);\n"
" if (v.len != 19) { return 92; };\n"
" if (v[0] != 57u8) { return 93; };\n" /* '9', not '-' (45) */
" if (v[18] != 57u8) { return 94; };\n" /* trailing '9' */
" return 53;\n"
"};\n",
53 },
{ "int_printf",
"package main;\n"
"import os;\n"
"import fmt;\n"
"import io;\n"
"export fn main() i32 = {\n"
" let buf: [16]u8;\n"
" let x: int = 42;\n"
" let v: str = \"\";\n"
" match (fmt.bsprintf(buf[0:16], \"{}\", x)) { case let s: str => { v = s; }; case io.error => { return 91; }; };\n"
" if (v.len != 2) { return 92; };\n"
" if (v[0] != 52u8 || v[1] != 50u8) { return 93; };\n"
" return 54;\n"
"};\n",
54 },
{ "unaffected_i64",
"package main;\n"
"import os;\n"
"import fmt;\n"
"import io;\n"
"import memio;\n"
"export fn main() i32 = {\n"
" let buf: [32]u8;\n"
" let st: memio.stream = memio.fixed(buf[0:32]);\n"
" let vs: io.stream = &st.vt;\n"
" match (fmt.fprint(vs, 42i64)) { case size => {}; case io.error => { return 91; }; };\n"
" let v: str = memio.string(&st);\n"
" if (v.len != 2) { return 92; };\n"
" if (v[0] != 52u8 || v[1] != 50u8) { return 93; };\n"
" return 55;\n"
"};\n",
55 },
{ "unaffected_str",
"package main;\n"
"import os;\n"
"import fmt;\n"
"import io;\n"
"import memio;\n"
"export fn main() i32 = {\n"
" let buf: [32]u8;\n"
" let st: memio.stream = memio.fixed(buf[0:32]);\n"
" let vs: io.stream = &st.vt;\n"
" match (fmt.fprint(vs, \"hi\")) { case size => {}; case io.error => { return 91; }; };\n"
" let v: str = memio.string(&st);\n"
" if (v.len != 2) { return 92; };\n"
" if (v[0] != 104u8 || v[1] != 105u8) { return 93; };\n"
" return 56;\n"
"};\n",
56 },
{ "mixed",
"package main;\n"
"import os;\n"
"import fmt;\n"
"import io;\n"
"import memio;\n"
"export fn main() i32 = {\n"
" let buf: [32]u8;\n"
" let st: memio.stream = memio.fixed(buf[0:32]);\n"
" let vs: io.stream = &st.vt;\n"
" let x: int = 1;\n"
" match (fmt.fprint(vs, x, 2i64, true)) { case size => {}; case io.error => { return 91; }; };\n"
" let v: str = memio.string(&st);\n"
" if (v.len != 8) { return 92; };\n" /* "1 2 true" */
" if (v[0] != 49u8 || v[1] != 32u8) { return 93; };\n" /* '1' ' ' */
" if (v[2] != 50u8 || v[3] != 32u8) { return 94; };\n" /* '2' ' ' */
" if (v[4] != 116u8 || v[7] != 101u8) { return 95; };\n" /* 't' .. 'e' */
" return 57;\n"
"};\n",
57 },
{ "bare_int", "test/wcc/data/r815_bare_int/case.ww" },
{ "bare_int_neg", "test/wcc/data/r815_bare_int_neg/case.ww" },
{ "bare_uint", "test/wcc/data/r815_bare_uint/case.ww" },
{ "uint_highbit", "test/wcc/data/r815_uint_highbit/case.ww" },
{ "int_printf", "test/wcc/data/r815_int_printf/case.ww" },
{ "unaffected_i64", "test/wcc/data/r815_unaffected_i64/case.ww" },
{ "unaffected_str", "test/wcc/data/r815_unaffected_str/case.ww" },
{ "mixed", "test/wcc/data/r815_mixed/case.ww" },
};
static int
write_source(const char *path, const char *src)
{
FILE *f = fopen(path, "wb");
if (!f) return -1;
fputs(src, f);
fclose(f);
return 0;
static int runwait(const char *cmd) {
int rc = system(cmd);
return rc != -1 && WIFEXITED(rc) ? WEXITSTATUS(rc) : -1;
}
/* Per-row tmpdir cleanup. ww_ww writes intermediates next to the source
* (task #15), so each row's build leaves <src>.{combined.ww,s,o} + bare
* exe alongside. Mirror of 781's. */
static void
cleanup_tmp(const char *tmpdir, const char *base)
{
char p[640];
snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "%s/%s.s", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "%s/%s.o", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base); unlink(p);
snprintf(p, sizeof p, "%s/%s", tmpdir, base); unlink(p);
rmdir(tmpdir);
static int copyfile(const char *src, const char *dst) {
char buf[8192]; size_t n; int rc = 0;
FILE *in = fopen(src, "rb"), *out;
if (!in) return -1;
out = fopen(dst, "wb");
if (!out) { fclose(in); return -1; }
while ((n = fread(buf, 1, sizeof buf, in)) != 0)
if (fwrite(buf, 1, n, out) != n) { rc = -1; break; }
if (ferror(in)) rc = -1;
if (fclose(out) != 0) rc = -1;
fclose(in); return rc;
}
static int
build_via_driver(const char *driver, const char *tmpdir, const char *cwd,
const char *src)
{
char cmd[2048];
static int samefile(const char *a, const char *b) {
FILE *fa = fopen(a, "rb"), *fb = fopen(b, "rb"); int rc = 0;
if (!fa || !fb) rc = -1;
else for (;;) { int ca = fgetc(fa), cb = fgetc(fb); if (ca != cb) { rc = -1; break; } if (ca == EOF) break; }
if (fa) fclose(fa); if (fb) fclose(fb); return rc;
}
static int byteid(const char *cdrv, const char *wdrv, const char *cwd,
const struct row *r, int seq) {
char tmp[256], src[512], cs[512], ws[512], cmd[2048]; int rc = -1;
snprintf(tmp, sizeof tmp, "/tmp/fic_%d_b_%d", getpid(), seq);
snprintf(src, sizeof src, "%s/main815b.ww", tmp);
snprintf(cs, sizeof cs, "%s/all_cs.s", tmp);
snprintf(ws, sizeof ws, "%s/all_ww.s", tmp);
/* an unowned path (stale dir, full /tmp) must not be built in — or
* rm -rf'd — below. */
if (mkdir(tmp, 0755) != 0) { perror(tmp); return -1; }
if (copyfile(r->path, src) != 0) goto out;
snprintf(cmd, sizeof cmd,
"cd %s && timeout 180 %s build -I %s/lib %s 2>/dev/null",
tmpdir, driver, cwd, src);
return runwait(cmd);
}
static int
run_row(const char *driver, const char *cwd, const struct row *r, int seq)
{
char tmpdir[256], src[512], base[64], outbin[768];
snprintf(tmpdir, sizeof tmpdir, "/tmp/fic_%d_d_%d", getpid(), seq);
snprintf(base, sizeof base, "main815");
snprintf(src, sizeof src, "%s/%s.ww", tmpdir, base);
mkdir(tmpdir, 0755);
if (write_source(src, r->src) != 0) { cleanup_tmp(tmpdir, base); return -1; }
int rc;
int br = build_via_driver(driver, tmpdir, cwd, src);
if (br == 0) {
snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base);
rc = runwait(outbin);
} else {
rc = -1;
}
cleanup_tmp(tmpdir, base);
return rc;
}
/* asm_byte_identical — #94 sep layout: the cstage driver's --sep build
* (which needs -I lib for fmt import resolution that raw w6c can't do)
* emits per-package w6c asm to <stem>_c.sepwork/, and the wwstage driver's
* --sep build emits per-package w6c_ww asm to <stem>_w.sepwork/. Concat
* (sorted glob, identical package set) and diff — proves no cs!=ww
* tag/render divergence on the widened union. */
static int
asm_byte_identical(const char *cdrv, const char *wdrv, const char *cwd,
const struct row *r, int seq)
{
char tmpdir[256], base[64], src[512], cs[640], ws[640];
char cmd[2048];
snprintf(tmpdir, sizeof tmpdir, "/tmp/fic_%d_b_%d", getpid(), seq);
snprintf(base, sizeof base, "main815b");
snprintf(src, sizeof src, "%s/%s.ww", tmpdir, base);
snprintf(cs, sizeof cs, "%s/all_cs.s", tmpdir);
snprintf(ws, sizeof ws, "%s/all_ww.s", tmpdir);
mkdir(tmpdir, 0755);
if (write_source(src, r->src) != 0) {
snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir);
if (system(cmd)) {}
return -1;
}
int rc = 0;
"cd %s && timeout 180 %s build -S -I %s/lib "
"-o %s/main815b_c %s 2>/dev/null", tmp, cdrv, cwd, tmp, src);
if (runwait(cmd) != 0) goto out;
snprintf(cmd, sizeof cmd,
"cd %s && WW_PKGCACHE=%s/pkgc_c timeout 180 %s build --sep -I %s/lib "
"-o %s/%s_c %s 2>/dev/null", tmpdir, tmpdir, cdrv, cwd, tmpdir, base, src);
"cd %s && timeout 180 %s build -S -I %s/lib "
"-o %s/main815b_w %s 2>/dev/null", tmp, wdrv, cwd, tmp, src);
if (runwait(cmd) != 0) goto out;
snprintf(cmd, sizeof cmd,
"cat %s/main815b_c.sepwork/*.s > %s 2>/dev/null", tmp, cs);
if (runwait(cmd) != 0) goto out;
snprintf(cmd, sizeof cmd,
"cat %s/main815b_w.sepwork/*.s > %s 2>/dev/null", tmp, ws);
if (runwait(cmd) != 0) goto out;
rc = samefile(cs, ws);
out:
if (rc != 0) fprintf(stderr, "fmt_int_run[%s]: byte mismatch/error\n", r->label);
snprintf(cmd, sizeof cmd, "rm -rf %s", tmp);
if (runwait(cmd) != 0) {
fprintf(stderr, "fmt_int_run[byteid][%s]: cstage sep build failed\n",
r->label);
rc = -1;
fprintf(stderr, "fmt_int_run[%s]: cleanup %s failed\n", r->label, tmp);
if (rc == 0) rc = -1;
}
if (rc == 0) {
snprintf(cmd, sizeof cmd,
"cd %s && WW_PKGCACHE=%s/pkgc_w timeout 180 %s build --sep "
"-I %s/lib -o %s/%s_w %s 2>/dev/null",
tmpdir, tmpdir, wdrv, cwd, tmpdir, base, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "fmt_int_run[byteid][%s]: wwstage sep build "
"failed\n", r->label);
rc = -1;
}
}
if (rc == 0) {
snprintf(cmd, sizeof cmd, "cat %s/%s_c.sepwork/*.s > %s 2>/dev/null",
tmpdir, base, cs); if (system(cmd)) {}
snprintf(cmd, sizeof cmd, "cat %s/%s_w.sepwork/*.s > %s 2>/dev/null",
tmpdir, base, ws); if (system(cmd)) {}
FILE *fc = fopen(cs, "rb");
FILE *fw = fopen(ws, "rb");
if (!fc || !fw) {
rc = -1;
} else {
for (;;) {
int a = fgetc(fc);
int b = fgetc(fw);
if (a != b) { rc = -1; break; }
if (a == EOF) break;
}
}
if (fc) fclose(fc);
if (fw) fclose(fw);
if (rc != 0)
fprintf(stderr,
"fmt_int_run[byteid][%s]: cstage vs wwstage asm differs\n",
r->label);
}
snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir);
if (system(cmd)) {}
return rc;
}
int
main(void)
{
const char *bin = getenv("BIN");
int main(void) {
const char *bin = getenv("BIN"); char cwd[1024], absbin[1024];
char cdrv[1024], wdrv[1024];
if (!getcwd(cwd, sizeof cwd)) return 1;
if (!bin) bin = "out/bin";
char cwd[256];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
char absbin[512];
if (bin[0] != '/') {
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
char cdrv[640], wdrv[640];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
if (bin[0] != '/') { snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); bin = absbin; }
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
int wwpresent = (access(wdrv, X_OK) == 0);
int seq = 0;
for (int i = 0; i < n; i++) {
total++;
int got = run_row(cdrv, cwd, &rows[i], seq++);
if (got != rows[i].want_exit) {
fprintf(stderr, "fmt_int_run[cs][%s]: exit=%d want=%d\n",
rows[i].label, got, rows[i].want_exit);
fail++;
}
if (wwpresent) {
total++;
int gw = run_row(wdrv, cwd, &rows[i], seq++);
if (gw != rows[i].want_exit) {
fprintf(stderr, "fmt_int_run[ww][%s]: exit=%d want=%d\n",
rows[i].label, gw, rows[i].want_exit);
fail++;
}
}
}
if (wwpresent) {
for (int i = 0; i < n; i++) {
total++;
if (asm_byte_identical(cdrv, wdrv, cwd, &rows[i], seq++) != 0)
fail++;
}
} else {
fprintf(stderr, "fmt_int_run: skip wwstage (no %s)\n", wdrv);
}
if (fail) {
fprintf(stderr, "fmt_int_run: %d/%d fixtures failed\n", fail, total);
return 1;
}
printf("fmt_int_run: %d/%d ok\n", total, total);
return 0;
if (access(wdrv, X_OK) != 0) { fprintf(stderr, "fmt_int_run: skip wwstage\n"); return 0; }
int n = (int)(sizeof rows / sizeof rows[0]), fail = 0;
for (int i = 0; i < n; i++) if (byteid(cdrv, wdrv, cwd, &rows[i], i) != 0) fail++;
if (fail) { fprintf(stderr, "fmt_int_run: %d/%d byte rows failed\n", fail, n); return 1; }
printf("fmt_int_run: %d/%d byte rows ok\n", n, n); return 0;
}

View File

@@ -1,57 +1,13 @@
/*
* 826_alias_tuple_coerce — an alias of a tuple type, initialised with an
* UNTYPED tuple literal, must coerce element-wise just like a direct tuple
* (task #99). `type pair = (int, int); let x: pair = (3, 4)` was rejected by
* cstage — `init (untyped_int, untyped_int) not assignable to declared pair`
* — while the direct `let x: (int,int) = (3,4)` and the typed-alias
* `let x: pair = (3:int, 4:int)` both compiled. wwstage already accepted+ran
* the untyped-alias form correctly.
*
* ROOT (cstage only): cmd/wcc/type.c type_assignable's tuple-to-tuple arm
* gated on the UN-chased `dst->kind == TY_TUPLE`. For `pair` (a TY_NAMED
* alias) dst->kind is TY_NAMED, so the arm was skipped and the per-element
* untyped->int coercion never ran. The fix chases TY_NAMED on both sides
* before the tuple check (mirroring the #258 slice-borrow arm just below),
* so an alias-of-tuple gets the same element-wise coercion as a direct tuple.
* type_assignable is the shared assignability predicate, so INIT, FN-ARG and
* RETURN positions all flip reject->accept in one spot. cstage-only —
* wwstage check.ww/cgen are unchanged.
*
* The coercion still type-checks per element, so a genuinely mismatched
* element type or wrong arity STILL louds — those are the negative controls.
*
* row | shape | want
* ------------+------------------------------------------------+------
* init | type pair=(int,int); let x:pair=(3,4); x.0+x.1 | 7
* fnarg | fn g(p:pair); g((3,4)); p.0+p.1 | 7
* ret | fn f() pair = { return (3,4); }; r.0+r.1 | 7
* mixed | type box=(int,u16); let x:box=(3,4); coerce ea | 7
* ctrl_dir | direct (int,int)=(3,4) (no-regress) | 7
* ctrl_typed | alias (3:int,4:int) (no-regress) | 7
*
* init / fnarg / ret / mixed are the mutation-sane rows: pre-fix cstage
* build-FAILS them (the untyped->alias coercion was rejected); pre-fix
* wwstage built but a two-field alias-tuple fn-arg read garbage (the param
* spill half). Post-fix BOTH stages build+run the want.
*
* Negative controls (cstage must STILL loud — don't over-accept):
* neg_type | let x:pair=(3,"s") (element type mismatch)
* neg_arity | let x:pair=(3,4,5) (wrong arity)
*
* byte-id: the init, fnarg and ret rows are asserted byte-identical
* (cstage==wwstage .s). #99 shipped in two halves — the cstage type.c chase
* (init/fnarg/ret reject->accept) AND the wwstage cgendecl.ww tuple-PARAM
* spill chase (the param twin: an alias param gated on the SYNTACTIC
* N_TTUPLE node fell through to the scalar path, spilling one arg reg and
* dropping the second tuple word, so a two-field read of an alias-tuple
* fn-arg returned garbage in wwstage). With the wwstage half landed the
* fnarg/ret prologues converge to cstage, so both read both fields and
* match byte-for-byte. mixed (an init row) stays runtime-only.
* 826_alias_tuple_coerce — residual asymmetric and assembly byte checks.
* Symmetric runtime ownership is the r826_* compiler fixture set; the three
* byte rows compile those exact files directly. Two negative controls remain
* embedded because cstage must reject them while wwstage still over-accepts
* them, so the symmetric fixture grammar cannot own that assertion.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
static int
@@ -63,90 +19,15 @@ runwait(const char *cmd)
return -1;
}
struct row { const char *label; const char *src; int want; int byteid; };
struct row { const char *label; const char *path; };
static const struct row rows[] = {
/* init — THE construct: untyped tuple literal -> alias, read both
* fields. byte-id clean across stages. */
{ "init",
"package main;\n"
"type pair = (int, int);\n"
"export fn main() i32 = {\n"
"\tlet x: pair = (3, 4);\n"
"\treturn (x.0 + x.1): i32;\n"
"};\n",
7, 1 },
/* fnarg — untyped tuple literal coerced into an alias-typed param at
* the call site, reading BOTH fields. The wwstage tuple-PARAM spill
* chase (cgendecl.ww, #99 second half) sizes the alias param slot 16B
* and spills both arg regs, so p.0+p.1 is correct and byte-identical
* to cstage. */
{ "fnarg",
"package main;\n"
"type pair = (int, int);\n"
"fn g(p: pair) i32 = {\n"
"\treturn (p.0 + p.1): i32;\n"
"};\n"
"export fn main() i32 = {\n"
"\treturn g((3, 4));\n"
"};\n",
7, 1 },
/* ret — untyped tuple literal coerced into the declared alias return
* type; the receiving alias-tuple let reads both fields, byte-id. */
{ "ret",
"package main;\n"
"type pair = (int, int);\n"
"fn f() pair = {\n"
"\treturn (3, 4);\n"
"};\n"
"export fn main() i32 = {\n"
"\tlet r: pair = f();\n"
"\treturn (r.0 + r.1): i32;\n"
"};\n",
7, 1 },
/* mixed — heterogeneous element types under the alias; each untyped
* element coerces to its declared field type. runtime-only. */
{ "mixed",
"package main;\n"
"type box = (int, u16);\n"
"export fn main() i32 = {\n"
"\tlet x: box = (3, 4);\n"
"\treturn (x.0 + (x.1: int)): i32;\n"
"};\n",
7, 0 },
/* ctrl_dir — direct (un-aliased) tuple init; already worked, guards
* against a regression in the original tuple path. */
{ "ctrl_dir",
"package main;\n"
"export fn main() i32 = {\n"
"\tlet x: (int, int) = (3, 4);\n"
"\treturn (x.0 + x.1): i32;\n"
"};\n",
7, 0 },
/* ctrl_typed — alias init with already-typed elements (no untyped
* coercion); already worked via the named-arm type_eq. */
{ "ctrl_typed",
"package main;\n"
"type pair = (int, int);\n"
"export fn main() i32 = {\n"
"\tlet x: pair = (3: int, 4: int);\n"
"\treturn (x.0 + x.1): i32;\n"
"};\n",
7, 0 },
{ "init", "test/wcc/data/r826_alias_tuple_init/case.ww" },
{ "fnarg", "test/wcc/data/r826_alias_tuple_fnarg/case.ww" },
{ "ret", "test/wcc/data/r826_alias_tuple_ret/case.ww" },
};
/* neg — cstage must STILL reject these (the coercion type-checks per
* element; a type mismatch or wrong arity is not assignable). cstage-only:
* wwstage pre-existingly over-accepts a mismatched tuple init (a separate
* wwstage bug, out of #99 scope), so the negative is asserted against the
* cstage driver only — the side #99 touches. */
static const char *neg[] = {
/* neg_type — second element str vs declared int. */
"package main;\n"
"type pair = (int, int);\n"
"export fn main() i32 = {\n"
@@ -154,7 +35,6 @@ static const char *neg[] = {
"\treturn x.0: i32;\n"
"};\n",
/* neg_arity — three elements vs the two-element alias. */
"package main;\n"
"type pair = (int, int);\n"
"export fn main() i32 = {\n"
@@ -164,93 +44,47 @@ static const char *neg[] = {
};
static int
run_driver(const char *driver, const struct row *r, int i)
compile_should_fail(const char *compiler, const char *src, int i)
{
char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/atc_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/atc_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/atc_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
runwait(rmcmd);
return -1;
}
int got = runwait(outbin);
runwait(rmcmd);
return got;
}
/* build_should_fail — the negative control must error on `driver`; returns
* 0 when the build correctly FAILS, non-zero when it wrongly succeeded. */
static int
build_should_fail(const char *driver, const char *src, int i)
{
char tmpdir[64], s[128], outbin[128], rmcmd[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/atc_neg_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(s, sizeof s, "%s/atc_neg_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/atc_neg_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
char s[128], outasm[128], cmd[1024];
snprintf(s, sizeof s, "/tmp/atc_neg_%d_%d.ww", getpid(), i);
snprintf(outasm, sizeof outasm, "/tmp/atc_neg_%d_%d.s", getpid(), i);
FILE *f = fopen(s, "wb");
if (!f) { runwait(rmcmd); return -1; }
if (!f) return -1;
fputs(src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, s);
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
compiler, outasm, s);
int rc = runwait(cmd);
runwait(rmcmd);
return rc == 0 ? -1 : 0; /* build must NOT succeed */
unlink(s);
unlink(outasm);
return rc == 0 ? -1 : 0;
}
/* asm_byte_identical — w6c vs w6c_ww .s for the same source must match. */
static int
asm_byte_identical(const char *bin, const struct row *r, int i)
{
char src[64], cs[64], ws[64], cmd[1024];
snprintf(src, sizeof src, "/tmp/atc_asm_%d_%d.ww", getpid(), i);
snprintf(cs, sizeof cs, "/tmp/atc_asm_%d_%d_c.s", getpid(), i);
snprintf(ws, sizeof ws, "/tmp/atc_asm_%d_%d_w.s", getpid(), i);
FILE *f = fopen(src, "wb");
if (!f) return -1;
fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
char cs[64], ws[64], cmd[1024];
snprintf(cs, sizeof cs, "/tmp/atc_asm_%d_%d_c.s", getpid(), i);
snprintf(ws, sizeof ws, "/tmp/atc_asm_%d_%d_w.s", getpid(), i);
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null",
bin, cs, r->path);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
unlink(src);
return -1;
}
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
bin, ws, src);
bin, ws, r->path);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
unlink(src); unlink(cs);
unlink(cs);
return -1;
}
FILE *fc = fopen(cs, "rb");
FILE *fw = fopen(ws, "rb");
int rc = 0;
if (!fc || !fw) {
rc = -1;
} else {
if (!fc || !fw) rc = -1;
else {
for (;;) {
int a = fgetc(fc);
int b = fgetc(fw);
@@ -263,7 +97,8 @@ asm_byte_identical(const char *bin, const struct row *r, int i)
if (rc != 0)
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
r->label);
unlink(src); unlink(cs); unlink(ws);
unlink(cs);
unlink(ws);
return rc;
}
@@ -279,71 +114,33 @@ main(void)
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
char cdrv[1024];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
char wdrv[1024];
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
struct { const char *name; const char *path; int gated_on_existence; }
drivers[] = {
{ "cstage", cdrv, 0 },
{ "wwstage", wdrv, 1 },
{ NULL, NULL, 0 },
};
char cdrv[1024], wdrv[1024];
snprintf(cdrv, sizeof cdrv, "%s/w6c", bin);
snprintf(wdrv, sizeof wdrv, "%s/w6c_ww", bin);
int n = (int)(sizeof rows / sizeof rows[0]);
int nneg = (int)(sizeof neg / sizeof neg[0]);
int total = 0, fail = 0;
for (int d = 0; drivers[d].name; d++) {
if (drivers[d].gated_on_existence
&& access(drivers[d].path, X_OK) != 0) {
fprintf(stderr, "alias_tuple_coerce: skip %s (no %s)\n",
drivers[d].name, drivers[d].path);
continue;
}
for (int i = 0; i < n; i++) {
int got = run_driver(drivers[d].path, &rows[i], i);
total++;
if (got != rows[i].want) {
fprintf(stderr,
"alias_tuple_coerce[%s][%s]: exit=%d want=%d\n",
drivers[d].name, rows[i].label,
got, rows[i].want);
fail++;
}
}
}
/* Negative controls — cstage must STILL reject (don't over-accept).
* cstage-only: the #99 fix is cstage-side, and wwstage pre-existingly
* over-accepts a mismatched tuple init (out-of-scope wwstage bug). */
for (int i = 0; i < nneg; i++) {
total++;
if (build_should_fail(cdrv, neg[i], i) != 0) {
if (compile_should_fail(cdrv, neg[i], i) != 0) {
fprintf(stderr,
"alias_tuple_coerce[cstage][neg_%d]: built (should loud)\n",
i);
fail++;
}
}
/* byte-id — only rows flagged byteid (the INIT form). */
if (access(wdrv, X_OK) == 0) {
for (int i = 0; i < n; i++) {
if (!rows[i].byteid) continue;
total++;
if (asm_byte_identical(bin, &rows[i], i) != 0)
fail++;
if (asm_byte_identical(bin, &rows[i], i) != 0) fail++;
}
}
if (fail) {
fprintf(stderr,
"alias_tuple_coerce: %d/%d fixtures failed\n", fail, total);
"alias_tuple_coerce: %d/%d residual checks failed\n",
fail, total);
return 1;
}
printf("alias_tuple_coerce: %d/%d ok\n", total, total);
printf("alias_tuple_coerce: %d/%d residual checks ok\n", total, total);
return 0;
}

View File

@@ -53,6 +53,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -194,8 +195,13 @@ run_one(const struct driver *drv, const struct scenario *sc)
drv->name, sc->label, sc->files[i].name);
rc = -1; goto done;
}
fputs(sc->files[i].src, f);
fclose(f);
int writebad = fputs(sc->files[i].src, f) == EOF || ferror(f);
if (fclose(f) != 0) writebad = 1;
if (writebad) {
fprintf(stderr, "839[%s][%s]: write %s\n",
drv->name, sc->label, sc->files[i].name);
rc = -1; goto done;
}
}
snprintf(cmd, sizeof cmd, "cd %s && %s build -I %s %s/main.ww "
@@ -226,8 +232,25 @@ run_one(const struct driver *drv, const struct scenario *sc)
}
done:
snprintf(cmd, sizeof cmd, "rm -rf %s", dir);
(void)runwait(cmd);
{
int cleanfail = 0;
for (int i = 0; sc->files[i].name; i++) {
snprintf(path, sizeof path, "%s/%s", dir,
sc->files[i].name);
if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1;
}
snprintf(path, sizeof path, "%s/main", dir);
if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1;
snprintf(path, sizeof path, "%s/main.sepwork", dir);
snprintf(cmd, sizeof cmd, "rm -rf %s", path);
if (runwait(cmd) != 0) cleanfail = 1;
if (rmdir(dir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "839[%s][%s]: workspace cleanup failed\n",
drv->name, sc->label);
if (rc == 0) rc = -1;
}
}
return rc;
}

View File

@@ -1,51 +1,12 @@
/*
* 844_size_untyped_int — the size/align/offset builtins fold to an
* untyped_int constant and RETURN untyped_int to the caller (the
* Hare-correct type), so `let x: size = size(T)` is assignable
* (catB-9; 2026-06-14).
*
* The fold stamps the node `e.type_ = untyped_int` in BOTH stages
* (correct), but pre-fix wwstage's check.ww RETURNED `i32` to the
* caller while cstage returned `untyped_int` (check.c:1570/1602).
* untyped_int is assignable to any integer type (size, u64, ...);
* i32 is NOT assignable to `size`. So `let lenbytes: size =
* size(u64);` — the canonical Hare idiom (sha256.ww:189) — was
* FALSE-REJECTED by wwstage (`let: not assignable (i32 -> size)`)
* while cstage accepted. The fix aligns wwstage UP: check.ww:3099/
* 3108/3125 now return mktname("untyped_int"). cstage is unchanged.
*
* DISCRIMINATION (verified against the pre-fix master out/bin/ww_ww):
* p1 built rc=1 with `let: not assignable (i32 -> size)` on wwstage
* and rc=0 on cstage; post-fix both accept. (A live discrimination
* row can't run here — the post-fix binary accepts p1 — so the
* pre-fix reject is pinned in this comment, not as a runtime row.)
*
* row | shape | want
* --------+----------------------------------------+-----
* p1 | let lenbytes: size = size(u64); | 8
* p2 | let x: u64 = size(int); | 8
* p3 | let pad: size = size(u64) - n; (n=1) | 7
* p7_ctrl | let x = size(u64); (UNANNOTATED) | 8
*
* p7_ctrl is drew's de-risk control: an unannotated `let` defaults
* the untyped_int to int and behaves IDENTICALLY in both stages,
* pre AND post fix (a both-ACCEPT, run 8). It proves the return-type
* flip did not perturb the default-int path.
*
* BYTE-ID (rule 10): the bid program mixes a `size`-typed local with
* `size(u64)` in a comparison — the context where the fold's new
* untyped_int could converge a type and shift CMPL->CMPQ width. Post-
* fix cstage's w6c and wwstage's w6c_ww emit byte-identical asm (both
* CMPQ); the row diffs the two .s files.
*
* Table-driven, BOTH stages (cstage `ww`, wwstage `ww_ww` gated on
* existence), mirrors 682_arr_enum_elem / 842_dup_main_reject.
* Intermediates land in tmpdirs, never the source tree (rule 14).
* Runtime rows moved to wwfixtures. This residual retains the distinct
* rule-10 probe: a size-typed comparison must emit byte-identical assembly
* through the C and WW frontends.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
static int
@@ -57,41 +18,6 @@ runwait(const char *cmd)
return -1;
}
struct row { const char *label; const char *src; int want; };
static const struct row rows[] = {
{ "p1_let_size",
"package main;\n"
"export fn main() i32 = {\n"
" let lenbytes: size = size(u64);\n"
" return lenbytes: i32;\n"
"};\n", 8 },
{ "p2_let_u64",
"package main;\n"
"export fn main() i32 = {\n"
" let x: u64 = size(int);\n"
" return x: i32;\n"
"};\n", 8 },
{ "p3_size_arith",
"package main;\n"
"export fn main() i32 = {\n"
" let n: size = 1;\n"
" let pad: size = size(u64) - n;\n"
" return pad: i32;\n"
"};\n", 7 },
{ "p7_ctrl_unannotated",
"package main;\n"
"export fn main() i32 = {\n"
" let x = size(u64);\n"
" return x: i32;\n"
"};\n", 8 },
};
/* the byte-id program: a size()-mixing comparison (the CMPL->CMPQ
* width context). Both stages must emit identical asm. */
static const char *bid_src =
"package main;\n"
"export fn main() i32 = {\n"
@@ -101,42 +27,12 @@ static const char *bid_src =
"};\n";
static int
run_driver(const char *driver, const struct row *r, int i)
{
char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/szui_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/szui_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/szui_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
runwait(rmcmd);
return -1;
}
int got = runwait(outbin);
runwait(rmcmd);
return got;
}
static int
asm_byte_identical(const char *bin, int i)
asm_byte_identical(const char *bin)
{
char src[64], cs[64], ws[64], cmd[1024];
snprintf(src, sizeof src, "/tmp/szui_asm_%d_%d.ww", getpid(), i);
snprintf(cs, sizeof cs, "/tmp/szui_asm_%d_%d_c.s", getpid(), i);
snprintf(ws, sizeof ws, "/tmp/szui_asm_%d_%d_w.s", getpid(), i);
snprintf(src, sizeof src, "/tmp/szui_asm_%d.ww", getpid());
snprintf(cs, sizeof cs, "/tmp/szui_asm_%d_c.s", getpid());
snprintf(ws, sizeof ws, "/tmp/szui_asm_%d_w.s", getpid());
FILE *f = fopen(src, "wb");
if (!f) return -1;
@@ -146,14 +42,18 @@ asm_byte_identical(const char *bin, int i)
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "bid: w6c errored\n");
unlink(src);
/* the failed leg may still have written a partial .s. */
if (unlink(src) != 0 && errno != ENOENT) perror(src);
if (unlink(cs) != 0 && errno != ENOENT) perror(cs);
return -1;
}
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
bin, ws, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "bid: w6c_ww errored\n");
unlink(src); unlink(cs);
if (unlink(src) != 0 && errno != ENOENT) perror(src);
if (unlink(cs) != 0 && errno != ENOENT) perror(cs);
if (unlink(ws) != 0 && errno != ENOENT) perror(ws);
return -1;
}
@@ -174,7 +74,10 @@ asm_byte_identical(const char *bin, int i)
if (fw) fclose(fw);
if (rc != 0)
fprintf(stderr, "bid: cstage vs wwstage asm differs\n");
unlink(src); unlink(cs); unlink(ws);
/* a silent cleanup failure must fail an otherwise-passing probe. */
if (unlink(src) != 0 && errno != ENOENT) { perror(src); rc = -1; }
if (unlink(cs) != 0 && errno != ENOENT) { perror(cs); rc = -1; }
if (unlink(ws) != 0 && errno != ENOENT) { perror(ws); rc = -1; }
return rc;
}
@@ -191,47 +94,19 @@ main(void)
bin = absbin;
}
char cdrv[1024], wdrv[1024];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
struct { const char *name; const char *path; int gated; } drivers[] = {
{ "cstage", cdrv, 0 },
{ "wwstage", wdrv, 1 },
{ NULL, NULL, 0 },
};
int n = (int)(sizeof rows / sizeof rows[0]);
char wdrv[1024];
/* gate on the binary the probe actually invokes (w6c_ww), not the
* ww_ww driver — a driver-only or frontend-only build would
* otherwise skip real coverage or fail vacuously. */
snprintf(wdrv, sizeof wdrv, "%s/w6c_ww", bin);
int total = 0, fail = 0;
for (int d = 0; drivers[d].name; d++) {
if (drivers[d].gated && access(drivers[d].path, X_OK) != 0) {
fprintf(stderr, "size_untyped_int: skip %s (no %s)\n",
drivers[d].name, drivers[d].path);
continue;
}
for (int i = 0; i < n; i++) {
total++;
int got = run_driver(drivers[d].path, &rows[i], i);
if (got != rows[i].want) {
fprintf(stderr,
"size_untyped_int[%s][%s]: exit=%d want=%d\n",
drivers[d].name, rows[i].label,
got, rows[i].want);
fail++;
}
}
}
if (access(wdrv, X_OK) == 0) {
total++;
if (asm_byte_identical(bin, 0) != 0)
fail++;
if (asm_byte_identical(bin) != 0) fail++;
}
if (fail) {
fprintf(stderr,
"size_untyped_int: %d/%d fixtures failed\n", fail, total);
fprintf(stderr, "size_untyped_int: %d/%d fixtures failed\n",
fail, total);
return 1;
}
printf("size_untyped_int: %d/%d ok\n", total, total);

View File

@@ -59,6 +59,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -143,8 +144,12 @@ run_ast_proof(const char *wwdump, const char *wwdump_ww,
FILE *f = fopen(src, "wb");
if (!f) { fprintf(stderr, "848-ast[%s]: write\n", ac->label);
rc = -1; goto done; }
fputs(ac->src, f);
fclose(f);
int writebad = fputs(ac->src, f) == EOF || ferror(f);
if (fclose(f) != 0) writebad = 1;
if (writebad) {
fprintf(stderr, "848-ast[%s]: write\n", ac->label);
rc = -1; goto done;
}
snprintf(cs, sizeof cs, "%s/cs.ast", dir);
snprintf(ww, sizeof ww, "%s/ww.ast", dir);
@@ -176,8 +181,21 @@ run_ast_proof(const char *wwdump, const char *wwdump_ww,
rc = -1;
}
done:
snprintf(cmd, sizeof cmd, "rm -rf %s", dir);
(void)runwait(cmd);
{
int cleanfail = 0;
snprintf(src, sizeof src, "%s/a.ww", dir);
if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1;
snprintf(cs, sizeof cs, "%s/cs.ast", dir);
if (unlink(cs) != 0 && errno != ENOENT) cleanfail = 1;
snprintf(ww, sizeof ww, "%s/ww.ast", dir);
if (unlink(ww) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(dir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "848-ast[%s]: workspace cleanup failed\n",
ac->label);
if (rc == 0) rc = -1;
}
}
return rc;
}
@@ -308,47 +326,39 @@ run_scenario(const char *bin, const char *cdrv, const char *wdrv,
FILE *f = fopen(path, "wb");
if (!f) { fprintf(stderr, "848[%s]: write %s\n", sc->label,
sc->files[i].name); rc = -1; goto done; }
fputs(sc->files[i].src, f);
fclose(f);
int writebad = fputs(sc->files[i].src, f) == EOF || ferror(f);
if (fclose(f) != 0) writebad = 1;
if (writebad) {
fprintf(stderr, "848[%s]: write %s\n", sc->label,
sc->files[i].name);
rc = -1; goto done;
}
}
/* #94 sep layout: cstage --sep build + run pins parse + runtime field
* values; pin WW_PKGCACHE under the scratch dir. */
/* Runtime values moved to wwfixtures. These builds intentionally keep
* their caller-owned compiler artifacts for the byte assertion below. */
snprintf(cmd, sizeof cmd,
"cd %s && WW_PKGCACHE=%s/pkgc_c %s build --sep -I %s -o %s/main %s/main.ww",
dir, dir, cdrv, dir, dir, dir);
"cd %s && %s build -I %s -o %s/main %s/main.ww",
dir, cdrv, dir, dir, dir);
if (runwait(cmd) != 0) {
fprintf(stderr, "848[%s]: cstage build failed "
"(qualified struct literal parse reject?)\n", sc->label);
rc = -1; goto done;
}
snprintf(path, sizeof path, "%s/main", dir);
int got = runwait(path);
if (got != sc->want_exit) {
fprintf(stderr, "848[%s]: cstage exit %d, want %d\n",
sc->label, got, sc->want_exit);
rc = -1;
}
/* rule-10 discriminator: the wwstage driver's --sep build emits
* per-package w6c_ww asm that must be byte-identical to cstage's. The
* root + each imported pkg compile to separate <stem>.sepwork/<pkg>.s;
* concat (sorted glob, identical set both stages) for the compare. */
/* rule-10 discriminator: this colocated two-file fixture composes into
* the exact retained __root.s artifact, which must be byte-identical
* across driver stages. */
char cs_s[1024], ws_s[1024];
snprintf(cmd, sizeof cmd,
"cd %s && WW_PKGCACHE=%s/pkgc_w %s build --sep -I %s -o %s/mainww %s/main.ww",
dir, dir, wdrv, dir, dir, dir);
"cd %s && %s build -I %s -o %s/mainww %s/main.ww",
dir, wdrv, dir, dir, dir);
if (runwait(cmd) != 0) {
fprintf(stderr, "848[%s]: ww_ww build failed "
"(qualified struct literal parse reject?)\n", sc->label);
rc = -1; goto done;
}
snprintf(cs_s, sizeof cs_s, "%s/all_cs.s", dir);
snprintf(ws_s, sizeof ws_s, "%s/all_ww.s", dir);
snprintf(cmd, sizeof cmd, "cat %s/main.sepwork/*.s > %s 2>/dev/null",
dir, cs_s); if (system(cmd)) {}
snprintf(cmd, sizeof cmd, "cat %s/mainww.sepwork/*.s > %s 2>/dev/null",
dir, ws_s); if (system(cmd)) {}
snprintf(cs_s, sizeof cs_s, "%s/main.sepwork/__root.s", dir);
snprintf(ws_s, sizeof ws_s, "%s/mainww.sepwork/__root.s", dir);
if (slurp_eq(cs_s, ws_s) != 0) {
fprintf(stderr, "848[%s]: cs.s/ww.s DIFFER (rule-10 byte-id "
"violation)\n", sc->label);
@@ -356,8 +366,31 @@ run_scenario(const char *bin, const char *cdrv, const char *wdrv,
}
done:
snprintf(cmd, sizeof cmd, "rm -rf %s", dir);
(void)runwait(cmd);
{
int cleanfail = 0;
for (int i = 0; sc->files[i].name; i++) {
snprintf(path, sizeof path, "%s/%s", dir,
sc->files[i].name);
if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1;
}
const char *files[] = { "main", "mainww", NULL };
for (int i = 0; files[i]; i++) {
snprintf(path, sizeof path, "%s/%s", dir, files[i]);
if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1;
}
snprintf(path, sizeof path, "%s/main.sepwork", dir);
snprintf(cmd, sizeof cmd, "rm -rf %s", path);
if (runwait(cmd) != 0) cleanfail = 1;
snprintf(path, sizeof path, "%s/mainww.sepwork", dir);
snprintf(cmd, sizeof cmd, "rm -rf %s", path);
if (runwait(cmd) != 0) cleanfail = 1;
if (rmdir(dir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "848[%s]: workspace cleanup failed\n",
sc->label);
if (rc == 0) rc = -1;
}
}
return rc;
}

View File

@@ -22,7 +22,7 @@
*
* #90 sep-feed: the E3 flip retired the combined.ww amalgamator, so the
* library/selfhost gap fixtures (A-D) feed their RESOLVED sep unit (see
* resolveunit — `ww build --sep` composes <stem>.sepwork/__root.unit.ww
* resolveunit — `ww build -S` composes <stem>.sepwork/__root.unit.ww
* from the dep `.wwi` stubs + the root body) rather than a pre-built
* <stem>.combined.ww. The single-file, import-free test fixtures (E-G)
* carry no imports and stay raw-fed.
@@ -45,6 +45,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -91,47 +92,25 @@ slurp(const char *path, char **outbuf, size_t *outlen)
}
/* resolveunit — compose <fixture>'s RESOLVED translation unit the way the
* sep driver does. Mirrors 990_selfhost's resolveunit: copy the fixture's
* whole package directory into a private /tmp dir (so a white-box `_test`
* file's sibling package sources resolve, and the sepwork lands off the repo
* tree), `ww build --sep <base>`, then read <stem>.sepwork/__root.unit.ww —
* sep driver does. `ww build -S -o <owned-stem>` reads the fixture and its
* sibling package sources in place while putting caller-owned compiler
* artifacts below the private workspace, then this probe reads the exact
* <stem>.sepwork/__root.unit.ww artifact
* the single self-contained unit w6c/wwdump consume for the root. Feeding the
* raw module file instead would leave its import refs (os/fmt/strconv …)
* unresolved, a partial unit the asserttyped invariant must never see. The
* link step fails for an import-only module (no main), but the unit is
* written before the link, so we detect the artifact rather than gating on
* the build exit. Writes the owning tmpdir into `td` (caller rm -rf's it) and
* the unit path into `out`. Returns 0 on success, -1 otherwise. */
* unresolved, a partial unit the asserttyped invariant must never see.
* Returns the unit path in `out`, 0 on success, and -1 otherwise. */
static int
resolveunit(const char *bin, const char *cwd, const char *fixture,
int idx, char *td, size_t tdsz, char *out, size_t outsz)
const char *work, char *out, size_t outsz)
{
char cmd[2048];
snprintf(td, tdsz, "/tmp/atgap_ru_%d_%d", getpid(), idx);
snprintf(cmd, sizeof cmd, "rm -rf %s", td);
runwait(cmd);
mkdir(td, 0755);
const char *slash = strrchr(fixture, '/');
const char *base = slash ? slash + 1 : fixture;
char dir[1024];
if (slash) {
size_t dl = (size_t)(slash - fixture);
if (dl >= sizeof dir) return -1;
memcpy(dir, fixture, dl);
dir[dl] = '\0';
} else {
dir[0] = '.'; dir[1] = '\0';
}
snprintf(cmd, sizeof cmd, "cp %s/%s/*.ww %s/ 2>/dev/null", cwd, dir, td);
runwait(cmd);
char cmd[4096], src[2048], stem[2048];
snprintf(src, sizeof src, "%s/%s", cwd, fixture);
snprintf(stem, sizeof stem, "%s/unit", work);
snprintf(cmd, sizeof cmd,
"cd %s && timeout 180 %s/ww build --sep %s >/dev/null 2>&1",
td, bin, base);
runwait(cmd);
char stem[1024];
snprintf(stem, sizeof stem, "%s/%s", td, base);
char *dot = strrchr(stem, '.');
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
"timeout 180 %s/ww build -S -o %s %s >/dev/null 2>&1",
bin, stem, src);
if (runwait(cmd) != 0) return -1;
snprintf(out, outsz, "%s.sepwork/__root.unit.ww", stem);
return access(out, 0) == 0 ? 0 : -1;
}
@@ -187,30 +166,39 @@ main(void)
int fail = 0, n = 0;
for (int i = 0; manifest[i].rel; i++) {
char src[2048], errf[64], cmd[4096];
char td[64] = {0}, unit[1280];
char work[] = "/tmp/atgap_XXXXXX";
char src[2048], errf[256], cmd[4096], unit[1280];
char stem[256], scratch[288];
int rowfail = 0;
n++;
if (mkdtemp(work) == NULL) {
fprintf(stderr, "asserttyped_gap FAIL: %s [%s] "
"workspace acquisition failed\n",
manifest[i].rel, manifest[i].cls);
fail++;
continue;
}
snprintf(stem, sizeof stem, "%s/unit", work);
snprintf(scratch, sizeof scratch, "%s.sepwork", stem);
snprintf(errf, sizeof errf, "%s/asserttyped.err", work);
if (manifest[i].sep) {
if (resolveunit(bin, cwd, manifest[i].rel, i, td,
sizeof td, unit, sizeof unit) != 0) {
if (resolveunit(bin, cwd, manifest[i].rel, work, unit,
sizeof unit) != 0) {
fprintf(stderr, "asserttyped_gap FAIL: %s [%s] "
"no resolved sep unit\n",
manifest[i].rel, manifest[i].cls);
if (td[0]) { snprintf(cmd, sizeof cmd,
"rm -rf %s", td); runwait(cmd); }
fail++; n++;
continue;
rowfail = 1;
goto row_done;
}
snprintf(src, sizeof src, "%s", unit);
} else {
snprintf(src, sizeof src, "%s/%s", cwd, manifest[i].rel);
}
snprintf(errf, sizeof errf, "/tmp/atgap_%d_%d.err", getpid(), i);
snprintf(cmd, sizeof cmd,
"timeout 180 %s/wwdump_ww -c %s >/dev/null 2>%s",
bin, src, errf);
runwait(cmd);
int got = count_asserttyped(errf);
n++;
if (got != manifest[i].want) {
fprintf(stderr,
"asserttyped_gap FAIL: %s [%s] expected %d, got %d\n",
@@ -220,11 +208,25 @@ main(void)
snprintf(dump, sizeof dump,
"grep '^asserttyped:' %s 1>&2", errf);
runwait(dump);
fail++;
rowfail = 1;
}
unlink(errf);
if (td[0]) { snprintf(cmd, sizeof cmd, "rm -rf %s", td);
runwait(cmd); }
row_done:
{
int cleanfail = 0;
if (unlink(errf) != 0 && errno != ENOENT) cleanfail = 1;
snprintf(cmd, sizeof cmd, "rm -rf %s", scratch);
if (runwait(cmd) != 0) cleanfail = 1;
if (unlink(stem) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(work) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "asserttyped_gap FAIL: %s [%s] "
"workspace cleanup failed\n",
manifest[i].rel, manifest[i].cls);
rowfail = 1;
}
}
if (rowfail) fail++;
}
if (fail) {

View File

@@ -8,7 +8,7 @@
* assert-fail/SIGSEGV/SIGFPE, then a final pass). The runner must
* proceed past every failure, emit the right per-test `name ... ok/
* FAIL` line (table-driven below), print the right `P passed, F
* failed` summary, and exit with the failure count (nonzero).
* failed` summary, and clamp any failure set to exit 1.
* 2. ALL-PASS — `ww test` the all-pass fixture exits 0 (no false
* failure from the new runner).
* 3. BYTE-ID (rule 10) — `ww test -c` the mixed fixture into a
@@ -21,6 +21,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
@@ -64,41 +65,57 @@ static const char *expect_lines[] = {
"rec_segv ... FAIL",
"rec_div0 ... FAIL",
"rec_pass_b ... ok",
"2 passed, 3 failed",
"2 passed, 3 failed, 0 skipped, 0 harness errors",
"5 discovered, 5 selected, 5 started, 5 completed",
};
static int
record_continue(const char *bin)
{
int pid = getpid();
char dir[] = "/tmp/at911_record_XXXXXX";
char out[256], cmd[4096], buf[8192];
snprintf(out, sizeof out, "/tmp/at911_%d.out", pid);
int result = 1;
if (mkdtemp(dir) == NULL) {
fprintf(stderr, "911 FAIL: record workspace acquisition failed\n");
return 1;
}
snprintf(out, sizeof out, "%s/output", dir);
/* `ww test` builds the fixture (auto-bundling lib/test) and runs it;
* the exit code is the runner's failure count. */
* three failures remain visible in output while the public exit clamps. */
snprintf(cmd, sizeof cmd,
"%s/ww test test/wcc/data/attest_record.ww > %s 2>/dev/null",
bin, out);
int rc = runwait(cmd);
if (rc != 3) {
if (rc != 1) {
fprintf(stderr, "911 FAIL: record-and-continue exit %d "
"(expected 3 failures)\n", rc);
return 1;
"(expected canonical failure exit 1)\n", rc);
goto done;
}
if (slurp(out, buf, sizeof buf) != 0) {
fprintf(stderr, "911 FAIL: cannot read runner output\n");
return 1;
goto done;
}
for (size_t i = 0; i < sizeof expect_lines / sizeof expect_lines[0]; i++) {
if (strstr(buf, expect_lines[i]) == NULL) {
fprintf(stderr, "911 FAIL: missing runner line '%s'\n"
"--- got ---\n%s\n", expect_lines[i], buf);
unlink(out);
return 1;
goto done;
}
}
unlink(out);
return 0;
result = 0;
done:
{
int cleanfail = 0;
if (unlink(out) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(dir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "911 FAIL: record workspace cleanup failed\n");
result = 1;
}
}
return result;
}
static int
@@ -119,39 +136,53 @@ all_pass(const char *bin)
static int
byteid(const char *bin)
{
int pid = getpid();
char dir[] = "/tmp/at911_bid_XXXXXX";
char stemc[256], stemw[256], cs[256], ws[256], cmd[4096];
snprintf(stemc, sizeof stemc, "/tmp/at911_bid_c_%d", pid);
snprintf(stemw, sizeof stemw, "/tmp/at911_bid_w_%d", pid);
snprintf(cs, sizeof cs, "/tmp/at911_bid_cs_%d.s", pid);
snprintf(ws, sizeof ws, "/tmp/at911_bid_ws_%d.s", pid);
char scratchc[288], scratchw[288];
int rc = 1;
if (mkdtemp(dir) == NULL) {
fprintf(stderr, "911 FAIL: byteid workspace acquisition failed\n");
return 1;
}
snprintf(stemc, sizeof stemc, "%s/cstage", dir);
snprintf(stemw, sizeof stemw, "%s/wwstage", dir);
snprintf(cs, sizeof cs, "%s/cstage.s", dir);
snprintf(ws, sizeof ws, "%s/wwstage.s", dir);
snprintf(scratchc, sizeof scratchc, "%s.sepwork", stemc);
snprintf(scratchw, sizeof scratchw, "%s.sepwork", stemw);
/* #94 sep layout: `ww test -c --sep -o <stem>` carries -T to the root
/* #94 sep layout: `ww test -c -o <stem>` carries -T to the root
* so w6c synthesizes the test table + fork loop, and emits per-package
* asm to <stem>.sepwork/ (compile-only, no harness run; WW_PKGCACHE
* pinned to /tmp). The cstage build (w6c) and wwstage build (w6c_ww)
* asm to <stem>.sepwork/ (compile-only, no harness run). The cstage
* build (w6c) and wwstage build (w6c_ww)
* must produce byte-identical asm across all packages. */
snprintf(cmd, sizeof cmd,
"WW_PKGCACHE=/tmp/at911_pkgc_c_%d %s/ww test -c --sep -o %s "
"test/wcc/data/attest_record.ww > /dev/null 2>&1", pid, bin, stemc);
"%s/ww test -c -o %s "
"test/wcc/data/attest_record.ww > /dev/null 2>&1", bin, stemc);
if (runwait(cmd) != 0) {
fprintf(stderr, "911 FAIL: cstage ww test -c --sep\n");
return 1;
fprintf(stderr, "911 FAIL: cstage ww test -c \n");
goto done;
}
snprintf(cmd, sizeof cmd,
"WW_PKGCACHE=/tmp/at911_pkgc_w_%d %s/ww_ww test -c --sep -o %s "
"test/wcc/data/attest_record.ww > /dev/null 2>&1", pid, bin, stemw);
"%s/ww_ww test -c -o %s "
"test/wcc/data/attest_record.ww > /dev/null 2>&1", bin, stemw);
if (runwait(cmd) != 0) {
fprintf(stderr, "911 FAIL: wwstage ww_ww test -c --sep\n");
return 1;
fprintf(stderr, "911 FAIL: wwstage ww_ww test -c \n");
goto done;
}
snprintf(cmd, sizeof cmd, "cat %s.sepwork/*.s > %s 2>/dev/null", stemc, cs);
if (system(cmd)) {}
if (runwait(cmd) != 0) {
fprintf(stderr, "911 FAIL: cstage asm aggregation failed\n");
goto done;
}
snprintf(cmd, sizeof cmd, "cat %s.sepwork/*.s > %s 2>/dev/null", stemw, ws);
if (system(cmd)) {}
if (runwait(cmd) != 0) {
fprintf(stderr, "911 FAIL: wwstage asm aggregation failed\n");
goto done;
}
char bc[262144], bw[262144];
int rc = 0;
rc = 0;
if (slurp(cs, bc, sizeof bc) < 0 || slurp(ws, bw, sizeof bw) < 0) {
fprintf(stderr, "911 FAIL: slurp byteid asm\n");
rc = 1;
@@ -159,11 +190,23 @@ byteid(const char *bin)
fprintf(stderr, "911 FAIL: -T asm differs between stages\n");
rc = 1;
}
unlink(cs); unlink(ws);
snprintf(cmd, sizeof cmd, "rm -rf %s.sepwork %s.sepwork %s %s "
"/tmp/at911_pkgc_c_%d /tmp/at911_pkgc_w_%d",
stemc, stemw, stemc, stemw, pid, pid);
if (system(cmd)) {}
done:
{
int cleanfail = 0;
if (unlink(cs) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(ws) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(stemc) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(stemw) != 0 && errno != ENOENT) cleanfail = 1;
snprintf(cmd, sizeof cmd, "rm -rf %s", scratchc);
if (runwait(cmd) != 0) cleanfail = 1;
snprintf(cmd, sizeof cmd, "rm -rf %s", scratchw);
if (runwait(cmd) != 0) cleanfail = 1;
if (rmdir(dir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "911 FAIL: byteid workspace cleanup failed\n");
rc = 1;
}
}
return rc;
}
@@ -178,6 +221,7 @@ main(void)
if (byteid(bin) != 0) return 1;
printf("@test record-and-continue: 3 fault classes recorded + run "
"proceeds + counts + all-pass exit 0 + cs/ww byte-id (#17)\n");
"proceeds + exact counts + failure exit clamps to 1 + all-pass "
"exit 0 + cs/ww byte-id (#17)\n");
return 0;
}

View File

@@ -43,6 +43,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -75,6 +76,7 @@ struct row {
* the same u16 sequence strconv used, so the expected exits are unchanged
* (idx0→0, idx2→0x0801&0xFF=1, idx4→0x1006&0xFF=6). */
#define PROBE_MOD \
"package wcmodarr;\n" \
"export let probe_table: [8]u16 = [0u16, 0x0800u16, 0x0801u16, " \
"0x0803u16, 0x1006u16, 0x1009u16, 0x100Du16, 0x1812u16];\n"
@@ -169,62 +171,93 @@ main(void)
int n = 0, fail = 0;
for (int i = 0; rows[i].src; i++, n++) {
char tmpdir[96];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwmoda_%d_d_%d",
getpid(), i);
mkdir(tmpdir, 0755);
char src[160];
char tmpdir[] = "/tmp/wwmoda_XXXXXX";
char src[160], moddir[224] = {0}, modf[320] = {0};
char stem[200] = {0}, scratch[232] = {0};
char cs_s[200] = {0}, ws_s[200] = {0}, cmd[2048];
int rowfail = 0, moddir_owned = 0;
if (mkdtemp(tmpdir) == NULL) {
fprintf(stderr, "row[%s]: workspace acquisition failed\n",
rows[i].label);
fail++;
continue;
}
snprintf(src, sizeof src, "%s/main915.ww", tmpdir);
FILE *f = fopen(src, "wb");
if (f == NULL) { fail++; rmdir(tmpdir); continue; }
if (f == NULL) {
fprintf(stderr, "row[%s]: source create failed\n",
rows[i].label);
rowfail = 1;
goto row_done;
}
fputs(rows[i].src, f);
fclose(f);
int werr = ferror(f);
if (fclose(f) != 0) werr = 1;
if (werr) {
fprintf(stderr, "row[%s]: source write failed\n",
rows[i].label);
rowfail = 1;
goto row_done;
}
char cmd[2048];
/* Exported-probe-table package lives at <tmpdir>/<mod>/<mod>.ww
* so the sep dep-scan (rooted at the entry's dir) resolves the
* `import <mod>;`. */
if (rows[i].modname != NULL) {
char moddir[224], modf[320];
snprintf(moddir, sizeof moddir, "%s/%s", tmpdir,
rows[i].modname);
mkdir(moddir, 0755);
if (mkdir(moddir, 0755) != 0) {
fprintf(stderr, "row[%s]: module directory create failed\n",
rows[i].label);
rowfail = 1;
goto row_done;
}
moddir_owned = 1;
snprintf(modf, sizeof modf, "%s/%s.ww", moddir,
rows[i].modname);
FILE *mf = fopen(modf, "wb");
if (mf == NULL) {
fail++;
snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir);
(void)runwait(cmd);
continue;
fprintf(stderr, "row[%s]: module source create failed\n",
rows[i].label);
rowfail = 1;
goto row_done;
}
fputs(rows[i].modsrc, mf);
fclose(mf);
werr = ferror(mf);
if (fclose(mf) != 0) werr = 1;
if (werr) {
fprintf(stderr, "row[%s]: module source write failed\n",
rows[i].label);
rowfail = 1;
goto row_done;
}
}
/* Single-file runtime behavior moved to r915_arr_* wwfixtures.
* Keep auxiliary-package rows here because the fixture grammar
* cannot materialize their directory/import topology. */
if (rows[i].modname != NULL) {
/* #93 sep layout: build via `--sep -o <stem>` (the exported
/* #93 sep layout: build via `-o <stem>` (the exported
* probe table links across packages under separate compilation);
* WW_PKGCACHE pinned under tmpdir. */
char stem[200];
* all outputs stay under tmpdir. */
snprintf(stem, sizeof stem, "%s/main915", tmpdir);
snprintf(scratch, sizeof scratch, "%s.sepwork", stem);
snprintf(cmd, sizeof cmd,
"cd %s && WW_PKGCACHE=%s/pkgc %s/ww build --sep -o %s %s "
">/dev/null 2>&1", tmpdir, tmpdir, bin, stem, src);
"cd %s && %s/ww build -o %s %s "
">/dev/null 2>&1", tmpdir, bin, stem, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: cstage build failed\n",
rows[i].label);
fail++;
snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir);
(void)runwait(cmd);
continue;
rowfail = 1;
goto row_done;
}
int got = runwait(stem);
if (got != rows[i].want_exit) {
fprintf(stderr, "row[%s]: cstage exit %d, want %d\n",
rows[i].label, got, rows[i].want_exit);
fail++;
rowfail = 1;
}
}
/* Byte-id leg — bare w6c vs w6c_ww (the compiler binaries, NOT
@@ -232,7 +265,6 @@ main(void)
* fed standalone; the module rows' runtime exit IS their gate
* (+ the 994 bootstrap corpus for the cross-stage byte-id). */
if (rows[i].modname == NULL) {
char cs_s[200], ws_s[200];
snprintf(cs_s, sizeof cs_s, "%s/cs.s", tmpdir);
snprintf(ws_s, sizeof ws_s, "%s/ww.s", tmpdir);
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
@@ -240,26 +272,49 @@ main(void)
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c failed\n",
rows[i].label);
fail++;
rowfail = 1;
} else {
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
w6c_ww, ws_s, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww failed\n",
rows[i].label);
fail++;
rowfail = 1;
} else if (slurp_eq(cs_s, ws_s) != 0) {
fprintf(stderr,
"row[%s]: cstage/wwstage .s DIFFER "
"(rule-10 byte-id violation)\n",
rows[i].label);
fail++;
rowfail = 1;
}
}
}
snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir);
(void)runwait(cmd);
row_done:
{
int cleanfail = 0;
if (cs_s[0] && unlink(cs_s) != 0 && errno != ENOENT)
cleanfail = 1;
if (ws_s[0] && unlink(ws_s) != 0 && errno != ENOENT)
cleanfail = 1;
if (scratch[0]) {
snprintf(cmd, sizeof cmd, "rm -rf %s", scratch);
if (runwait(cmd) != 0) cleanfail = 1;
}
if (stem[0] && unlink(stem) != 0 && errno != ENOENT)
cleanfail = 1;
if (modf[0] && unlink(modf) != 0 && errno != ENOENT)
cleanfail = 1;
if (moddir_owned && rmdir(moddir) != 0) cleanfail = 1;
if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(tmpdir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "row[%s]: workspace cleanup failed\n",
rows[i].label);
rowfail = 1;
}
}
if (rowfail) fail++;
}
if (fail) {

View File

@@ -22,14 +22,13 @@
* - runtime: build via the ww / ww_ww drivers and run; the exit
* code pins every payload word INCLUDING the last (the
* truncation signature).
* Rows flagged `buildfail` must be REJECTED by both stages (the
* rule-7 loud-stops over the unwired #40-family shapes: widening
* sret forward, `?`/arg-position consumption of an sret-class call
* result).
* Rows flagged `buildfail` are source carriers only; their symmetric
* diagnostic checks moved to r926_sret_reject_* wwfixtures.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -470,24 +469,19 @@ file_has(const char *path, const char *needle)
}
static int
run_driver(const char *driver, const char *src, const char *label)
run_driver(const char *driver, const char *src, const char *label,
const char *work)
{
char tmpdir[128], outbin[256], rmcmd[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/tsret_%d_%s_d", getpid(), driver);
mkdir(tmpdir, 0755);
snprintf(outbin, sizeof outbin, "%s/m", tmpdir);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
char outbin[256], cmd[1024];
snprintf(outbin, sizeof outbin, "%s/%s.run", work, driver);
snprintf(cmd, sizeof cmd, "%s/%s build -o %s %s >/dev/null 2>&1",
g_bin, driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
label, driver);
runwait(rmcmd);
return -1;
}
int got = runwait(outbin);
runwait(rmcmd);
return got;
return runwait(outbin);
}
int
@@ -508,60 +502,57 @@ main(void)
int total = 0, fail = 0;
for (int i = 0; i < n; i++) {
const struct row *r = &rows[i];
char src[128], cs_s[128], ww_s[128];
char cs_e[128], ww_e[128];
snprintf(src, sizeof src, "/tmp/tsret_%d_%d.ww",
getpid(), i);
snprintf(cs_s, sizeof cs_s, "/tmp/tsret_%d_%d_cs.s",
getpid(), i);
snprintf(ww_s, sizeof ww_s, "/tmp/tsret_%d_%d_ww.s",
getpid(), i);
snprintf(cs_e, sizeof cs_e, "/tmp/tsret_%d_%d_cs.err",
getpid(), i);
snprintf(ww_e, sizeof ww_e, "/tmp/tsret_%d_%d_ww.err",
getpid(), i);
/* Loud-reject behavior is owned by r926_sret_reject_* fixtures. */
if (r->buildfail) continue;
char work[] = "/tmp/tsret_XXXXXX";
char src[256], cs_s[256], ww_s[256];
char cs_e[256], ww_e[256], cbin[256], wbin[256];
char cscratch[288], wscratch[288], cmd[384];
int rowfail = 0;
if (mkdtemp(work) == NULL) {
fprintf(stderr, "FAIL row[%s]: workspace acquisition failed\n",
r->label);
fail++;
continue;
}
snprintf(src, sizeof src, "%s/input.ww", work);
snprintf(cs_s, sizeof cs_s, "%s/c.s", work);
snprintf(ww_s, sizeof ww_s, "%s/w.s", work);
snprintf(cs_e, sizeof cs_e, "%s/c.err", work);
snprintf(ww_e, sizeof ww_e, "%s/w.err", work);
snprintf(cbin, sizeof cbin, "%s/ww.run", work);
snprintf(wbin, sizeof wbin, "%s/ww_ww.run", work);
snprintf(cscratch, sizeof cscratch, "%s.sepwork", cbin);
snprintf(wscratch, sizeof wscratch, "%s.sepwork", wbin);
FILE *f = fopen(src, "wb");
if (!f) return 1;
if (!f) {
fprintf(stderr, "FAIL row[%s]: source create failed\n", r->label);
rowfail = 1;
goto row_done;
}
fputs("package main;\n\n", f);
fputs(r->src, f);
fclose(f);
int werr = ferror(f);
if (fclose(f) != 0) werr = 1;
if (werr) {
fprintf(stderr, "FAIL row[%s]: source write failed\n", r->label);
rowfail = 1;
goto row_done;
}
int cs_rc = compile_s("w6c", src, cs_s, cs_e);
int ww_rc = compile_s("w6c_ww", src, ww_s, ww_e);
if (r->buildfail) {
total++;
if (cs_rc == 0 || ww_rc == 0) {
fprintf(stderr, "FAIL row[%s]: loud-stop "
"expected, cstage rc=%d wwstage rc=%d\n",
r->label, cs_rc, ww_rc);
fail++;
} else if (!(file_has(cs_e, "#38b") || file_has(cs_e, "#40"))
|| !(file_has(ww_e, "#38b") || file_has(ww_e, "#40"))) {
/* the rejection must be THE #38b/#40 loud-stop,
* not an unrelated checker error masquerading
* as coverage. */
fprintf(stderr, "FAIL row[%s]: rejected but "
"without the #38b/#40 loud-stop marker\n",
r->label);
fail++;
}
unlink(src); unlink(cs_s); unlink(ww_s);
unlink(cs_e); unlink(ww_e);
continue;
}
total++;
if (cs_rc != 0 || ww_rc != 0) {
fprintf(stderr, "FAIL row[%s]: compile rc cs=%d "
"ww=%d\n", r->label, cs_rc, ww_rc);
fail++;
unlink(src); unlink(cs_s); unlink(ww_s);
unlink(cs_e); unlink(ww_e);
continue;
rowfail = 1;
goto row_done;
}
if (!file_eq(cs_s, ww_s)) {
fprintf(stderr, "FAIL row[%s]: cs != ww .s\n",
r->label);
fail++;
rowfail = 1;
}
/* hidden-RDI dest: present iff the row is sret-class.
* The boundary rows pin the 32B class stays register. */
@@ -569,33 +560,54 @@ main(void)
if (r->sret && !has_di) {
fprintf(stderr, "FAIL row[%s]: expected sret "
"hidden-RDI LEAQ, none emitted\n", r->label);
fail++;
rowfail = 1;
}
if (!r->sret && has_di) {
fprintf(stderr, "FAIL row[%s]: 32B-slot row "
"flipped to sret (classifier boundary "
"off-by-one)\n", r->label);
fail++;
rowfail = 1;
}
int got_cs = run_driver("ww", src, r->label);
int got_cs = run_driver("ww", src, r->label, work);
if (got_cs != r->want) {
fprintf(stderr, "FAIL row[%s] cstage: want %d "
"got %d\n", r->label, r->want, got_cs);
fail++;
rowfail = 1;
}
char wwdrv[600];
snprintf(wwdrv, sizeof wwdrv, "%s/ww_ww", g_bin);
if (access(wwdrv, X_OK) == 0) {
int got_ww = run_driver("ww_ww", src, r->label);
int got_ww = run_driver("ww_ww", src, r->label, work);
if (got_ww != r->want) {
fprintf(stderr, "FAIL row[%s] wwstage: "
"want %d got %d\n",
r->label, r->want, got_ww);
fail++;
rowfail = 1;
}
}
unlink(src); unlink(cs_s); unlink(ww_s);
unlink(cs_e); unlink(ww_e);
row_done:
{
int cleanfail = 0;
if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(cs_s) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(ww_s) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(cs_e) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(ww_e) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(cbin) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(wbin) != 0 && errno != ENOENT) cleanfail = 1;
snprintf(cmd, sizeof cmd, "rm -rf %s", cscratch);
if (runwait(cmd) != 0) cleanfail = 1;
snprintf(cmd, sizeof cmd, "rm -rf %s", wscratch);
if (runwait(cmd) != 0) cleanfail = 1;
if (rmdir(work) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "FAIL row[%s]: workspace cleanup failed\n",
r->label);
rowfail = 1;
}
}
if (rowfail) fail++;
}
if (fail) {
fprintf(stderr, "tagged_sret_run: %d/%d rows failed\n",

View File

@@ -18,8 +18,9 @@
* the pre-#44 compiler when the fix landed), and the N_INDEX
* tagged-element store site mixing with an sret return.
*
* Per row: w6c vs w6c_ww byte-id (rule 10) + runtime via both the ww
* and ww_ww drivers. Payload checks stay within the 32B register
* Runtime behavior now lives in the r926_tagscr_* wwfixtures. This wrapper
* retains only w6c vs w6c_ww byte identity (rule 10). Payload checks stay
* within the 32B register
* cursor — wide tagged ELEMENT reads past 32B truncate (the filed #43
* residual, pre-existing, out of #44's scope).
*/
@@ -190,22 +191,6 @@ file_eq(const char *a, const char *b)
return runwait(cmd) == 0;
}
static int
run_driver(const char *driver, const char *src, const char *tmpdir,
const char *label)
{
char cmd[1024], outbin[256];
snprintf(outbin, sizeof outbin, "%s/drvout", tmpdir);
snprintf(cmd, sizeof cmd, "%s/%s build -o %s %s >/dev/null 2>&1",
g_bin, driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
label, driver);
return -1;
}
return runwait(outbin);
}
int
main(void)
{
@@ -227,7 +212,12 @@ main(void)
char tmpdir[96], src[160], cs_s[160], ww_s[160], rmcmd[160];
snprintf(tmpdir, sizeof tmpdir, "/tmp/tagscr_%d_%d_d",
getpid(), i);
mkdir(tmpdir, 0755);
/* an unowned path (stale dir, full /tmp) must not be compiled
* in — or rm -rf'd — below. */
if (mkdir(tmpdir, 0755) != 0) {
perror(tmpdir);
return 1;
}
snprintf(src, sizeof src, "%s/tagscr_%d_%d.ww",
tmpdir, getpid(), i);
snprintf(cs_s, sizeof cs_s, "%s/tagscr_%d_%d_cs.s",
@@ -236,50 +226,45 @@ main(void)
tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return 1; }
if (!f) {
perror(src);
if (runwait(rmcmd) != 0)
fprintf(stderr, "tagscr_sizes_run: cleanup "
"%s failed\n", tmpdir);
return 1;
}
fputs("package main;\n\n", f);
fputs(r->src, f);
fclose(f);
total++;
int rowfail = 0;
int cs_rc = compile_s("w6c", src, cs_s);
int ww_rc = compile_s("w6c_ww", src, ww_s);
if (cs_rc != 0 || ww_rc != 0) {
fprintf(stderr, "FAIL row[%s]: compile rc cs=%d "
"ww=%d\n", r->label, cs_rc, ww_rc);
fail++;
runwait(rmcmd);
continue;
}
if (!file_eq(cs_s, ww_s)) {
rowfail = 1;
} else if (!file_eq(cs_s, ww_s)) {
fprintf(stderr, "FAIL row[%s]: cs != ww .s\n",
r->label);
rowfail = 1;
}
/* a silent cleanup failure must fail an otherwise-passing
* row without masking its own diagnostic. */
if (runwait(rmcmd) != 0) {
fprintf(stderr, "FAIL row[%s]: cleanup %s failed\n",
r->label, tmpdir);
rowfail = 1;
}
if (rowfail)
fail++;
}
int got_cs = run_driver("ww", src, tmpdir, r->label);
if (got_cs != r->want) {
fprintf(stderr, "FAIL row[%s] cstage: want %d "
"got %d\n", r->label, r->want, got_cs);
fail++;
}
char wwdrv[600];
snprintf(wwdrv, sizeof wwdrv, "%s/ww_ww", g_bin);
if (access(wwdrv, X_OK) == 0) {
int got_ww = run_driver("ww_ww", src, tmpdir, r->label);
if (got_ww != r->want) {
fprintf(stderr, "FAIL row[%s] wwstage: "
"want %d got %d\n",
r->label, r->want, got_ww);
fail++;
}
}
runwait(rmcmd);
}
if (fail) {
fprintf(stderr, "tagscr_sizes_run: %d/%d rows failed\n",
fail, total);
return 1;
}
printf("tagscr_sizes_run: %d rows ok\n", total);
printf("tagscr_sizes_run: %d assembly rows ok\n", total);
return 0;
}

View File

@@ -19,6 +19,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -268,14 +269,36 @@ write_file(const char *dir, const char *name, const char *body)
static int
run_driver(const char *driver, const struct row *r, int i)
{
char tmpdir[96], adir[128], cmd[2048];
snprintf(tmpdir, sizeof tmpdir, "/tmp/m4cmr_%d_%d", getpid(), i);
char tmpdir[128], adir[160], asrc[192], bsrc[192];
char outbin[192], scratch[208], cmd[2048];
snprintf(tmpdir, sizeof tmpdir, "/tmp/m4cmr_%d_%d_XXXXXX",
getpid(), i);
if (mkdtemp(tmpdir) == NULL) {
fprintf(stderr, "row[%s]: temporary directory acquisition failed\n",
r->label);
return -1;
}
snprintf(adir, sizeof adir, "%s/a", tmpdir);
mkdir(tmpdir, 0755);
mkdir(adir, 0755);
snprintf(asrc, sizeof asrc, "%s/a.ww", adir);
snprintf(bsrc, sizeof bsrc, "%s/b.ww", tmpdir);
snprintf(outbin, sizeof outbin, "%s/b", tmpdir);
snprintf(scratch, sizeof scratch, "%s/b.sepwork", tmpdir);
if (write_file(adir, "a.ww", r->a_src) != 0) return -1;
if (write_file(tmpdir, "b.ww", r->b_src) != 0) return -1;
int result = -1, adir_owned = 0;
if (mkdir(adir, 0755) != 0) {
fprintf(stderr, "row[%s]: module directory creation failed\n",
r->label);
goto cleanup;
}
adir_owned = 1;
if (write_file(adir, "a.ww", r->a_src) != 0) {
fprintf(stderr, "row[%s]: cannot write a/a.ww\n", r->label);
goto cleanup;
}
if (write_file(tmpdir, "b.ww", r->b_src) != 0) {
fprintf(stderr, "row[%s]: cannot write b.ww\n", r->label);
goto cleanup;
}
/* Build via the driver from inside tmpdir so `use a;` resolves to
* ./a/a.ww and ww's source-dir search hits b's siblings first. */
@@ -284,18 +307,30 @@ run_driver(const char *driver, const struct row *r, int i)
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir);
runwait(cmd);
return -1;
goto cleanup;
}
char outbin[160];
snprintf(outbin, sizeof outbin, "%s/b", tmpdir);
int got = runwait(outbin);
result = runwait(outbin);
snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir);
runwait(cmd);
return got;
cleanup:
{
int bad = 0;
snprintf(cmd, sizeof cmd, "rm -rf %s", scratch);
if (runwait(cmd) != 0) bad = 1;
if (unlink(outbin) != 0 && errno != ENOENT) bad = 1;
if (unlink(bsrc) != 0 && errno != ENOENT) bad = 1;
if (adir_owned) {
if (unlink(asrc) != 0 && errno != ENOENT) bad = 1;
if (rmdir(adir) != 0) bad = 1;
}
if (rmdir(tmpdir) != 0) bad = 1;
if (bad) {
fprintf(stderr, "row[%s]: temporary cleanup failed\n",
r->label);
if (result == r->want) result = -1;
}
}
return result;
}
int

View File

@@ -26,17 +26,15 @@
* - runtime: build via ww / ww_ww and run; exit codes read the tag
* AND the late payload words (slot offsets +48/+56, past the old
* register cap) FIRST so their loss is the visible failure.
* Rows flagged `buildfail` must be rejected by BOTH stages with that
* row's EXACT #38b diagnostic — pinning WHICH guard fired (the
* remaining unwired sub-shapes: sret-class call source, global-let
* source, exact-type rvalue, variadic element, register-overflow
* mixing caller- and callee-side). #40/FB3 wired the place-resolved
* Rows flagged `buildfail` are source carriers only; their exact symmetric
* diagnostics moved to r929_memarg_reject_* wwfixtures. #40/FB3 wired the place-resolved
* sources (slice element, deref-spine, nested index) via the
* cgplaceaddr fallback — those are run rows now.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -623,24 +621,18 @@ file_has(const char *path, const char *needle)
}
static int
run_driver(const char *driver, const char *src, const char *label)
run_driver(const char *driver, const char *src, const char *outbin,
const char *label)
{
char tmpdir[128], outbin[160], rmcmd[200], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/tmemarg_%d_d", getpid());
mkdir(tmpdir, 0755);
snprintf(outbin, sizeof outbin, "%s/m", tmpdir);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
char cmd[1024];
snprintf(cmd, sizeof cmd, "%s/%s build -o %s %s >/dev/null 2>&1",
g_bin, driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
label, driver);
runwait(rmcmd);
return -1;
}
int got = runwait(outbin);
runwait(rmcmd);
return got;
return runwait(outbin);
}
int
@@ -661,23 +653,40 @@ main(void)
int total = 0, fail = 0;
for (int i = 0; i < n; i++) {
const struct row *r = &rows[i];
char src[128], cs_s[128], ww_s[128];
char cs_e[128], ww_e[128];
snprintf(src, sizeof src, "/tmp/tmemarg_%d_%d.ww",
getpid(), i);
snprintf(cs_s, sizeof cs_s, "/tmp/tmemarg_%d_%d_cs.s",
getpid(), i);
snprintf(ww_s, sizeof ww_s, "/tmp/tmemarg_%d_%d_ww.s",
getpid(), i);
snprintf(cs_e, sizeof cs_e, "/tmp/tmemarg_%d_%d_cs.err",
getpid(), i);
snprintf(ww_e, sizeof ww_e, "/tmp/tmemarg_%d_%d_ww.err",
char tmpdir[128];
snprintf(tmpdir, sizeof tmpdir, "/tmp/tmemarg_%d_%d_XXXXXX",
getpid(), i);
if (mkdtemp(tmpdir) == NULL) {
fprintf(stderr, "FAIL row[%s]: temporary directory "
"acquisition failed\n", r->label);
fail++;
continue;
}
char src[192], cs_s[192], ww_s[192], cs_e[192], ww_e[192];
char cs_bin[192], ww_bin[192], cs_work[208], ww_work[208];
snprintf(src, sizeof src, "%s/case.ww", tmpdir);
snprintf(cs_s, sizeof cs_s, "%s/cs.s", tmpdir);
snprintf(ww_s, sizeof ww_s, "%s/ww.s", tmpdir);
snprintf(cs_e, sizeof cs_e, "%s/cs.err", tmpdir);
snprintf(ww_e, sizeof ww_e, "%s/ww.err", tmpdir);
snprintf(cs_bin, sizeof cs_bin, "%s/run_c", tmpdir);
snprintf(ww_bin, sizeof ww_bin, "%s/run_w", tmpdir);
snprintf(cs_work, sizeof cs_work, "%s.sepwork", cs_bin);
snprintf(ww_work, sizeof ww_work, "%s.sepwork", ww_bin);
FILE *f = fopen(src, "wb");
if (!f) return 1;
if (!f) {
fprintf(stderr, "FAIL row[%s]: source setup failed\n",
r->label);
fail++;
goto row_cleanup;
}
fputs("package main;\n\n", f);
fputs(r->src, f);
fclose(f);
/* Loud-reject behavior is owned by r929_memarg_reject_* fixtures. */
if (r->buildfail) goto row_cleanup;
int cs_rc = compile_s("w6c", src, cs_s, cs_e);
int ww_rc = compile_s("w6c_ww", src, ww_s, ww_e);
@@ -699,18 +708,14 @@ main(void)
r->label, r->failmark);
fail++;
}
unlink(src); unlink(cs_s); unlink(ww_s);
unlink(cs_e); unlink(ww_e);
continue;
goto row_cleanup;
}
total++;
if (cs_rc != 0 || ww_rc != 0) {
fprintf(stderr, "FAIL row[%s]: compile rc cs=%d "
"ww=%d\n", r->label, cs_rc, ww_rc);
fail++;
unlink(src); unlink(cs_s); unlink(ww_s);
unlink(cs_e); unlink(ww_e);
continue;
goto row_cleanup;
}
if (!file_eq(cs_s, ww_s)) {
fprintf(stderr, "FAIL row[%s]: cs != ww .s\n",
@@ -728,7 +733,7 @@ main(void)
r->label);
fail++;
}
int got_cs = run_driver("ww", src, r->label);
int got_cs = run_driver("ww", src, cs_bin, r->label);
if (got_cs != r->want) {
fprintf(stderr, "FAIL row[%s] cstage: want %d "
"got %d\n", r->label, r->want, got_cs);
@@ -737,7 +742,7 @@ main(void)
char wwdrv[600];
snprintf(wwdrv, sizeof wwdrv, "%s/ww_ww", g_bin);
if (access(wwdrv, X_OK) == 0) {
int got_ww = run_driver("ww_ww", src, r->label);
int got_ww = run_driver("ww_ww", src, ww_bin, r->label);
if (got_ww != r->want) {
fprintf(stderr, "FAIL row[%s] wwstage: "
"want %d got %d\n",
@@ -745,8 +750,29 @@ main(void)
fail++;
}
}
unlink(src); unlink(cs_s); unlink(ww_s);
unlink(cs_e); unlink(ww_e);
row_cleanup:
{
int bad = 0;
char rmcmd[256];
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", cs_work);
if (runwait(rmcmd) != 0) bad = 1;
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", ww_work);
if (runwait(rmcmd) != 0) bad = 1;
if (unlink(cs_bin) != 0 && errno != ENOENT) bad = 1;
if (unlink(ww_bin) != 0 && errno != ENOENT) bad = 1;
if (unlink(src) != 0 && errno != ENOENT) bad = 1;
if (unlink(cs_s) != 0 && errno != ENOENT) bad = 1;
if (unlink(ww_s) != 0 && errno != ENOENT) bad = 1;
if (unlink(cs_e) != 0 && errno != ENOENT) bad = 1;
if (unlink(ww_e) != 0 && errno != ENOENT) bad = 1;
if (rmdir(tmpdir) != 0) bad = 1;
if (bad) {
fprintf(stderr, "FAIL row[%s]: temporary cleanup failed\n",
r->label);
fail++;
}
}
}
if (fail) {
fprintf(stderr, "tagged_memarg_run: %d/%d rows failed\n",

View File

@@ -25,10 +25,9 @@
* @symbol("rt_free") decl — covered by the lisp example / stdlib
* suites, not re-pinned here.
*
* Per row: w6c vs w6c_ww .s byte-id (rule 10), a negative grep that
* no `free` symbol survives anywhere in the .s (byte-id alone would
* pass if BOTH stages still emitted CALL free), and runtime via both
* drivers.
* Runtime behavior is carried by the r930_free_* wwfixtures. This wrapper
* retains w6c vs w6c_ww byte identity plus the negative assembly assertion
* that no `free` symbol survives.
*/
#include <stdio.h>
#include <stdlib.h>
@@ -154,22 +153,6 @@ has_free_sym(const char *s_path)
return runwait(cmd) == 0;
}
static int
run_driver(const char *driver, const char *src, const char *tmpdir,
const char *label)
{
char cmd[1024], outbin[256];
snprintf(outbin, sizeof outbin, "%s/%s_out", tmpdir, driver);
snprintf(cmd, sizeof cmd, "%s/%s build -o %s %s >/dev/null 2>&1",
g_bin, driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
label, driver);
return -1;
}
return runwait(outbin);
}
int
main(void)
{
@@ -192,66 +175,69 @@ main(void)
* byte-id .s dumps all live under one tmpdir; rm -rf on every
* exit path. Symbol mangling is package/import-derived, not
* entry-path derived, so the in-tmpdir source keeps the cs==ww
* .s identical. */
* .s identical. Temp names must avoid the substring 'free':
* has_free_sym greps the whole .s, so an embedded path would
* false-fail every row. */
char tmpdir[64], src[128], cs_s[128], ww_s[128], rmcmd[160];
snprintf(tmpdir, sizeof tmpdir, "/tmp/freenoop_%d_d_%d",
snprintf(tmpdir, sizeof tmpdir, "/tmp/noopfr_%d_d_%d",
getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/freenoop_%d_%d.ww",
/* an unowned path (stale dir, full /tmp) must not be compiled
* in — or rm -rf'd — below. */
if (mkdir(tmpdir, 0755) != 0) {
perror(tmpdir);
return 1;
}
snprintf(src, sizeof src, "%s/noopfr_%d_%d.ww",
tmpdir, getpid(), i);
snprintf(cs_s, sizeof cs_s, "%s/cs.s", tmpdir);
snprintf(ww_s, sizeof ww_s, "%s/ww.s", tmpdir);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return 1; }
if (!f) {
perror(src);
if (runwait(rmcmd) != 0)
fprintf(stderr, "free_noop_run: cleanup "
"%s failed\n", tmpdir);
return 1;
}
fputs("package main;\n\n", f);
fputs(r->src, f);
fclose(f);
total++;
int rowfail = 0;
int cs_rc = compile_s("w6c", src, cs_s);
int ww_rc = compile_s("w6c_ww", src, ww_s);
if (cs_rc != 0 || ww_rc != 0) {
fprintf(stderr, "FAIL row[%s]: compile rc cs=%d "
"ww=%d\n", r->label, cs_rc, ww_rc);
fail++;
runwait(rmcmd);
continue;
}
if (!file_eq(cs_s, ww_s)) {
fprintf(stderr, "FAIL row[%s]: cs != ww .s\n",
r->label);
fail++;
}
if (has_free_sym(cs_s)) {
fprintf(stderr, "FAIL row[%s]: 'free' survives in "
"the .s — lowering is not a no-op\n", r->label);
fail++;
}
int got_cs = run_driver("ww", src, tmpdir, r->label);
if (got_cs != r->want) {
fprintf(stderr, "FAIL row[%s] cstage: want %d "
"got %d\n", r->label, r->want, got_cs);
fail++;
}
char wwdrv[600];
snprintf(wwdrv, sizeof wwdrv, "%s/ww_ww", g_bin);
if (access(wwdrv, X_OK) == 0) {
int got_ww = run_driver("ww_ww", src, tmpdir, r->label);
if (got_ww != r->want) {
fprintf(stderr, "FAIL row[%s] wwstage: "
"want %d got %d\n",
r->label, r->want, got_ww);
fail++;
fail++; rowfail = 1;
} else {
if (!file_eq(cs_s, ww_s)) {
fprintf(stderr, "FAIL row[%s]: cs != ww .s\n",
r->label);
fail++; rowfail = 1;
}
if (has_free_sym(cs_s)) {
fprintf(stderr, "FAIL row[%s]: 'free' survives in "
"the .s — lowering is not a no-op\n", r->label);
fail++; rowfail = 1;
}
}
runwait(rmcmd);
/* a silent cleanup failure must fail an otherwise-passing
* row without masking its own diagnostic. */
if (runwait(rmcmd) != 0) {
fprintf(stderr, "FAIL row[%s]: cleanup %s failed\n",
r->label, tmpdir);
if (!rowfail)
fail++;
}
}
if (fail) {
fprintf(stderr, "free_noop_run: %d/%d rows failed\n",
fail, total);
return 1;
}
printf("free_noop_run: %d rows ok\n", total);
printf("free_noop_run: %d assembly rows ok\n", total);
return 0;
}

View File

@@ -19,12 +19,13 @@
* already used (LEAQ off(BP), DI). Local rows pin no regression at the
* shared cgassign / cglet sret-receive sites.
*
* Each row runs on BOTH cstage (ww) and wwstage (ww_ww), and the
* generated asm is checked byte-identical (cs.s == ww.s, rule 10).
* Runtime behavior is carried by the r940_global_sret_* wwfixtures. This
* wrapper retains the separate-build assembly artifacts and their byte
* identity (cs.s == ww.s, rule 10).
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -144,49 +145,62 @@ static int
byteid(const char *dir, int i)
{
char cs[256], ws[256], cmd[1024];
int rc;
snprintf(cs, sizeof cs, "%s/bid_cs_%d.s", dir, i);
snprintf(ws, sizeof ws, "%s/bid_ww_%d.s", dir, i);
/* an unmatched sepwork glob must be a loud harness error — two
* empty concatenations would otherwise cmp equal (vacuous green). */
snprintf(cmd, sizeof cmd, "cat %s/gsret_c_%d.sepwork/*.s > %s 2>/dev/null",
dir, i, cs);
if (system(cmd)) {}
if (runwait(cmd) != 0) {
rc = -1;
goto cleanup;
}
snprintf(cmd, sizeof cmd, "cat %s/gsret_w_%d.sepwork/*.s > %s 2>/dev/null",
dir, i, ws);
if (system(cmd)) {}
if (runwait(cmd) != 0) {
rc = -1;
goto cleanup;
}
snprintf(cmd, sizeof cmd, "cmp -s %s %s", cs, ws);
int rc = runwait(cmd);
unlink(cs); unlink(ws);
return rc == 0 ? 0 : 1;
rc = runwait(cmd) == 0 ? 0 : 1;
cleanup:
if (unlink(cs) != 0 && errno != ENOENT) { perror(cs); if (rc == 0) rc = -1; }
if (unlink(ws) != 0 && errno != ENOENT) { perror(ws); if (rc == 0) rc = -1; }
return rc;
}
/* Build `r` with `driver` via `--sep -o <dir>/<stem>` (per-package asm in
* <dir>/<stem>.sepwork/), run the binary, return its exit code (or -1 on
* build failure). `cachetag` keys a private WW_PKGCACHE. */
/* Build `r` with `driver -o <dir>/<stem>` (per-package asm in
* <dir>/<stem>.sepwork/), returning zero on build success. */
static int
run_driver(const char *driver, const struct row *r, const char *dir,
int i, const char *stem, const char *cachetag)
int i, const char *stem)
{
char src[256], cmd[1024];
char src[256], taggedstem[128], cmd[1024];
snprintf(src, sizeof src, "%s/gsret_%d.ww", dir, i);
snprintf(taggedstem, sizeof taggedstem, "%s_%d", stem, i);
FILE *f = fopen(src, "wb");
if (!f) return -1;
wwtest_fputs(r->src, f);
fclose(f);
int werr = ferror(f);
if (fclose(f) != 0 || werr) {
fprintf(stderr, "row[%s]: writing %s failed\n", r->label, src);
return -1;
}
snprintf(cmd, sizeof cmd,
"cd %s && WW_PKGCACHE=%s/pkgc_%s %s build --sep -o %s/%s %s",
dir, dir, cachetag, driver, dir, stem, src);
"cd %s && %s build -S -o %s/%s %s",
dir, driver, dir, taggedstem, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
return -1;
}
char outbin[256];
snprintf(outbin, sizeof outbin, "%s/%s", dir, stem);
return runwait(outbin);
return 0;
}
int
@@ -217,26 +231,26 @@ main(void)
}
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
int total = 0, fail = 0, cleanfail = 0;
for (int i = 0; i < n; i++) {
/* cstage: --sep build + run → <dir>/gsret_c_<i>.sepwork/. */
int gc = run_driver(cdrv, &rows[i], dir, i, "gsret_c", "c");
/* cstage separate build → <dir>/gsret_c_<i>.sepwork/. */
int gc = run_driver(cdrv, &rows[i], dir, i, "gsret_c");
total++;
if (gc != rows[i].want) {
fprintf(stderr, "global_sret_run[cstage][%s]: "
"exit=%d want=%d\n", rows[i].label, gc,
"build=%d want=%d\n", rows[i].label, gc,
rows[i].want);
fail++;
}
/* wwstage: --sep build + run → <dir>/gsret_w_<i>.sepwork/. */
/* wwstage separate build → <dir>/gsret_w_<i>.sepwork/. */
if (have_ww) {
int gw = run_driver(wdrv, &rows[i], dir, i, "gsret_w", "w");
int gw = run_driver(wdrv, &rows[i], dir, i, "gsret_w");
total++;
if (gw != rows[i].want) {
fprintf(stderr, "global_sret_run[wwstage][%s]: "
"exit=%d want=%d\n", rows[i].label, gw,
"build=%d want=%d\n", rows[i].label, gw,
rows[i].want);
fail++;
}
@@ -257,14 +271,25 @@ main(void)
}
char p[640];
snprintf(p, sizeof p, "%s/gsret_%d.ww", dir, i); unlink(p);
snprintf(p, sizeof p, "%s/gsret_%d.ww", dir, i);
/* ENOENT = the row failed before writing the source. */
if (unlink(p) != 0 && errno != ENOENT) {
perror(p);
cleanfail = 1;
}
snprintf(p, sizeof p, "rm -rf %s/gsret_c_%d.sepwork "
"%s/gsret_w_%d.sepwork %s/gsret_c_%d %s/gsret_w_%d "
"%s/pkgc_c %s/pkgc_w", dir, i, dir, i, dir, i, dir, i,
dir, dir);
if (system(p)) {}
"%s/gsret_w_%d.sepwork %s/gsret_c_%d %s/gsret_w_%d",
dir, i, dir, i, dir, i, dir, i);
if (runwait(p) != 0) {
fprintf(stderr, "global_sret_run: cleanup row %d "
"failed\n", i);
cleanfail = 1;
}
}
if (rmdir(dir) != 0) {
perror(dir);
cleanfail = 1;
}
rmdir(dir);
if (!have_ww)
fprintf(stderr, "global_sret_run: skip wwstage (no %s)\n", wdrv);
@@ -274,6 +299,9 @@ main(void)
fail, total);
return 1;
}
/* cleanup diagnostics already printed; must not stay green. */
if (cleanfail)
return 1;
printf("global_sret_run: %d/%d ok\n", total, total);
return 0;
}

View File

@@ -18,6 +18,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -49,21 +50,30 @@ static int
run_row(const char *bin, const char *label, const char *src,
const char *wwerr, int i)
{
char dir[96], srcf[160], outbin[160], wwe[160], ss[160], rm[200], cmd[2048];
snprintf(dir, sizeof dir, "/tmp/aacc_%d_%d", getpid(), i);
mkdir(dir, 0755);
char dir[128], srcf[192], outbin[192], wwe[192], ss[192];
char scratch[208], cmd[2048];
snprintf(dir, sizeof dir, "/tmp/aacc_%d_%d_XXXXXX", getpid(), i);
if (mkdtemp(dir) == NULL) {
fprintf(stderr, "row[%s]: temporary directory acquisition failed\n",
label);
return -1;
}
snprintf(srcf, sizeof srcf, "%s/c.ww", dir);
snprintf(outbin, sizeof outbin, "%s/bin", dir);
snprintf(wwe, sizeof wwe, "%s/ww.err", dir);
snprintf(ss, sizeof ss, "%s/o.s", dir);
snprintf(rm, sizeof rm, "rm -rf %s", dir);
snprintf(scratch, sizeof scratch, "%s.sepwork", outbin);
FILE *f = fopen(srcf, "wb");
if (!f) { runwait(rm); return -1; }
int ok = 1;
if (!f) {
fprintf(stderr, "row[%s]: source setup failed\n", label);
ok = 0;
goto cleanup;
}
fputs(src, f);
fclose(f);
int ok = 1;
snprintf(cmd, sizeof cmd,
"timeout 20 %s/ww build -o %s %s >/dev/null 2>&1", bin, outbin, srcf);
if (runwait(cmd) != 0) {
@@ -80,7 +90,22 @@ run_row(const char *bin, const char *label, const char *src,
label, wwerr, wrc);
ok = 0;
}
runwait(rm);
cleanup:
{
int bad = 0;
snprintf(cmd, sizeof cmd, "rm -rf %s", scratch);
if (runwait(cmd) != 0) bad = 1;
if (unlink(ss) != 0 && errno != ENOENT) bad = 1;
if (unlink(wwe) != 0 && errno != ENOENT) bad = 1;
if (unlink(outbin) != 0 && errno != ENOENT) bad = 1;
if (unlink(srcf) != 0 && errno != ENOENT) bad = 1;
if (rmdir(dir) != 0) bad = 1;
if (bad) {
fprintf(stderr, "row[%s]: temporary cleanup failed\n", label);
ok = 0;
}
}
return ok ? 0 : 1;
}

View File

@@ -14,6 +14,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -31,19 +32,26 @@ runwait(const char *cmd)
static int
run_row(const char *bin, const char *label, const char *src, int i)
{
char dir[96], srcf[160], ss[160], rm[200], cmd[2048];
snprintf(dir, sizeof dir, "/tmp/ab5_%d_%d", getpid(), i);
mkdir(dir, 0755);
char dir[128], srcf[192], ss[192], cmd[2048];
snprintf(dir, sizeof dir, "/tmp/ab5_%d_%d_XXXXXX", getpid(), i);
if (mkdtemp(dir) == NULL) {
fprintf(stderr, "row[%s]: temporary directory acquisition failed\n",
label);
return -1;
}
snprintf(srcf, sizeof srcf, "%s/c.ww", dir);
snprintf(ss, sizeof ss, "%s/o.s", dir);
snprintf(rm, sizeof rm, "rm -rf %s", dir);
FILE *f = fopen(srcf, "wb");
if (!f) { runwait(rm); return -1; }
int ok = 1;
if (!f) {
fprintf(stderr, "row[%s]: source setup failed\n", label);
ok = 0;
goto cleanup;
}
fputs(src, f);
fclose(f);
int ok = 1;
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, ss, srcf);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: cstage frontend compile failed\n", label);
@@ -54,7 +62,18 @@ run_row(const char *bin, const char *label, const char *src, int i)
fprintf(stderr, "row[%s]: wwstage frontend compile failed\n", label);
ok = 0;
}
runwait(rm);
cleanup:
{
int bad = 0;
if (unlink(ss) != 0 && errno != ENOENT) bad = 1;
if (unlink(srcf) != 0 && errno != ENOENT) bad = 1;
if (rmdir(dir) != 0) bad = 1;
if (bad) {
fprintf(stderr, "row[%s]: temporary cleanup failed\n", label);
ok = 0;
}
}
return ok ? 0 : 1;
}

View File

@@ -22,6 +22,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -73,16 +74,28 @@ static const char *experr = "deferred #277";
static int
check(const char *driver, int expect_err, int want)
{
char tmpdir[96], srcp[128], outbin[128], errf[128], rmcmd[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/vc277_%d_%d", getpid(), expect_err);
mkdir(tmpdir, 0755);
char tmpdir[128], srcp[192], outbin[192], errf[192];
char scratch[208], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/vc277_%d_%d_XXXXXX",
getpid(), expect_err);
if (mkdtemp(tmpdir) == NULL) {
fprintf(stderr, "callret_bound277: %s temporary directory "
"acquisition failed\n", driver);
return 1;
}
snprintf(srcp, sizeof srcp, "%s/c.ww", tmpdir);
snprintf(outbin, sizeof outbin, "%s/c", tmpdir);
snprintf(errf, sizeof errf, "%s/err", tmpdir);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
snprintf(scratch, sizeof scratch, "%s.sepwork", outbin);
FILE *f = fopen(srcp, "wb");
if (!f) { runwait(rmcmd); return 1; }
int rc = 0;
if (!f) {
fprintf(stderr, "callret_bound277: %s source setup failed\n",
driver);
rc = 1;
goto cleanup;
}
fputs(src, f);
fclose(f);
@@ -90,7 +103,6 @@ check(const char *driver, int expect_err, int want)
driver, outbin, srcp, errf);
int brc = runwait(cmd);
int rc = 0;
if (expect_err) {
if (brc == 0 || !errlog_has(errf, experr)) {
fprintf(stderr, "callret_bound277: %s expected loud "
@@ -108,7 +120,22 @@ check(const char *driver, int expect_err, int want)
rc = 1;
}
}
runwait(rmcmd);
cleanup:
{
int bad = 0;
snprintf(cmd, sizeof cmd, "rm -rf %s", scratch);
if (runwait(cmd) != 0) bad = 1;
if (unlink(errf) != 0 && errno != ENOENT) bad = 1;
if (unlink(outbin) != 0 && errno != ENOENT) bad = 1;
if (unlink(srcp) != 0 && errno != ENOENT) bad = 1;
if (rmdir(tmpdir) != 0) bad = 1;
if (bad) {
fprintf(stderr, "callret_bound277: %s temporary cleanup "
"failed\n", driver);
rc = 1;
}
}
return rc;
}

View File

@@ -1,5 +1,5 @@
/*
* 946_structparam_run — runtime + byte-id + asm-pattern net for #165, the
* 946_structparam_run — byte-id + asm-pattern net for #165, the
* float-bearing struct-PARAM SysV ABI (the param twin of #171's struct
* RETURN, and the struct counterpart of #163's per-element tuple PARAM).
*
@@ -14,7 +14,7 @@
* NECESSARY-NOT-SUFFICIENT here (cf. 907_f32arg_run, same situation). The
* genuine defect is SysV register-CLASS conformance, observable only in
* the emitted asm (and at a real ABI boundary). The discriminating
* dimension is therefore the asm pattern (c) below.
* dimension is therefore the asm pattern below.
*
* THE FIX: per-EIGHTBYTE SysV classification (structs classify per
* eightbyte, unlike #163's per-element tuples). Each 8-byte eightbyte that
@@ -33,17 +33,17 @@
* f32f32 row asserts BOTH the correct round-tripped value AND the ABSENCE
* of an SSE receive, proving the gate caught it.
*
* Each row carries THREE dimensions:
* (a) cstage `ww build` + run — exit code (end-to-end round-trip).
* (b) w6c vs w6c_ww `.s` cmp — rule-10 byte-id (both stages identical).
* (c) asm-pattern on the CALLEE function's receive: a lone-f64 eightbyte
* The end-to-end values now live in r946_structparam_* fixtures. This carrier
* retains the two observations unavailable to the fixture grammar:
* (a) w6c vs w6c_ww `.s` cmp — rule-10 byte-id (both stages identical).
* (b) asm-pattern on the CALLEE function's receive: a lone-f64 eightbyte
* is received `MOVSD Xn, -off(BP)` (XMM -> slot); a GP eightbyte is
* `MOVQ DI, ...`. The marker is scoped to the callee `main.use` text
* (main's float-literal init also emits `MOVSD X0, -off(BP)`, so a
* whole-file grep would not discriminate). PRESENT for the SSE-routed
* rows, ABSENT for the GP / fallback rows. A pre-fix (or GP-regressed)
* build receives every struct eightbyte via MOVQ -> marker absent,
* which is exactly what (c) catches.
* which is exactly what the pattern check catches.
*/
#include <stdio.h>
#include <stdlib.h>
@@ -64,7 +64,6 @@ runwait(const char *cmd)
struct row {
const char *label;
const char *src;
int want_exit;
int want_sse; /* 1 = callee must receive an f64 eightbyte via XMM */
};
@@ -79,7 +78,7 @@ static const struct row rows[] = {
"\tlet s: pff = pff { a = 3.0, b = 5.0 };\n"
"\tif (use(s) != 8.0) { return 1; };\n"
"\treturn 0;\n"
"};\n", 0, 1 },
"};\n", 1 },
/* struct{f64,i64} — eb0 lone f64 (X0), eb1 pure-INT (DI). Class is
* independent of eightbyte position. (s.a:i64)+s.b = 3+5 = 8. */
{ "f64i64_arg",
@@ -90,7 +89,7 @@ static const struct row rows[] = {
"\tlet s: pfi = pfi { a = 3.0, b = 5 };\n"
"\tif (use(s) != 8) { return 1; };\n"
"\treturn 0;\n"
"};\n", 0, 1 },
"};\n", 1 },
/* struct{i64,f64} — order-swap: eb0 pure-INT (DI), eb1 lone f64 (X0,
* the first float still gets X0). Confirms the float lands in the next
* XMM regardless of position. s.a + (s.b:i64) = 3+5 = 8. */
@@ -102,7 +101,7 @@ static const struct row rows[] = {
"\tlet s: pif = pif { a = 3, b = 5.0 };\n"
"\tif (use(s) != 8) { return 1; };\n"
"\treturn 0;\n"
"};\n", 0, 1 },
"};\n", 1 },
/* GP REGRESSION — struct{i64,i64}: every eightbyte pure-INT, no float
* to route, so it STAYS on the GP transport unchanged (the in-tree
* byte-id case). Callee receives via MOVQ DI/SI -> no SSE marker. */
@@ -114,7 +113,7 @@ static const struct row rows[] = {
"\tlet s: pii = pii { a = 3, b = 5 };\n"
"\tif (use(s) != 8) { return 1; };\n"
"\treturn 0;\n"
"};\n", 0, 0 },
"};\n", 0 },
/* FALLBACK GATE — struct{f32,f32}: two f32 pack into ONE SSE eightbyte
* (8B struct). The gate rejects f32, so the struct stays GP-routed
* (received via MOVQ DI). The value still round-trips (the 8B MOVQ
@@ -129,7 +128,7 @@ static const struct row rows[] = {
"\tlet s: pf32 = pf32 { a = 3.0f32, b = 5.0f32 };\n"
"\tif (use(s) != 8) { return 1; };\n"
"\treturn 0;\n"
"};\n", 0, 0 },
"};\n", 0 },
/* CURSOR INDEPENDENCE — i64 scalar + struct{f64,f64}. n->DI (INTEGER
* cursor), s.a->X0, s.b->X1 (SSE cursor), independent counters.
* n + s.a + s.b = 2+3+5 = 10. SSE receive present. */
@@ -143,8 +142,8 @@ static const struct row rows[] = {
"\tlet s: pff = pff { a = 3.0, b = 5.0 };\n"
"\tif (use(2, s) != 10) { return 1; };\n"
"\treturn 0;\n"
"};\n", 0, 1 },
{ NULL, NULL, 0, 0 }
"};\n", 1 },
{ NULL, NULL, 0 }
};
static int
@@ -177,6 +176,12 @@ callee_receives_sse(const char *path)
if (!f) return -1;
static char buf[1 << 18];
size_t n = fread(buf, 1, sizeof buf - 1, f);
/* a .s larger than the buffer would silently drop the region the
* marker scan needs — refuse instead of scanning a truncated tail. */
if (n == sizeof buf - 1 && fgetc(f) != EOF) {
fclose(f);
return -1;
}
fclose(f);
buf[n] = '\0';
@@ -213,18 +218,21 @@ main(void)
int n = 0, fail = 0;
for (int i = 0; rows[i].src; i++, n++) {
/* src, binary, and both .s files all live under one tmpdir so
/* The source and both .s files live under one tmpdir so
* the compiler's path-derived .sepwork scratch stays inside it;
* a single `rm -rf` at the end of all phases reclaims it. */
char tmpdir[64], src[128], outbin[128];
char tmpdir[64], src[128];
char cs_s[128], ws_s[128], rmcmd[160];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwstp_%d_d_%d",
getpid(), i);
mkdir(tmpdir, 0755);
if (mkdir(tmpdir, 0755) != 0) {
/* not owned — must not rm a path we failed to create */
perror(tmpdir);
fail++;
continue;
}
snprintf(src, sizeof src, "%s/wwstp_%d_%d.ww",
tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/wwstp_%d_%d",
tmpdir, getpid(), i);
snprintf(cs_s, sizeof cs_s, "%s/wwstp_%d_%d_cs.s",
tmpdir, getpid(), i);
snprintf(ws_s, sizeof ws_s, "%s/wwstp_%d_%d_ww.s",
@@ -232,43 +240,25 @@ main(void)
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (f == NULL) { runwait(rmcmd); fail++; continue; }
if (f == NULL) { fail++; goto rowdone; }
fputs(rows[i].src, f);
fclose(f);
char cmd[4096];
/* (a) cstage build + run. */
snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s",
bin, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: cstage build failed\n",
rows[i].label);
fail++;
runwait(rmcmd);
continue;
}
int got = runwait(outbin);
if (got != rows[i].want_exit) {
fprintf(stderr, "row[%s]: cstage exit %d, want %d\n",
rows[i].label, got, rows[i].want_exit);
fail++;
}
/* (b) cs==ww byte-id gate: emit .s from both stages, cmp. */
/* cs==ww byte-id gate: emit .s from both stages, cmp. */
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
w6c, cs_s, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label);
fail++; runwait(rmcmd); continue;
fail++; goto rowdone;
}
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
w6c_ww, ws_s, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww failed\n",
rows[i].label);
fail++; runwait(rmcmd); continue;
fail++; goto rowdone;
}
if (slurp_eq(cs_s, ws_s) != 0) {
fprintf(stderr,
@@ -277,8 +267,7 @@ main(void)
fail++;
}
/* (c) asm-pattern discriminator on the callee receive. byte-id
* (b) proves ww_s mirrors cs_s, so checking cs_s suffices. */
/* byte-id proves ww_s mirrors cs_s, so checking cs_s suffices. */
int sse = callee_receives_sse(cs_s);
if (sse < 0) {
fprintf(stderr, "row[%s]: cannot scan callee .s\n",
@@ -292,14 +281,20 @@ main(void)
rows[i].want_sse ? "present" : "absent");
fail++;
}
runwait(rmcmd);
rowdone:
/* a failed rm must fail the carrier, not leak silently. */
if (runwait(rmcmd) != 0) {
fprintf(stderr, "row[%s]: cleanup rm failed\n",
rows[i].label);
fail++;
}
}
if (fail) {
fprintf(stderr, "%d/%d struct-param tests failed\n", fail, n);
return 1;
}
printf("structparam: %d/%d ok (cstage run + cs==ww byte-id + "
printf("structparam: %d/%d ok (cs==ww byte-id + "
"SSE-receive asm)\n", n, n);
return 0;
}

View File

@@ -1,5 +1,5 @@
/*
* 946_structret_run — runtime + byte-id + asm-pattern net for #171a, the
* 946_structret_run — byte-id + asm-pattern net for #171a, the
* float-bearing struct-RETURN SysV ABI (the return twin of #165's struct
* PARAM, and the struct counterpart of #164's per-element tuple RETURN).
*
@@ -14,7 +14,7 @@
* value check are therefore NECESSARY-NOT-SUFFICIENT here (cf.
* 946_structparam_run, the param twin). The genuine defect is SysV
* register-CLASS conformance, observable only in the emitted asm (and at a
* real ABI boundary). The discriminating dimension is the asm pattern (c).
* real ABI boundary). The discriminating dimension is the asm pattern.
*
* THE FIX: per-EIGHTBYTE SysV classification (reusing struct_float_class /
* structfloatclass verbatim from #165). Each 8-byte eightbyte that is a
@@ -33,10 +33,10 @@
* asserts BOTH the correct round-tripped value AND the ABSENCE of an SSE
* return/receive, proving the gate caught it.
*
* Each row carries THREE dimensions:
* (a) cstage `ww build` + run — exit code (end-to-end round-trip).
* (b) w6c vs w6c_ww `.s` cmp — rule-10 byte-id (both stages identical).
* (c) asm-pattern: the producer `main.mk` returns a lone-f64 eightbyte
* The end-to-end values now live in r946_structret_* fixtures. This carrier
* retains the two observations unavailable to the fixture grammar:
* (a) w6c vs w6c_ww `.s` cmp — rule-10 byte-id (both stages identical).
* (b) asm-pattern: the producer `main.mk` returns a lone-f64 eightbyte
* as `MOVSD <off>(BP), Xn` (scratch -> XMM; a GP eightbyte is
* `MOVQ <off>(BP), AX`), and the consumer `main` receives it as
* `MOVSD Xn, -off(BP)` (XMM -> slot). The producer marker is scoped
@@ -45,7 +45,7 @@
* `TEXT main,` (a GP recv MOVQ's instead). PRESENT for the SSE-routed
* rows, ABSENT for the GP / fallback rows. A pre-fix (or GP-regressed)
* build routes every eightbyte via AX/DX/CX -> both markers absent,
* which is exactly what (c) catches.
* which is exactly what the pattern check catches.
*/
#include <stdio.h>
#include <stdlib.h>
@@ -66,7 +66,6 @@ runwait(const char *cmd)
struct row {
const char *label;
const char *src;
int want_exit;
int want_sse; /* 1 = struct return rides an XMM eightbyte */
};
@@ -82,7 +81,7 @@ static const struct row rows[] = {
"\tlet s: pff = mk();\n"
"\tif ((s.a: i64) + (s.b: i64) != 8) { return 1; };\n"
"\treturn 0;\n"
"};\n", 0, 1 },
"};\n", 1 },
/* CURSOR INDEPENDENCE — struct{f64,i32}: eb0 lone f64 (X0), eb1
* pure-INT (the i32 rides AX, NOT DX — the GP cursor starts at 0
* regardless of the float ahead of it). (s.a:i64)+(s.b:i64) = 8. */
@@ -94,7 +93,7 @@ static const struct row rows[] = {
"\tlet s: pfi = mk();\n"
"\tif ((s.a: i64) + (s.b: i64) != 8) { return 1; };\n"
"\treturn 0;\n"
"};\n", 0, 1 },
"};\n", 1 },
/* ORDER SWAP — struct{i64,f64}: eb0 pure-INT (AX), eb1 lone f64 (X0,
* the first float still gets X0). Confirms the float lands in the
* next XMM regardless of position. s.a + (s.b:i64) = 8. */
@@ -106,7 +105,7 @@ static const struct row rows[] = {
"\tlet s: pif = mk();\n"
"\tif (s.a + (s.b: i64) != 8) { return 1; };\n"
"\treturn 0;\n"
"};\n", 0, 1 },
"};\n", 1 },
/* GP REGRESSION — struct{i64,i64}: every eightbyte pure-INT, no float
* to route, so it STAYS on the AX/DX/CX transport unchanged (the
* in-tree byte-id case). No SSE return/receive marker. */
@@ -118,7 +117,7 @@ static const struct row rows[] = {
"\tlet s: pii = mk();\n"
"\tif (s.a + s.b != 8) { return 1; };\n"
"\treturn 0;\n"
"};\n", 0, 0 },
"};\n", 0 },
/* FALLBACK GATE — struct{f32,f32}: two f32 pack into ONE SSE eightbyte
* (8B struct). The gate rejects f32, so the struct stays GP-routed
* (returned/received via AX). The value still round-trips (the 8B MOVQ
@@ -134,8 +133,8 @@ static const struct row rows[] = {
"\tlet s: pf32 = mk();\n"
"\tif ((s.a: i64) + (s.b: i64) != 8) { return 1; };\n"
"\treturn 0;\n"
"};\n", 0, 0 },
{ NULL, NULL, 0, 0 }
"};\n", 0 },
{ NULL, NULL, 0 }
};
static int
@@ -186,6 +185,12 @@ ret_rides_sse(const char *path)
if (!f) return -1;
static char buf[1 << 18];
size_t n = fread(buf, 1, sizeof buf - 1, f);
/* a .s larger than the buffer would silently drop the region the
* marker scan needs — refuse instead of scanning a truncated tail. */
if (n == sizeof buf - 1 && fgetc(f) != EOF) {
fclose(f);
return -1;
}
fclose(f);
buf[n] = '\0';
@@ -224,19 +229,22 @@ main(void)
int n = 0, fail = 0;
for (int i = 0; rows[i].src; i++, n++) {
/* src + outbin + cs.s + ww.s all live under one tmpdir so the
/* The source and both .s files live under one tmpdir so the
* compiler's .sepwork scratch (derived from the src path) lands
* inside it; a single rm -rf at the end clears every phase. */
char tmpdir[64];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwsrt_%d_d_%d",
getpid(), i);
mkdir(tmpdir, 0755);
if (mkdir(tmpdir, 0755) != 0) {
/* not owned — must not rm a path we failed to create */
perror(tmpdir);
fail++;
continue;
}
char src[128], outbin[128], cs_s[128], ws_s[128], rmcmd[160];
char src[128], cs_s[128], ws_s[128], rmcmd[160];
snprintf(src, sizeof src, "%s/wwsrt_%d_%d.ww",
tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/wwsrt_%d_%d",
tmpdir, getpid(), i);
snprintf(cs_s, sizeof cs_s, "%s/wwsrt_%d_%d_cs.s",
tmpdir, getpid(), i);
snprintf(ws_s, sizeof ws_s, "%s/wwsrt_%d_%d_ww.s",
@@ -244,43 +252,25 @@ main(void)
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (f == NULL) { runwait(rmcmd); fail++; continue; }
if (f == NULL) { fail++; goto rowdone; }
fputs(rows[i].src, f);
fclose(f);
char cmd[4096];
/* (a) cstage build + run. */
snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s",
bin, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: cstage build failed\n",
rows[i].label);
fail++;
runwait(rmcmd);
continue;
}
int got = runwait(outbin);
if (got != rows[i].want_exit) {
fprintf(stderr, "row[%s]: cstage exit %d, want %d\n",
rows[i].label, got, rows[i].want_exit);
fail++;
}
/* (b) cs==ww byte-id gate: emit .s from both stages, cmp. */
/* cs==ww byte-id gate: emit .s from both stages, cmp. */
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
w6c, cs_s, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label);
fail++; runwait(rmcmd); continue;
fail++; goto rowdone;
}
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
w6c_ww, ws_s, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww failed\n",
rows[i].label);
fail++; runwait(rmcmd); continue;
fail++; goto rowdone;
}
if (slurp_eq(cs_s, ws_s) != 0) {
fprintf(stderr,
@@ -289,8 +279,7 @@ main(void)
fail++;
}
/* (c) asm-pattern discriminator on the return/receive. byte-id
* (b) proves ww_s mirrors cs_s, so checking cs_s suffices. */
/* byte-id proves ww_s mirrors cs_s, so checking cs_s suffices. */
int sse = ret_rides_sse(cs_s);
if (sse < 0) {
fprintf(stderr, "row[%s]: cannot scan .s (or SEND/RECV "
@@ -304,14 +293,20 @@ main(void)
rows[i].want_sse ? "present" : "absent");
fail++;
}
runwait(rmcmd);
rowdone:
/* a failed rm must fail the carrier, not leak silently. */
if (runwait(rmcmd) != 0) {
fprintf(stderr, "row[%s]: cleanup rm failed\n",
rows[i].label);
fail++;
}
}
if (fail) {
fprintf(stderr, "%d/%d struct-return tests failed\n", fail, n);
return 1;
}
printf("structret: %d/%d ok (cstage run + cs==ww byte-id + "
printf("structret: %d/%d ok (cs==ww byte-id + "
"SSE-return asm)\n", n, n);
return 0;
}

View File

@@ -7,16 +7,17 @@
* silently-swallowed positional.
* test — -I/-c/-o carry meaning; -l/-L and any unknown flag are
* rejected with "ww test: unknown flag" (rc 2); a lone -I/-o
* is "ww test: -X needs an argument" (rc 2); -c/-o without a
* single test file is "ww test: -c/-o need a single test file"
* (rc 2, #17); a 2nd positional (fnmatch name-filter pattern)
* is "ww test: -X needs an argument" (rc 2); -o without a
* single test file is "ww test: -c/-S/-o need a single test
* file" (rc 2, #17); a missing -run argument is rc 2; a 2nd positional
* in directory mode is "ww test: pattern needs a single test
* file" (rc 2, #17).
* Each row asserts the expected rc + stderr substring AND that the two
* drivers are byte-identical (rule 10): the cstage parse_build_flags /
* do_test must match the wwstage main.ww dobuild/dorun/dotest verbatim.
* This carrier also owns the `-V` output and C/WW-driver version parity.
*
* All rows error during flag parsing, before any compile, so there are no
* All flag rows error during parsing, before any compile, so there are no
* build intermediates to redirect (rule 14: light driver test, any NNN).
* Sibling of 949_missingpkg (driver enforcement) and 993_ww_ww (twin parity).
*/
@@ -25,6 +26,7 @@
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include "ww.h"
struct row {
const char *args; /* argv tail after the driver path */
@@ -37,47 +39,85 @@ static const struct row rows[] = {
{ "build -I", 2, "ww build: -I needs an argument" },
{ "build -L", 2, "ww build: -L needs an argument" },
{ "build -l", 2, "ww build: -l needs an argument" },
{ "build -zz", 2, "ww build: unknown flag" },
{ "run -o", 2, "ww run: -o needs an argument" },
{ "run -l", 2, "ww run: -l needs an argument" },
{ "run -zz", 2, "ww run: unknown flag" },
{ "test -l", 2, "ww test: unknown flag" },
{ "test -zz", 2, "ww test: unknown flag" },
{ "test -I", 2, "ww test: -I needs an argument" },
/* #17: -c (compile-only) + -o are now meaningful for `test`; a lone -o
* needs an arg, and -c/-o require a single test file (not the default
* "." directory enumeration). */
/* Package `test -c` is now valid and covered by native package tests. A
* lone -o needs an argument, while -o still has no directory contract. */
{ "test -o", 2, "ww test: -o needs an argument" },
{ "test -o x", 2, "ww test: -c/-o need a single test file" },
{ "test -c", 2, "ww test: -c/-o need a single test file" },
{ "test -o x", 2, "ww test: -c/-S/-o need a single test file" },
{ "test -run", 2, "ww test: -run needs an argument" },
/* #17: a 2nd positional is a fnmatch name-filter pattern, valid only
* for a single test file/module; in directory mode (target ".") it
* has no single binary to route to and is rejected (rc 2). */
{ "test . zzz", 2, "ww test: pattern needs a single test file" },
};
/* Run "<bin>/<drv> <args>" capturing rc + stderr text into err (NUL-
* terminated). Returns the child's exit code (or -1 on spawn failure). */
static void
read_text(const char *path, char *buf, size_t bufsz)
{
buf[0] = '\0';
FILE *f = fopen(path, "r");
if (f == NULL)
return;
size_t n = fread(buf, 1, bufsz - 1, f);
buf[n] = '\0';
fclose(f);
}
/* Run "<bin>/<drv> <args>" and capture both output streams. */
static int
run_drv(const char *bin, const char *drv, const char *args,
char *err, size_t errsz)
char *out, size_t outsz, char *err, size_t errsz)
{
int pid = getpid();
char errf[64], cmd[2048];
char outf[64], errf[64], cmd[2048];
snprintf(outf, sizeof outf, "/tmp/dfa949_%d.out", pid);
snprintf(errf, sizeof errf, "/tmp/dfa949_%d.err", pid);
snprintf(cmd, sizeof cmd, "%s/%s %s >/dev/null 2>%s", bin, drv, args, errf);
snprintf(cmd, sizeof cmd, "%s/%s %s >%s 2>%s",
bin, drv, args, outf, errf);
int rc = system(cmd);
if (rc == -1) { unlink(errf); return -1; }
rc = WIFEXITED(rc) ? WEXITSTATUS(rc) : 1;
err[0] = '\0';
FILE *f = fopen(errf, "r");
if (f) {
size_t got = fread(err, 1, errsz - 1, f);
err[got] = '\0';
fclose(f);
if (rc == -1) {
unlink(outf);
unlink(errf);
return -1;
}
rc = WIFEXITED(rc) ? WEXITSTATUS(rc) : 1;
read_text(outf, out, outsz);
read_text(errf, err, errsz);
unlink(outf);
unlink(errf);
return rc;
}
static int
test_version(const char *bin)
{
char cout[128], cerr[128], wout[128], werr[128], want[128];
int crc = run_drv(bin, "ww", "-V", cout, sizeof cout,
cerr, sizeof cerr);
int wrc = run_drv(bin, "ww_ww", "-V", wout, sizeof wout,
werr, sizeof werr);
int fail = 0;
snprintf(want, sizeof want, "ww %s\n", WW_VERSION);
if (crc != 0 || strcmp(cout, want) != 0 || cerr[0] != '\0') {
fprintf(stderr, "949 FAIL: ww -V rc=%d stdout='%s' stderr='%s'\n",
crc, cout, cerr);
fail = 1;
}
if (wrc != crc || strcmp(wout, cout) != 0 || strcmp(werr, cerr) != 0) {
fprintf(stderr, "949 FAIL: ww_ww -V rc=%d stdout='%s' stderr='%s' "
"!= ww rc=%d stdout='%s' stderr='%s'\n",
wrc, wout, werr, crc, cout, cerr);
fail = 1;
}
return fail;
}
int
main(void)
{
@@ -91,12 +131,14 @@ main(void)
bin = absbin;
}
int fail = 0;
int fail = test_version(bin);
size_t n = sizeof rows / sizeof rows[0];
for (size_t i = 0; i < n; i++) {
char cerr[4096], werr[4096];
int crc = run_drv(bin, "ww", rows[i].args, cerr, sizeof cerr);
int wrc = run_drv(bin, "ww_ww", rows[i].args, werr, sizeof werr);
char cout[256], cerr[4096], wout[256], werr[4096];
int crc = run_drv(bin, "ww", rows[i].args,
cout, sizeof cout, cerr, sizeof cerr);
int wrc = run_drv(bin, "ww_ww", rows[i].args,
wout, sizeof wout, werr, sizeof werr);
if (crc != rows[i].rc) {
fprintf(stderr, "949 FAIL: ww %s rc=%d want=%d\n",
@@ -119,9 +161,15 @@ main(void)
rows[i].args, werr, cerr);
fail = 1;
}
if (strcmp(wout, cout) != 0) {
fprintf(stderr, "949 FAIL: %s ww_ww stdout '%s' != ww '%s'\n",
rows[i].args, wout, cout);
fail = 1;
}
}
if (fail) return 1;
printf("driver flag-argument error branches: %zu rows, ww==ww_ww pinned\n", n);
printf("driver version + flag-argument errors: %zu rows, ww==ww_ww pinned\n",
n);
return 0;
}

View File

@@ -17,6 +17,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -35,24 +36,28 @@ runwait(const char *cmd)
static int
run_miss(const char *bin)
{
int pid = getpid();
char tmpdir[64], src[128], errf[128], outb[128], cmd[2048];
char rmcmd[160], line[4096];
/* source + binary + intermediates + .sepwork all under one tmpdir so
* the compiler's output-derived .sepwork scratch lands here (not bare
* /tmp where unlink/rmdir would never name it) and the single rm -rf
* reclaims it on every exit path. */
snprintf(tmpdir, sizeof tmpdir, "/tmp/mp949_miss_%d_d", pid);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/mp949_miss_%d.ww", tmpdir, pid);
snprintf(errf, sizeof errf, "%s/mp949_miss_%d.err", tmpdir, pid);
/* -o a writable stem inside tmpdir (not /dev/null): post-T3 the
* intermediate .combined.ww/.s/.o follow -o, so /dev/null would yield
* an uncreatable /dev/null.combined.ww and mask the missing-pkg fatal. */
snprintf(outb, sizeof outb, "%s/mp949_miss_%d.out", tmpdir, pid);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
char tmpdir[] = "/tmp/mp949_miss_XXXXXX";
char src[128], errf[128], outb[128], scratch[144], cmd[2048];
char line[4096];
if (mkdtemp(tmpdir) == NULL) {
fprintf(stderr, "949 FAIL: acquire miss workspace\n");
return 1;
}
/* Source, capture, output, and output-derived .sepwork all live under
* this invocation-owned directory. */
snprintf(src, sizeof src, "%s/miss.ww", tmpdir);
snprintf(errf, sizeof errf, "%s/miss.err", tmpdir);
/* -o needs a writable stem inside tmpdir (not /dev/null): the
* output-derived .sepwork follows -o, so /dev/null would yield an
* uncreatable /dev/null.sepwork and mask the missing-package fatal. */
snprintf(outb, sizeof outb, "%s/miss.out", tmpdir);
snprintf(scratch, sizeof scratch, "%s.sepwork", outb);
int result = 1;
FILE *f = fopen(src, "w");
if (!f) { fprintf(stderr, "949 FAIL: stage miss\n"); runwait(rmcmd); return 1; }
if (!f) {
fprintf(stderr, "949 FAIL: stage miss\n");
goto cleanup;
}
fputs("package main;\n"
"import nosuchpkg;\n"
"export fn main() i32 = { return 0; };\n", f);
@@ -63,8 +68,7 @@ run_miss(const char *bin)
int rc = runwait(cmd);
if (rc == 0) {
fprintf(stderr, "949 FAIL: ww accepted a missing import\n");
runwait(rmcmd);
return 1;
goto cleanup;
}
int found = 0;
f = fopen(errf, "r");
@@ -75,13 +79,28 @@ run_miss(const char *bin)
}
fclose(f);
}
runwait(rmcmd);
if (!found) {
fprintf(stderr, "949 FAIL: no 'cannot find package nosuchpkg' "
"on stderr\n");
return 1;
goto cleanup;
}
return 0;
result = 0;
cleanup:
{
int bad = 0;
snprintf(cmd, sizeof cmd, "rm -rf %s", scratch);
if (runwait(cmd) != 0) bad = 1;
if (unlink(outb) != 0 && errno != ENOENT) bad = 1;
if (unlink(errf) != 0 && errno != ENOENT) bad = 1;
if (unlink(src) != 0 && errno != ENOENT) bad = 1;
if (rmdir(tmpdir) != 0) bad = 1;
if (bad) {
fprintf(stderr, "949 FAIL: cleanup miss workspace\n");
result = 1;
}
}
return result;
}
/* inline branch: a single-file multi-package unit whose `import aa` is
@@ -90,18 +109,23 @@ run_miss(const char *bin)
static int
run_inline(const char *bin)
{
int pid = getpid();
char tmpdir[64], src[128], outb[128], cmd[2048], rmcmd[160];
/* source + binary + .sepwork all under one tmpdir so the compiler's
* source-derived .sepwork scratch lands here (not bare /tmp) and the
* single rm -rf reclaims it. */
snprintf(tmpdir, sizeof tmpdir, "/tmp/mp949_inl_%d_d", pid);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/mp949_inl_%d.ww", tmpdir, pid);
snprintf(outb, sizeof outb, "%s/mp949_inl_%d", tmpdir, pid);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
char tmpdir[] = "/tmp/mp949_inl_XXXXXX";
char src[128], outb[128], scratch[144], cmd[2048];
if (mkdtemp(tmpdir) == NULL) {
fprintf(stderr, "949 FAIL: acquire inline workspace\n");
return 1;
}
/* Source, output, and output-derived .sepwork all live under this
* invocation-owned directory. */
snprintf(src, sizeof src, "%s/inline.ww", tmpdir);
snprintf(outb, sizeof outb, "%s/inline", tmpdir);
snprintf(scratch, sizeof scratch, "%s.sepwork", outb);
int result = 1;
FILE *f = fopen(src, "w");
if (!f) { fprintf(stderr, "949 FAIL: stage inline\n"); runwait(rmcmd); return 1; }
if (!f) {
fprintf(stderr, "949 FAIL: stage inline\n");
goto cleanup;
}
fputs("package aa;\n"
"export fn getv() i32 = { return 7; };\n"
"package main;\n"
@@ -115,16 +139,29 @@ run_inline(const char *bin)
if (rc != 0) {
fprintf(stderr, "949 FAIL: inline-package build failed "
"(inline-skip branch regressed)\n");
runwait(rmcmd);
return 1;
goto cleanup;
}
int got = runwait(outb);
runwait(rmcmd);
if (got != 7) {
fprintf(stderr, "949 FAIL: inline-package exit=%d want=7\n", got);
return 1;
goto cleanup;
}
return 0;
result = 0;
cleanup:
{
int bad = 0;
snprintf(cmd, sizeof cmd, "rm -rf %s", scratch);
if (runwait(cmd) != 0) bad = 1;
if (unlink(outb) != 0 && errno != ENOENT) bad = 1;
if (unlink(src) != 0 && errno != ENOENT) bad = 1;
if (rmdir(tmpdir) != 0) bad = 1;
if (bad) {
fprintf(stderr, "949 FAIL: cleanup inline workspace\n");
result = 1;
}
}
return result;
}
int

View File

@@ -43,14 +43,15 @@
* | — the #117 over-accept hazard) | |
*
* Cross-module via OWN 2-module fixtures in a PRIVATE mktemp dir (784
* model) — NOT the real lib/ascii: the driver writes intermediates next
* to traversed sources, so importing lib/ would race; a synthetic module
* model) — NOT the real lib/ascii: ordinary build retains its caller-owned
* <output>.sepwork tree, so importing lib/ would race; a synthetic module
* `cc` exercises the IDENTICAL `&mod.fn` mangle (mafn(leaf, module)).
* NNN<950 so test-unit runs it; the private mktemp dir makes the ww_ww
* driver race-free (946 ww_ww-driver precedent).
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -164,19 +165,19 @@ static const struct scenario scenarios[] = {
{ "nonfn_dotted_table", nonfn_dotted_files, K_BUILDERR, 0 },
};
/* Build `driver build --sep -o <dir>/<stem>` in the fixture dir; returns
/* Build `driver build -o <dir>/<stem>` in the fixture dir; returns
* the driver's build rc and (for K_RUN) runs the produced binary, comparing
* exit to want_exit. #94: the per-package asm lands in <dir>/<stem>.sepwork/
* (cachetag keys a private WW_PKGCACHE so out/.pkgcache is untouched). */
* exit to want_exit. #94: the caller-visible per-package asm is retained in
* <dir>/<stem>.sepwork/ until this carrier finishes observing it. */
static int
build_run(const char *dir, const char *driver, const struct scenario *sc,
const char *stem, const char *cachetag)
const char *stem)
{
char cmd[4096], path[1024];
snprintf(cmd, sizeof cmd,
"cd %s && WW_PKGCACHE=%s/pkgc_%s %s build --sep -I %s -o %s/%s "
"cd %s && %s build -I %s -o %s/%s "
"%s/main.ww >/dev/null 2>%s/err",
dir, dir, cachetag, driver, dir, dir, stem, dir, dir);
dir, driver, dir, dir, stem, dir, dir);
int brc = runwait(cmd);
if (sc->kind == K_BUILDERR) {
if (brc == 0) {
@@ -217,19 +218,25 @@ run_scenario(const char *bin, const struct scenario *sc)
FILE *f = fopen(path, "wb");
if (!f) { fprintf(stderr, "949[%s]: write %s\n", sc->label,
sc->files[i].name); rc = -1; goto done; }
fputs(sc->files[i].src, f);
fclose(f);
int writefail = fputs(sc->files[i].src, f) == EOF;
if (fclose(f) != 0) writefail = 1;
if (writefail) {
fprintf(stderr, "949[%s]: write %s\n", sc->label,
sc->files[i].name);
rc = -1;
goto done;
}
}
/* cstage driver --sep build (+run for K_RUN) → <dir>/main_c.sepwork/. */
/* cstage driver build (+run for K_RUN) → <dir>/main_c.sepwork/. */
snprintf(csdrv, sizeof csdrv, "%s/ww", bin);
if (build_run(dir, csdrv, sc, "main_c", "c") != 0) { rc = -1; goto done; }
if (build_run(dir, csdrv, sc, "main_c") != 0) { rc = -1; goto done; }
/* wwstage driver --sep build (+run for K_RUN) — race-free in the
/* wwstage driver build (+run for K_RUN) — race-free in the
* private mktemp dir. Direct proof the wwstage emits a runnable
* binary. Distinct stem so its sepwork doesn't clobber cstage's. */
snprintf(wwdrv, sizeof wwdrv, "%s/ww_ww", bin);
if (build_run(dir, wwdrv, sc, "main_w", "w") != 0) { rc = -1; goto done; }
if (build_run(dir, wwdrv, sc, "main_w") != 0) { rc = -1; goto done; }
if (sc->kind == K_BUILDERR) goto done;
@@ -242,10 +249,16 @@ run_scenario(const char *bin, const struct scenario *sc)
snprintf(cs_s, sizeof cs_s, "%s/all_cs.s", dir);
snprintf(ws_s, sizeof ws_s, "%s/all_ww.s", dir);
snprintf(cmd, sizeof cmd, "cat %s/main_c.sepwork/*.s > %s 2>/dev/null",
dir, cs_s); if (system(cmd)) {}
dir, cs_s);
int cs_cat = runwait(cmd);
snprintf(cmd, sizeof cmd, "cat %s/main_w.sepwork/*.s > %s 2>/dev/null",
dir, ws_s); if (system(cmd)) {}
if (slurp_eq(cs_s, ws_s) != 0) {
dir, ws_s);
int ws_cat = runwait(cmd);
if (cs_cat != 0 || ws_cat != 0) {
fprintf(stderr, "949[%s]: could not aggregate retained "
"sepwork asm\n", sc->label);
rc = -1;
} else if (slurp_eq(cs_s, ws_s) != 0) {
fprintf(stderr, "949[%s]: cs.s/ww.s DIFFER (rule-10 "
"byte-id violation)\n", sc->label);
rc = -1;
@@ -253,8 +266,30 @@ run_scenario(const char *bin, const struct scenario *sc)
}
done:
snprintf(cmd, sizeof cmd, "rm -rf %s", dir);
(void)runwait(cmd);
{
int cleanfail = 0;
snprintf(cmd, sizeof cmd, "rm -rf %s/main_c.sepwork", dir);
if (runwait(cmd) != 0) cleanfail = 1;
snprintf(cmd, sizeof cmd, "rm -rf %s/main_w.sepwork", dir);
if (runwait(cmd) != 0) cleanfail = 1;
for (int i = 0; sc->files[i].name; i++) {
snprintf(path, sizeof path, "%s/%s", dir, sc->files[i].name);
if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1;
}
const char *children[] = {
"main_c", "main_w", "err", "all_cs.s", "all_ww.s", NULL
};
for (int i = 0; children[i]; i++) {
snprintf(path, sizeof path, "%s/%s", dir, children[i]);
if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1;
}
if (rmdir(dir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "949[%s]: temporary cleanup failed\n",
sc->label);
rc = -1;
}
}
return rc;
}

View File

@@ -1,5 +1,5 @@
/*
* 951_defdim_struct_run — runtime + byte-id net for #141: a struct
* 951_defdim_struct_run — byte-id net for #141: a struct
* field whose array dimension is a `def` (`[MAX]u8`, MAX a same-module
* def), not an integer literal. BOTH stages were wrong, oppositely:
*
@@ -21,8 +21,8 @@
* ww read a corrupted `end`; post-fix it round-trips and cs==ww byte-id.
* Closes #13's def-dim half (the slice-repeat clause stays open).
*
* cstage `ww build` + run for exit code; w6c vs w6c_ww `.s` cmp for the
* rule-10 byte-id gate. The byte-id leg compiles the `ww build`-produced
* Runtime values live in r951_defdim_* fixtures. This carrier compares the
* `ww build`-produced
* .combined.ww, not the raw src: w6c does no import resolution, so the
* cross-module row (C1, `import os`) only folds once concatenated, and a
* same-module row combines to itself. The last row is the ACTUAL
@@ -33,6 +33,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -46,7 +47,7 @@ runwait(const char *cmd)
return -1;
}
struct row { const char *label; const char *src; int want_exit; };
struct row { const char *label; const char *src; };
static const struct row rows[] = {
/* TEETH: def-dim array FOLLOWED by a scalar; write both, read the
@@ -61,7 +62,7 @@ static const struct row rows[] = {
" s.b[3] = 9u8;\n"
" s.end = 42;\n"
" return s.end;\n"
"};\n", 42 },
"};\n" },
/* the array element itself must survive too — read the last byte
* (pre-fix the overlapping `end` write would clobber it). */
{ "m4_belem",
@@ -73,7 +74,7 @@ static const struct row rows[] = {
" s.b[3] = 200u8;\n"
" s.end = 7;\n"
" return s.b[3]: i32;\n"
"};\n", 200 },
"};\n" },
/* the ~4KB path::buffer shape — def MAX=4095, read the trailing
* scalar after a 4095-byte array. */
{ "m4095_end",
@@ -85,7 +86,7 @@ static const struct row rows[] = {
" s.b[4094] = 5u8;\n"
" s.end = 99;\n"
" return s.end;\n"
"};\n", 99 },
"};\n" },
/* two scalars after the def-dim array — pins the cumulative offset
* (a+c read their written values: 11+22=33). */
{ "twoafter",
@@ -97,7 +98,7 @@ static const struct row rows[] = {
" s.a = 11;\n"
" s.c = 22;\n"
" return s.a + s.c;\n"
"};\n", 33 },
"};\n" },
/* C1 (rob-mandatory): the ACTUAL path::buffer shape — a CROSS-
* MODULE def dimension. `def MAX = os.PATH_MAX - 1` folds an N_DOT
* (os.PATH_MAX) const-ref through eval_def_const's cross-module arm,
@@ -115,8 +116,8 @@ static const struct row rows[] = {
" s.b[4094] = 7u8;\n"
" s.end = 77;\n"
" return s.end;\n"
"};\n", 77 },
{ NULL, NULL, 0 }
"};\n" },
{ NULL, NULL }
};
static int
@@ -162,14 +163,21 @@ main(void)
char src[64];
snprintf(src, sizeof src, "/tmp/wwdds_%d_%d.ww", getpid(), i);
FILE *f = fopen(src, "wb");
if (f == NULL) { fail++; continue; }
if (f == NULL) { perror(src); fail++; continue; }
fputs(rows[i].src, f);
fclose(f);
char tmpdir[64];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwdds_%d_d_%d",
getpid(), i);
mkdir(tmpdir, 0755);
if (mkdir(tmpdir, 0755) != 0) {
/* not owned — must not rm a path we failed to create */
perror(tmpdir);
fail++;
if (unlink(src) != 0 && errno != ENOENT)
perror(src);
continue;
}
char base[64];
const char *b = strrchr(src, '/');
@@ -178,36 +186,16 @@ main(void)
char *dot = strrchr(base, '.');
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
/* #94 sep layout: each driver's --sep build emits the runnable
/* #94 sep layout: each driver's build emits the runnable
* binary at <stem> + per-package asm under <stem>.sepwork/.
* Distinct stems so the wwstage sepwork doesn't clobber cstage's;
* pin WW_PKGCACHE under tmpdir so out/.pkgcache is untouched. A
* pin all outputs stay under tmpdir. A
* cross-module row's `import os` resolves per-package, a same-
* module row is just __root — both compare via the sorted glob. */
char cmd[2048], stem_c[160], stem_w[160];
snprintf(stem_c, sizeof stem_c, "%s/%s_c", tmpdir, base);
snprintf(stem_w, sizeof stem_w, "%s/%s_w", tmpdir, base);
snprintf(cmd, sizeof cmd,
"cd %s && WW_PKGCACHE=%s/pkgc_c %s/ww build --sep -o %s %s",
tmpdir, tmpdir, bin, stem_c, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: cstage build failed\n",
rows[i].label);
fail++;
unlink(src);
snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir);
if (system(cmd)) {}
continue;
}
int got = runwait(stem_c);
if (got != rows[i].want_exit) {
fprintf(stderr, "row[%s]: cstage exit %d, want %d\n",
rows[i].label, got, rows[i].want_exit);
fail++;
}
char cs_s[64], ws_s[64];
snprintf(cs_s, sizeof cs_s, "/tmp/wwdds_%d_%d_cs.s",
getpid(), i);
@@ -215,20 +203,38 @@ main(void)
getpid(), i);
snprintf(cmd, sizeof cmd,
"cd %s && WW_PKGCACHE=%s/pkgc_w %s/ww_ww build --sep -o %s %s "
">/dev/null 2>&1", tmpdir, tmpdir, bin, stem_w, src);
"cd %s && %s/ww build -S -o %s %s",
tmpdir, bin, stem_c, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: cstage build failed\n",
rows[i].label);
fail++;
goto rowdone;
}
snprintf(cmd, sizeof cmd,
"cd %s && %s/ww_ww build -S -o %s %s "
">/dev/null 2>&1", tmpdir, bin, stem_w, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: ww_ww build failed\n",
rows[i].label);
fail++;
} else {
/* a failed cat (e.g. an empty sepwork glob) must fail
* the row — two absent trees would compare equal-empty. */
snprintf(cmd, sizeof cmd,
"cat %s.sepwork/*.s > %s 2>/dev/null", stem_c, cs_s);
if (system(cmd)) {}
int catc = runwait(cmd);
snprintf(cmd, sizeof cmd,
"cat %s.sepwork/*.s > %s 2>/dev/null", stem_w, ws_s);
if (system(cmd)) {}
if (slurp_eq(cs_s, ws_s) != 0) {
int catw = runwait(cmd);
if (catc != 0 || catw != 0) {
fprintf(stderr,
"row[%s]: sepwork .s concat failed "
"(cstage=%d wwstage=%d)\n",
rows[i].label, catc, catw);
fail++;
} else if (slurp_eq(cs_s, ws_s) != 0) {
fprintf(stderr,
"row[%s]: cstage/wwstage .s DIFFER "
"(rule-10 byte-id violation)\n",
@@ -237,9 +243,29 @@ main(void)
}
}
rowdone:
/* a failed cleanup must fail the carrier, not leak silently;
* ENOENT is fine — an early leg may exit before creating the
* concat files. */
snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir);
if (system(cmd)) {}
unlink(src); unlink(cs_s); unlink(ws_s);
int cleanfail = runwait(cmd) != 0;
if (unlink(src) != 0 && errno != ENOENT) {
perror(src);
cleanfail = 1;
}
if (unlink(cs_s) != 0 && errno != ENOENT) {
perror(cs_s);
cleanfail = 1;
}
if (unlink(ws_s) != 0 && errno != ENOENT) {
perror(ws_s);
cleanfail = 1;
}
if (cleanfail) {
fprintf(stderr, "row[%s]: cleanup failed\n",
rows[i].label);
fail++;
}
}
if (fail) {
@@ -247,7 +273,7 @@ main(void)
fail, n);
return 1;
}
printf("defdim_struct: %d/%d ok (cstage run + cs==ww byte-id)\n",
printf("defdim_struct: %d/%d ok (cs==ww byte-id)\n",
n, n);
return 0;
}

View File

@@ -33,6 +33,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -372,42 +373,61 @@ main(void)
int n = 0, fail = 0;
for (int i = 0; rows[i].src; i++, n++) {
char tmpdir[64];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwflt_%d_%d", getpid(), i);
mkdir(tmpdir, 0755);
char rmcmd[128];
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
char src[128];
snprintf(src, sizeof src, "%s/wwflt_%d_%d.ww", tmpdir, getpid(), i);
FILE *f = fopen(src, "wb");
if (f == NULL) { fail++; runwait(rmcmd); continue; }
fputs(rows[i].src, f);
fclose(f);
char outbin[128];
char tmpdir[] = "/tmp/wwflt_XXXXXX";
if (mkdtemp(tmpdir) == NULL) {
fprintf(stderr, "row %d: mkdtemp failed\n", i);
fail++;
continue;
}
int rowfail = 0;
char src[128], outbin[128], cmd[2048];
snprintf(src, sizeof src, "%s/case.ww", tmpdir);
snprintf(outbin, sizeof outbin, "%s/out", tmpdir);
char cmd[2048];
FILE *f = fopen(src, "wb");
if (f == NULL) {
fprintf(stderr, "row %d: source open failed\n", i);
rowfail = 1;
goto cleanup;
}
int writefail = fputs(rows[i].src, f) == EOF;
if (fclose(f) != 0) writefail = 1;
if (writefail) {
fprintf(stderr, "row %d: source write failed\n", i);
rowfail = 1;
goto cleanup;
}
snprintf(cmd, sizeof cmd, "%s/ww build -I %s/lib -o %s %s",
bin, cwd, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row %d: build failed\n src: %s\n",
i, rows[i].src);
fail++;
runwait(rmcmd);
continue;
rowfail = 1;
goto cleanup;
}
int got = runwait(outbin);
if (got != rows[i].want_exit) {
fprintf(stderr, "row %d: exit %d, want %d\n src: %s\n",
i, got, rows[i].want_exit, rows[i].src);
fail++;
rowfail = 1;
}
runwait(rmcmd);
cleanup:
{
int cleanfail = 0;
snprintf(cmd, sizeof cmd, "rm -rf %s/out.sepwork", tmpdir);
if (runwait(cmd) != 0) cleanfail = 1;
if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(tmpdir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "row %d: temporary cleanup failed\n", i);
rowfail = 1;
}
}
if (rowfail) fail++;
}
if (fail) {
fprintf(stderr, "%d/%d floats tests failed\n", fail, n);

View File

@@ -18,6 +18,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -61,19 +62,29 @@ main(void)
return 1;
}
char tmpdir[64], rmcmd[160], srcp[128], errf[128], cmd[2048];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwtuprecv_%d", getpid());
mkdir(tmpdir, 0755);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
char tmpdir[] = "/tmp/wwtuprecv_XXXXXX";
if (mkdtemp(tmpdir) == NULL) {
fprintf(stderr, "tuprecv: mkdtemp failed\n");
return 1;
}
char srcp[128], errf[128], cmd[2048];
snprintf(srcp, sizeof srcp, "%s/ctl_destr.ww", tmpdir);
snprintf(errf, sizeof errf, "%s/err.txt", tmpdir);
FILE *f = fopen(srcp, "wb");
if (f == NULL) { runwait(rmcmd); return 1; }
fputs(src, f);
fclose(f);
int fail = 0;
FILE *f = fopen(srcp, "wb");
if (f == NULL) {
fprintf(stderr, "tuprecv: source open failed\n");
fail++;
goto cleanup;
}
int writefail = fputs(src, f) == EOF;
if (fclose(f) != 0) writefail = 1;
if (writefail) {
fprintf(stderr, "tuprecv: source write failed\n");
fail++;
goto cleanup;
}
/* w6c_ww must compile the construct cleanly AND emit no asserttyped. */
snprintf(cmd, sizeof cmd, "%s -o /dev/null %s 2>%s", w6c_ww, srcp, errf);
@@ -89,7 +100,17 @@ main(void)
fail++;
}
runwait(rmcmd);
cleanup:
{
int cleanfail = 0;
if (unlink(errf) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(srcp) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(tmpdir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "tuprecv: temporary cleanup failed\n");
fail++;
}
}
if (fail) return 1;
printf("tuprecv: asserttyped-absence pin ok (#121)\n");
return 0;

View File

@@ -1,5 +1,5 @@
/*
* 956_modqualdestr_run — runtime + byte-id + stamp regression net for
* 956_modqualdestr_run — byte-id + stamp regression net for
* #6a-A: module-qualified destructure binding types.
*
* THE GAP: the wwstage N_MLET handler (check.ww) backfilled destructure
@@ -23,17 +23,16 @@
* Sibling of 956_tuprecv_f64_run (same-module float destructure, bare
* N_IDENT callee) and 953_f64crossmod_run (single-call module-qualified
* f64). Single-file multi-package form (like 953) so w6c/w6c_ww see the
* cross-module call without -I plumbing. Each row carries three
* dimensions:
* (a) cstage `ww build` + run, asserting the exit code (cstage is the
* reference — correct pre- and post-fix).
* (b) w6c vs w6c_ww `.s` cmp — rule-10 byte-id; pre-fix the float row
* cross-module call without -I plumbing. Runtime values now live in
* r956_modqual_* fixtures. This carrier retains two dimensions:
* (a) w6c vs w6c_ww `.s` cmp — rule-10 byte-id; pre-fix the float row
* diverges (w6c_ww loud-aborts, emits no .s).
* (c) #6a-A stamp gate: w6c_ww must emit no `asserttyped:` diagnostic
* (b) #6a-A stamp gate: w6c_ww must emit no `asserttyped:` diagnostic
* on the destructure binding idents. Pre-fix fires on every use.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -50,7 +49,6 @@ runwait(const char *cmd)
struct row {
const char *label;
const char *src;
int want_exit;
};
static const struct row rows[] = {
@@ -73,7 +71,7 @@ static const struct row rows[] = {
"\tif (g != 9.0) { return 1; };\n"
"\tif (exp != 1i64) { return 2; };\n"
"\treturn 0;\n"
"};\n", 0 },
"};\n" },
/* DOMINANT CLASS-A — module-qualified INTEGER destructure (the
* checked.addXX shape: `let (res, ov) = checked.addi64(a, b)`). cgen's
* structural classifier already routed the integer codegen, so this
@@ -92,7 +90,7 @@ static const struct row rows[] = {
"\tif (res != 15i64) { return 1; };\n"
"\tif (ov) { return 2; };\n"
"\treturn (res: i32);\n"
"};\n", 15 },
"};\n" },
/* MIXED — module-qualified (f64, str) destructure: the float rides
* the SSE cursor (X0), the str rides the integer cursor (AX,DX,CX).
* Confirms the stamp distributes correctly onto a wide-header binding
@@ -111,7 +109,7 @@ static const struct row rows[] = {
"\tif (g != 4.0) { return 1; };\n"
"\tif (s.len != 5) { return 2; };\n"
"\treturn (f: i32);\n"
"};\n", 4 },
"};\n" },
/* CLASS-CLOSURE — NON-destructure cross-module same-leaf shadow. The
* #6a-A fix lives at the exprtype N_CALL root, so it closes the whole
* class, not just the destructure surface (same coverage-trap as the
@@ -135,8 +133,8 @@ static const struct row rows[] = {
"\treturn (r.0: i32) + (r.1: i32);\n"
"};\n"
"package alpha;\n"
"export fn dup() (i64, str) = { return (9i64, \"wrong\"); };\n", 10 },
{ NULL, NULL, 0 }
"export fn dup() (i64, str) = { return (9i64, \"wrong\"); };\n" },
{ NULL, NULL }
};
static int
@@ -180,66 +178,52 @@ main(void)
int n = 0, fail = 0;
for (int i = 0; rows[i].src; i++, n++) {
/* src, outbin, cs.s, ww.s, err all live under one tmpdir so
* the compiler's path-derived .sepwork scratch stays inside it
* and a single rm -rf reclaims everything (V3). */
char tmpdir[64], rmcmd[160];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwmqd_%d_d_%d",
getpid(), i);
mkdir(tmpdir, 0755);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
char src[128];
snprintf(src, sizeof src, "%s/wwmqd_%d_%d.ww",
tmpdir, getpid(), i);
FILE *f = fopen(src, "wb");
if (f == NULL) { fail++; runwait(rmcmd); continue; }
fputs(rows[i].src, f);
fclose(f);
char cmd[2048];
/* (a) cstage build + run. */
char outbin[128];
snprintf(outbin, sizeof outbin, "%s/wwmqd_%d_%d",
tmpdir, getpid(), i);
snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s",
bin, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: cstage build failed\n",
rows[i].label);
/* Every compiler input, output, and capture lives under one
* collision-safe directory owned by this row. */
char tmpdir[] = "/tmp/wwmqd_XXXXXX";
if (mkdtemp(tmpdir) == NULL) {
fprintf(stderr, "row[%s]: mkdtemp failed\n", rows[i].label);
fail++;
runwait(rmcmd);
continue;
}
int got = runwait(outbin);
if (got != rows[i].want_exit) {
fprintf(stderr, "row[%s]: cstage exit %d, want %d\n",
rows[i].label, got, rows[i].want_exit);
char src[128];
snprintf(src, sizeof src, "%s/case.ww", tmpdir);
FILE *f = fopen(src, "wb");
if (f == NULL) {
fprintf(stderr, "row[%s]: source open failed\n", rows[i].label);
fail++;
goto row_cleanup;
}
int writefail = fputs(rows[i].src, f) == EOF;
if (fclose(f) != 0) writefail = 1;
if (writefail) {
fprintf(stderr, "row[%s]: source write failed\n", rows[i].label);
fail++;
goto row_cleanup;
}
/* (b) cs==ww byte-id gate: emit .s from both stages, cmp. */
char cmd[2048];
/* cs==ww byte-id gate: emit .s from both stages, cmp. */
char cs_s[128], ws_s[128];
snprintf(cs_s, sizeof cs_s, "%s/wwmqd_%d_%d_cs.s",
tmpdir, getpid(), i);
snprintf(ws_s, sizeof ws_s, "%s/wwmqd_%d_%d_ww.s",
tmpdir, getpid(), i);
snprintf(cs_s, sizeof cs_s, "%s/cs.s", tmpdir);
snprintf(ws_s, sizeof ws_s, "%s/ww.s", tmpdir);
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
w6c, cs_s, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label);
fail++; runwait(rmcmd); continue;
fail++;
goto row_cleanup;
}
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
w6c_ww, ws_s, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww failed\n",
rows[i].label);
fail++; runwait(rmcmd); continue;
fail++;
goto row_cleanup;
}
if (slurp_eq(cs_s, ws_s) != 0) {
fprintf(stderr,
@@ -248,24 +232,45 @@ main(void)
fail++;
}
/* (c) #6a-A stamp gate: the module-qualified destructure
/* The module-qualified destructure
* binding must carry a checker type stamp, so w6c_ww emits no
* `asserttyped:` diagnostic on its idents. Non-vacuous —
* pre-fix (HEAD) w6c_ww fires asserttyped on every binding use
* (and the float row loud-aborts in cgen above). */
char errf[128];
snprintf(errf, sizeof errf,
"%s/wwmqd_%d_%d_err.txt", tmpdir, getpid(), i);
snprintf(errf, sizeof errf, "%s/err.txt", tmpdir);
snprintf(cmd, sizeof cmd,
"%s -o /dev/null %s 2>%s", w6c_ww, src, errf);
runwait(cmd);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww stamp probe failed\n",
rows[i].label);
fail++;
}
snprintf(cmd, sizeof cmd, "grep -q asserttyped %s", errf);
if (runwait(cmd) == 0) {
fprintf(stderr, "row[%s]: w6c_ww emitted asserttyped "
"(destructure binding unstamped)\n", rows[i].label);
fail++;
}
runwait(rmcmd);
row_cleanup:
{
int cleanfail = 0;
char path[128];
const char *children[] = {
"case.ww", "cs.s", "ww.s", "err.txt", NULL
};
for (int j = 0; children[j]; j++) {
snprintf(path, sizeof path, "%s/%s", tmpdir, children[j]);
if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1;
}
if (rmdir(tmpdir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "row[%s]: temporary cleanup failed\n",
rows[i].label);
fail++;
}
}
}
if (fail) {
@@ -273,7 +278,7 @@ main(void)
fail, n);
return 1;
}
printf("modqualdestr: %d/%d ok (cstage run + cs==ww byte-id + "
printf("modqualdestr: %d/%d ok (cs==ww byte-id + "
"stamp)\n", n, n);
return 0;
}

View File

@@ -20,6 +20,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -146,40 +147,74 @@ main(void)
/* NON-VACUITY self-check: the grep must FIRE on a literal asserttyped
* line. A grep that never matches would pass every row vacuously. */
{
char probe[80], cmd[160];
snprintf(probe, sizeof probe, "/tmp/wwtupf_probe_%d.txt", getpid());
FILE *p = fopen(probe, "wb");
if (!p) { fprintf(stderr, "tuprecv_f64: probe open failed\n"); return 1; }
fputs("error: asserttyped: e.type_ nil\n", p);
fclose(p);
snprintf(cmd, sizeof cmd, "grep -q asserttyped %s", probe);
if (runwait(cmd) != 0) {
fprintf(stderr, "tuprecv_f64: grep self-check FAILED — the "
"asserttyped gate is vacuous\n");
unlink(probe);
char probedir[] = "/tmp/wwtupf_probe_XXXXXX";
if (mkdtemp(probedir) == NULL) {
fprintf(stderr, "tuprecv_f64: probe mkdtemp failed\n");
return 1;
}
unlink(probe);
char probe[128], cmd[256];
snprintf(probe, sizeof probe, "%s/probe.txt", probedir);
int probe_fail = 0;
FILE *p = fopen(probe, "wb");
if (!p) {
fprintf(stderr, "tuprecv_f64: probe open failed\n");
probe_fail = 1;
} else {
int writefail =
fputs("error: asserttyped: e.type_ nil\n", p) == EOF;
if (fclose(p) != 0) writefail = 1;
if (writefail) {
fprintf(stderr, "tuprecv_f64: probe write failed\n");
probe_fail = 1;
} else {
snprintf(cmd, sizeof cmd, "grep -q asserttyped %s", probe);
if (runwait(cmd) != 0) {
fprintf(stderr, "tuprecv_f64: grep self-check FAILED — the "
"asserttyped gate is vacuous\n");
probe_fail = 1;
}
}
}
int cleanfail = 0;
if (unlink(probe) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(probedir) != 0) cleanfail = 1;
if (cleanfail)
fprintf(stderr, "tuprecv_f64: probe cleanup failed\n");
if (probe_fail || cleanfail) return 1;
}
for (int i = 0; rows[i].src; i++, n++) {
char tmpdir[64], src[128], errf[128], rmcmd[160], cmd[2048];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwtupf_%d_d_%d",
getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/wwtupf_%d_%d.ww",
tmpdir, getpid(), i);
char tmpdir[] = "/tmp/wwtupf_XXXXXX";
if (mkdtemp(tmpdir) == NULL) {
fprintf(stderr, "row[%s]: mkdtemp failed\n", rows[i].label);
fail++;
continue;
}
char src[128], errf[128], cmd[2048];
snprintf(src, sizeof src, "%s/case.ww", tmpdir);
snprintf(errf, sizeof errf, "%s/err", tmpdir);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (f == NULL) { runwait(rmcmd); fail++; continue; }
fputs(rows[i].src, f);
fclose(f);
if (f == NULL) {
fprintf(stderr, "row[%s]: source open failed\n", rows[i].label);
fail++;
goto row_cleanup;
}
int writefail = fputs(rows[i].src, f) == EOF;
if (fclose(f) != 0) writefail = 1;
if (writefail) {
fprintf(stderr, "row[%s]: source write failed\n", rows[i].label);
fail++;
goto row_cleanup;
}
snprintf(cmd, sizeof cmd, "%s -o /dev/null %s 2>%s",
w6c_ww, src, errf);
runwait(cmd);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww stamp probe failed\n",
rows[i].label);
fail++;
}
snprintf(cmd, sizeof cmd, "grep -q asserttyped %s", errf);
if (runwait(cmd) == 0) {
fprintf(stderr, "row[%s]: w6c_ww emitted asserttyped "
@@ -187,7 +222,19 @@ main(void)
rows[i].label);
fail++;
}
runwait(rmcmd);
row_cleanup:
{
int cleanfail = 0;
if (unlink(errf) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(tmpdir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "row[%s]: temporary cleanup failed\n",
rows[i].label);
fail++;
}
}
}
if (fail) {

View File

@@ -1,5 +1,5 @@
/*
* 957_assert_builtin_run — runtime + byte-id + reject net for #58: the
* 957_assert_builtin_run — stderr + byte-id net for #58: the
* `assert(cond[, msg])` / `abort([msg])` builtins (the harec
* EXPR_ASSERT family) in BOTH stages.
*
@@ -12,12 +12,9 @@
* cmd/w6c/cgen.c:6618-6663). The fix mirrors that tag-then-lower pair
* into check.ww exprtype N_CALL + cgenexpr.ww cgcall.
*
* THIS TEST MUST CATCH A WWSTAGE-ONLY DIVERGENCE, so every positive
* row carries both dimensions (modelled on 953/954):
* (a) cstage `ww build` + run, asserting the exit code AND the
* runtime stderr (rt_abort writes msg verbatim; a NULL want_msg
* pins the (NULL, 0) no-msg shape as EMPTY stderr).
* (b) w6c vs w6c_ww `.s` cmp — FAILS if the stages diverge. On
* Shared runtime and reject contracts live in r957_* fixtures. This carrier
* retains runtime stderr for aborting rows and w6c vs w6c_ww `.s` identity.
* The latter fails if the stages diverge. On
* pre-fix master every assert/abort row diverges (wwstage emits
* the bogus CALL).
*
@@ -26,18 +23,15 @@
* and routes the regular call path in BOTH stages. The cross-module
* row pins the CURRENT flat-scope semantics — if the #45 root (filed
* as task #14: cross-module bare resolution of a foreign decl) is
* later ruled a reject, that row flips to expect_fail with it.
* later ruled a reject, its fixture should change polarity with it.
*
* Reject rows pin the checker mirror ON CONTENT: non-bool cond /
* non-str msg / arity overflows fail the build in BOTH stages with the
* shared diagnostic substring (cstage prefixes pos, wwstage's cerr is
* bare — strstr covers both). The alias-cond row pins the rule-10
* down-alignment: cstage compares ty_bool by identity (check.c:1560),
* so wwstage must not alias-peel either (widening = task #5 alias arc).
* The migrated reject fixtures retain the checker diagnostics and the
* alias-cond rule-10 down-alignment.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -68,8 +62,7 @@ struct row {
const char *src;
int want_exit;
const char *want_msg; /* run stderr must contain this; NULL = must be empty */
int expect_fail; /* 1 = BOTH stages must reject; want_exit/want_msg ignored */
const char *diag; /* reject rows: both stages' stderr must contain this */
int observe_output;
};
static const struct row rows[] = {
@@ -80,35 +73,35 @@ static const struct row rows[] = {
"\tassert(1 + 1 == 2);\n"
"\tassert(2 > 1, \"never fires\");\n"
"\treturn 42;\n"
"};\n", 42, NULL, 0, NULL },
"};\n", 42, NULL, 0 },
/* Failing assert without msg: rt_abort(NULL, 0), exit 1, silent. */
{ "fail_nomsg",
"package main;\n"
"export fn main() i32 = {\n"
"\tassert(1 == 2);\n"
"\treturn 0;\n"
"};\n", 1, NULL, 0, NULL },
"};\n", 1, NULL, 1 },
/* Failing assert with msg: rt_abort(ptr, len) writes it to stderr. */
{ "fail_msg",
"package main;\n"
"export fn main() i32 = {\n"
"\tassert(false, \"assert fired\\n\");\n"
"\treturn 0;\n"
"};\n", 1, "assert fired", 0, NULL },
"};\n", 1, "assert fired", 1 },
/* abort with msg: unconditional rt_abort, exit 1, msg on stderr. */
{ "abort_msg",
"package main;\n"
"export fn main() i32 = {\n"
"\tabort(\"boom\\n\");\n"
"\treturn 0;\n"
"};\n", 1, "boom", 0, NULL },
"};\n", 1, "boom", 1 },
/* bare abort(): rt_abort(NULL, 0), exit 1, silent. */
{ "abort_bare",
"package main;\n"
"export fn main() i32 = {\n"
"\tabort();\n"
"\treturn 0;\n"
"};\n", 1, NULL, 0, NULL },
"};\n", 1, NULL, 1 },
/* assert inside an IMPORTED module's fn (single-file multi-package
* form, like 953) — the regex fold-5 consumer shape. */
{ "imported_mod",
@@ -119,7 +112,7 @@ static const struct row rows[] = {
"};\n"
"package main;\n"
"import m;\n"
"export fn main() i32 = { return m.f(); };\n", 42, NULL, 0, NULL },
"export fn main() i32 = { return m.f(); };\n", 42, NULL, 0 },
/* SHADOW control, same-module: a user fn `assert` suppresses the
* builtin; the call routes the regular path (no-op fn), so the
* false cond does NOT abort. Exit 7 proves the user fn ran. */
@@ -130,7 +123,7 @@ static const struct row rows[] = {
"export fn main() i32 = {\n"
"\tassert(false);\n"
"\treturn g;\n"
"};\n", 7, NULL, 0, NULL },
"};\n", 7, NULL, 0 },
/* SHADOW control, cross-module (#45 shape, task #14): a foreign
* exported fn `assert` in the flat scope also suppresses the
* builtin; bare `assert(false, ...)` binds it and does NOT abort. */
@@ -142,44 +135,8 @@ static const struct row rows[] = {
"export fn main() i32 = {\n"
"\tassert(false, \"routed to m.assert\");\n"
"\treturn 42;\n"
"};\n", 42, NULL, 0, NULL },
/* Checker rejects, mirrored both stages — pinned on diagnostic
* CONTENT, not just a non-zero exit. */
{ "reject_nonbool",
"package main;\n"
"export fn main() i32 = {\n"
"\tassert(\"not a bool\");\n"
"\treturn 0;\n"
"};\n", 0, NULL, 1, "assert: cond must be bool" },
/* Alias-of-bool cond: ty_bool identity in cstage (check.c:1560)
* → reject; wwstage aligned down (no resolvealias peel). */
{ "reject_alias_cond",
"package main;\n"
"type myb = bool;\n"
"export fn main() i32 = {\n"
"\tlet b: myb = true;\n"
"\tassert(b);\n"
"\treturn 0;\n"
"};\n", 0, NULL, 1, "assert: cond must be bool" },
{ "reject_assert_arity",
"package main;\n"
"export fn main() i32 = {\n"
"\tassert(true, \"m\", \"extra\");\n"
"\treturn 0;\n"
"};\n", 0, NULL, 1, "assert: at most two args" },
{ "reject_abort_nonstr",
"package main;\n"
"export fn main() i32 = {\n"
"\tabort(42);\n"
"\treturn 0;\n"
"};\n", 0, NULL, 1, "abort: message must be str" },
{ "reject_abort_arity",
"package main;\n"
"export fn main() i32 = {\n"
"\tabort(\"a\", \"b\");\n"
"\treturn 0;\n"
"};\n", 0, NULL, 1, "abort: at most one arg" },
{ NULL, NULL, 0, NULL, 0, NULL }
"};\n", 42, NULL, 0 },
{ NULL, NULL, 0, NULL, 0 }
};
static int
@@ -224,105 +181,80 @@ main(void)
char errbuf[4096];
int n = 0, fail = 0;
for (int i = 0; rows[i].src; i++, n++) {
char tmpdir[64], src[128], errf[128], rmcmd[160];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwasrt_%d_d_%d",
getpid(), i);
mkdir(tmpdir, 0755);
char tmpdir[64] = "/tmp/wwasrt_XXXXXX";
if (mkdtemp(tmpdir) == NULL) {
perror("assert_builtin: mkdtemp");
fail++;
continue;
}
char src[128], errf[128], outbin[128], sepwork[160];
char cs_s[128], ws_s[128], rmcmd[192];
snprintf(src, sizeof src, "%s/wwasrt_%d_%d.ww",
tmpdir, getpid(), i);
snprintf(errf, sizeof errf, "%s/wwasrt_%d_%d.err",
tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (f == NULL) { runwait(rmcmd); fail++; continue; }
fputs(rows[i].src, f);
fclose(f);
if (rows[i].expect_fail) {
char cmd[2048];
const char *stage[2] = { w6c, w6c_ww };
const char *sname[2] = { "cstage", "wwstage" };
for (int s = 0; s < 2; s++) {
snprintf(cmd, sizeof cmd,
"%s -o /dev/null %s 2>%s",
stage[s], src, errf);
if (runwait(cmd) == 0) {
fprintf(stderr, "row[%s]: %s accepted, "
"expected reject\n",
rows[i].label, sname[s]);
fail++;
continue;
}
if (slurp(errf, errbuf, sizeof errbuf) < 0 ||
strstr(errbuf, rows[i].diag) == NULL) {
fprintf(stderr, "row[%s]: %s rejected "
"without \"%s\" on stderr\n",
rows[i].label, sname[s],
rows[i].diag);
fail++;
}
}
runwait(rmcmd);
continue;
}
/* (a) cstage build + run in a scratch dir. */
char outbin[128];
snprintf(outbin, sizeof outbin, "%s/wwasrt_%d_%d",
tmpdir, getpid(), i);
char cmd[2048];
snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s "
">/dev/null 2>&1", bin, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: cstage build failed\n",
rows[i].label);
fail++;
runwait(rmcmd);
continue;
}
snprintf(cmd, sizeof cmd, "%s >/dev/null 2>%s", outbin, errf);
int got = runwait(cmd);
if (got != rows[i].want_exit) {
fprintf(stderr, "row[%s]: cstage exit %d, want %d\n",
rows[i].label, got, rows[i].want_exit);
fail++;
}
long elen = slurp(errf, errbuf, sizeof errbuf);
if (rows[i].want_msg) {
if (elen < 0 || strstr(errbuf, rows[i].want_msg) == NULL) {
fprintf(stderr, "row[%s]: run stderr missing "
"\"%s\"\n", rows[i].label,
rows[i].want_msg);
fail++;
}
} else if (elen != 0) {
fprintf(stderr, "row[%s]: run stderr not empty "
"(rt_abort no-msg must be (NULL, 0))\n",
rows[i].label);
fail++;
}
/* (b) cs==ww byte-id gate. */
char cs_s[128], ws_s[128];
snprintf(sepwork, sizeof sepwork, "%s.sepwork", outbin);
snprintf(cs_s, sizeof cs_s, "%s/wwasrt_%d_%d_cs.s",
tmpdir, getpid(), i);
snprintf(ws_s, sizeof ws_s, "%s/wwasrt_%d_%d_ww.s",
tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepwork);
FILE *f = fopen(src, "wb");
if (f == NULL) { fail++; goto cleanup; }
fputs(rows[i].src, f);
fclose(f);
char cmd[2048];
if (rows[i].observe_output) {
snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s "
">/dev/null 2>&1", bin, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: cstage build failed\n",
rows[i].label);
fail++;
goto cleanup;
}
snprintf(cmd, sizeof cmd, "%s >/dev/null 2>%s", outbin, errf);
int got = runwait(cmd);
if (got != rows[i].want_exit) {
fprintf(stderr, "row[%s]: cstage exit %d, want %d\n",
rows[i].label, got, rows[i].want_exit);
fail++;
}
long elen = slurp(errf, errbuf, sizeof errbuf);
if (rows[i].want_msg) {
if (elen < 0 || strstr(errbuf, rows[i].want_msg) == NULL) {
fprintf(stderr, "row[%s]: run stderr missing "
"\"%s\"\n", rows[i].label,
rows[i].want_msg);
fail++;
}
} else if (elen != 0) {
fprintf(stderr, "row[%s]: run stderr not empty "
"(rt_abort no-msg must be (NULL, 0))\n",
rows[i].label);
fail++;
}
}
/* cs==ww byte-id gate. */
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
w6c, cs_s, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label);
fail++; runwait(rmcmd); continue;
fail++;
goto cleanup;
}
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
w6c_ww, ws_s, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww failed\n",
rows[i].label);
fail++; runwait(rmcmd); continue;
fail++;
goto cleanup;
}
if (slurp_eq(cs_s, ws_s) != 0) {
fprintf(stderr,
@@ -330,7 +262,22 @@ main(void)
"byte-id violation)\n", rows[i].label);
fail++;
}
runwait(rmcmd);
cleanup: {
int cleanbad = 0;
if (unlink(src) != 0 && errno != ENOENT) cleanbad = 1;
if (unlink(errf) != 0 && errno != ENOENT) cleanbad = 1;
if (unlink(outbin) != 0 && errno != ENOENT) cleanbad = 1;
if (unlink(cs_s) != 0 && errno != ENOENT) cleanbad = 1;
if (unlink(ws_s) != 0 && errno != ENOENT) cleanbad = 1;
if (runwait(rmcmd) != 0) cleanbad = 1;
if (rmdir(tmpdir) != 0) cleanbad = 1;
if (cleanbad) {
fprintf(stderr, "row[%s]: temporary cleanup failed\n",
rows[i].label);
fail++;
}
}
}
if (fail) {
@@ -338,7 +285,7 @@ main(void)
fail, n);
return 1;
}
printf("assert_builtin: %d/%d ok (cstage run+stderr + cs==ww byte-id + reject diags)\n",
printf("assert_builtin: %d/%d ok (stderr + cs==ww byte-id)\n",
n, n);
return 0;
}

View File

@@ -22,6 +22,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -98,23 +99,24 @@ main(void)
int n = 0, fail = 0;
for (int i = 0; rows[i].src; i++, n++) {
char tmpdir[64];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwslim_%d_%d", getpid(), i);
mkdir(tmpdir, 0755);
char rmcmd[128];
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
char tmpdir[64] = "/tmp/wwslim_XXXXXX";
if (mkdtemp(tmpdir) == NULL) {
perror("types_sizelim: mkdtemp");
fail++;
continue;
}
char src[128];
char src[128], outbin[128], sepwork[160], rmcmd[192];
snprintf(src, sizeof src, "%s/wwslim_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/out", tmpdir);
snprintf(sepwork, sizeof sepwork, "%s.sepwork", outbin);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepwork);
FILE *f = fopen(src, "wb");
if (f == NULL) { fail++; runwait(rmcmd); continue; }
if (f == NULL) { fail++; goto cleanup; }
fputs(rows[i].src, f);
fclose(f);
char outbin[128];
snprintf(outbin, sizeof outbin, "%s/out", tmpdir);
char cmd[2048];
snprintf(cmd, sizeof cmd, "%s/ww build -I %s/lib -o %s %s",
bin, cwd, outbin, src);
@@ -122,8 +124,7 @@ main(void)
fprintf(stderr, "row %d: build failed\n src: %s\n",
i, rows[i].src);
fail++;
runwait(rmcmd);
continue;
goto cleanup;
}
int got = runwait(outbin);
@@ -132,7 +133,18 @@ main(void)
i, got, rows[i].want_exit, rows[i].src);
fail++;
}
runwait(rmcmd);
cleanup: {
int cleanbad = 0;
if (unlink(src) != 0 && errno != ENOENT) cleanbad = 1;
if (unlink(outbin) != 0 && errno != ENOENT) cleanbad = 1;
if (runwait(rmcmd) != 0) cleanbad = 1;
if (rmdir(tmpdir) != 0) cleanbad = 1;
if (cleanbad) {
fprintf(stderr, "row %d: temporary cleanup failed\n", i);
fail++;
}
}
}
if (fail) {
fprintf(stderr, "%d/%d types_sizelim tests failed\n", fail, n);

View File

@@ -19,6 +19,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -97,24 +98,24 @@ main(void)
int n = 0, fail = 0;
for (int i = 0; rows[i].src; i++, n++) {
char tmpdir[64];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwilim_%d_%d", getpid(), i);
mkdir(tmpdir, 0755);
char tmpdir[64] = "/tmp/wwilim_XXXXXX";
if (mkdtemp(tmpdir) == NULL) {
perror("types_intlim: mkdtemp");
fail++;
continue;
}
char rmcmd[128];
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
char src[128];
char src[128], outbin[128], sepwork[160], rmcmd[192];
snprintf(src, sizeof src, "%s/wwilim_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/out", tmpdir);
snprintf(sepwork, sizeof sepwork, "%s.sepwork", outbin);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepwork);
FILE *f = fopen(src, "wb");
if (f == NULL) { fail++; runwait(rmcmd); continue; }
if (f == NULL) { fail++; goto cleanup; }
fputs(rows[i].src, f);
fclose(f);
char outbin[128];
snprintf(outbin, sizeof outbin, "%s/out", tmpdir);
char cmd[2048];
snprintf(cmd, sizeof cmd, "%s/ww build -I %s/lib -o %s %s",
bin, cwd, outbin, src);
@@ -122,8 +123,7 @@ main(void)
fprintf(stderr, "row %d: build failed\n src: %s\n",
i, rows[i].src);
fail++;
runwait(rmcmd);
continue;
goto cleanup;
}
int got = runwait(outbin);
@@ -132,7 +132,18 @@ main(void)
i, got, rows[i].want_exit, rows[i].src);
fail++;
}
runwait(rmcmd);
cleanup: {
int cleanbad = 0;
if (unlink(src) != 0 && errno != ENOENT) cleanbad = 1;
if (unlink(outbin) != 0 && errno != ENOENT) cleanbad = 1;
if (runwait(rmcmd) != 0) cleanbad = 1;
if (rmdir(tmpdir) != 0) cleanbad = 1;
if (cleanbad) {
fprintf(stderr, "row %d: temporary cleanup failed\n", i);
fail++;
}
}
}
if (fail) {
fprintf(stderr, "%d/%d types_intlim tests failed\n", fail, n);

View File

@@ -1,75 +1,20 @@
/*
* 961_opaque_guards — #108 sub-fold (b): the opaque USE-GUARDS.
* 961_opaque_guards — C-stage-only guards for the unsized opaque type.
*
* #108(a) made `opaque` an abstract, UNSIZED type (size = align =
* SIZE_UNDEFINED = (u64)-1), legal only behind indirection (`*opaque`
* 8B, `[]opaque` 24B header). That opened a footgun: a bare use of
* opaque where a concrete byte size is needed would fabricate a
* (u64)-1-byte slot — a silent miscompile (rule 7). This fold makes
* every such use a LOUD compile error.
* opaque is legal behind indirection (*opaque is 8 bytes and []opaque is a
* 24-byte header), but it cannot appear by value or be indexed through a
* slice. The WW frontend currently accepts the eight programs below because
* it does not validate those construction and binding sites. That polarity
* cannot be represented by the both-stage fixture protocol, so this carrier
* requires each program to fail with the C-stage driver.
*
* opaque is illegal by-value in FOUR aggregate positions (array elem,
* struct field, tuple member, tagged-union variant) + as a bare value,
* under size/align, and as a []opaque element-index. The guards (cstage
* cmd/wcc/check.c), each a build-fails row:
*
* guard | misuse | message
* ------+--------------------------------+---------------------------------
* 1 | bare local `let x: opaque` | unsized type 'opaque' cannot be a
* | param `fn f(x: opaque)` | variable / a parameter /
* | return-by-value `fn f() opaque` | a return type
* 2 | struct field `{ x: opaque }` | ... cannot be a struct field
* 3 | array elem `[N]opaque` | ... cannot be an array element
* 3t | tuple member `(opaque, i32)` | ... cannot be a tuple member
* 3u | tagged variant `(opaque|i32)` | ... cannot be a tagged union member
* 4 | size(opaque) / align(opaque) | cannot take size/align of unsized
* | | type 'opaque'
* 5 | indexing `s[i]`, s: []opaque | cannot index []opaque: element
* | | type 'opaque' has undefined size
*
* Nested aggregates (size([4]opaque), size(struct{x:opaque}),
* size((opaque,i32))) are caught transitively: the cstage rejects the
* inner aggregate at its own construction, and the wwstage size/align
* fold detects them via a RECURSIVE astunsized (an aggregate is unsized
* iff any member is). The tuple/tagged size() rows are the exact
* gate-blind miscompile the first #108(b) attempt (c9e98ca) left open —
* size((opaque,i32)) built and folded to 3.
*
* Detection is via the SIZE_UNDEFINED sentinel (the size/align the guard
* consults), so the legal sized forms `*opaque` (8B) and `[]opaque` (24B
* header) pass untouched — positive rows below + the 960 probe pin that.
* Mirrors harec's scattered `size == SIZE_UNDEFINED` guards
* (ref/harec/src/check.c:1524 binding, :3931 return-by-value, :2720
* size-of, :384 slice-index; ref/harec/src/type_store.c:1147 tuple
* member / :449 tagged variant; field/array at the type-construction
* sites).
*
* Stage placement (rule 10, per-guard — see also the worker report):
* - Guards 1/2/3/3t/3u/5 are CSTAGE-ONLY. The wwstage check.ww is an
* AST-level approximation: it has no binding-size computation (g1),
* no type-decl field/element/member validation walk (g2/g3/3t/3u),
* and its N_INDEX `indexresult` returns the element type without
* consulting its size and is documented to defer invalid-index
* rejection to the cstage (g5). Adding twins there would mean
* building check-sites the leaner stage doesn't have — same
* cstage-only precedent as 712_redecl / 708_param_shadow_mod.
* - Guard 4 is BOTH-STAGES. The wwstage HAS the size()/align() fold
* (exprtype + astsize/astalign), which would otherwise fold opaque
* (and any opaque-containing aggregate) to a bogus 0 (a silent
* miscompile); the twin is a RECURSIVE astunsized + deffolderr,
* since the wwstage lacks the cstage's per-construction guards and
* so its fold alone must detect tuple/tagged/nested opaque uses.
* Verified by hand on w6c_ww / wwdump_ww (this cstage-driver test
* does not exercise the wwstage, like 712/960).
*
* opaque is unused by the bootstrap, so every guard is inert on the
* selfhost corpus — 990-997 stay byte-identical.
*
* Driven like 712_redecl: kind==0 rows must FAIL to build; kind==1 rows
* must build AND exit with `want`.
* Both-stage size/align rejections, including nested aggregates, are owned by
* the six r961_neg_* fixtures. The sized pointer and slice controls are
* owned by r961_pos_ptr_opaque and r961_pos_slice_opaque.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -83,196 +28,112 @@ runwait(const char *cmd)
return -1;
}
/*
* kind == 0: negative — build must fail (any nonzero exit).
* kind == 1: positive — build must succeed AND binary exits with `want`.
*/
struct row { const char *label; int kind; const char *src; int want; };
struct row { const char *label; const char *src; };
static const struct row rows[] = {
/* guard 1 — bare local. */
{ "neg_bare_local", 0,
{ "neg_bare_local",
"package main;\n"
"export fn main() i32 = {\n"
" let x: opaque;\n"
" return 0;\n"
"};\n",
0 },
"\tlet x: opaque;\n"
"\treturn 0;\n"
"};\n" },
/* guard 1 — by-value parameter. */
{ "neg_param", 0,
{ "neg_param",
"package main;\n"
"fn f(x: opaque) i32 = { return 0; };\n"
"export fn main() i32 = { return 0; };\n",
0 },
"export fn main() i32 = { return 0; };\n" },
/* guard 1 — return-by-value. */
{ "neg_return", 0,
/*
* A declaration isolates the return-type guard. A function body returning
* 0 would add an unrelated untyped_int-to-opaque rejection.
*/
{ "neg_return",
"package main;\n"
"fn f() opaque = { return 0; };\n"
"export fn main() i32 = { return 0; };\n",
0 },
"fn f() opaque;\n"
"export fn main() i32 = { return 0; };\n" },
/* guard 2 — opaque struct field. */
{ "neg_struct_field", 0,
{ "neg_struct_field",
"package main;\n"
"type S = struct { x: opaque };\n"
"export fn main() i32 = { return 0; };\n",
0 },
"export fn main() i32 = { return 0; };\n" },
/* guard 3 — [N]opaque array element. */
{ "neg_array_elem", 0,
{ "neg_array_elem",
"package main;\n"
"export fn main() i32 = {\n"
" let a: [4]opaque;\n"
" return 0;\n"
"};\n",
0 },
"\tlet a: [4]opaque;\n"
"\treturn 0;\n"
"};\n" },
/* guard 4 — size(opaque). */
{ "neg_size_of", 0,
"package main;\n"
"export fn main() i32 = { return size(opaque): i32; };\n",
0 },
/* guard 4 — align(opaque). */
{ "neg_align_of", 0,
"package main;\n"
"export fn main() i32 = { return align(opaque): i32; };\n",
0 },
/* guard 5 — indexing a []opaque (legal sized header, unsized elem).
* The result is cast to i32 so this trips ONLY the index guard, not
* the bare-local guard. */
{ "neg_slice_index", 0,
/*
* The cast keeps this row on the slice-index guard rather than creating
* a bare opaque local.
*/
{ "neg_slice_index",
"package main;\n"
"export fn main() i32 = {\n"
" let s: []opaque;\n"
" let v: i32 = s[0]: i32;\n"
" return v;\n"
"};\n",
0 },
"\tlet s: []opaque;\n"
"\tlet v: i32 = s[0]: i32;\n"
"\treturn v;\n"
"};\n" },
/* guard tuple — opaque as a tuple member. The tuple type is rejected
* at construction (cstage type_store.c:1147); the wwstage twin folds
* it via the recursive astunsized. */
{ "neg_tuple_member", 0,
{ "neg_tuple_member",
"package main;\n"
"export fn main() i32 = {\n"
" let t: (opaque, i32);\n"
" return 0;\n"
"};\n",
0 },
"\tlet t: (opaque, i32);\n"
"\treturn 0;\n"
"};\n" },
/* guard tuple — size((opaque, i32)). This is the exact gate-blind
* miscompile c9e98ca left: it built + folded to 3. Now rejected. */
{ "neg_size_tuple", 0,
"package main;\n"
"export fn main() i32 = { return size((opaque, i32)): i32; };\n",
0 },
/* guard tagged — opaque as a tagged-union variant (an unsized variant
* has no payload slot; cstage type_store.c:449). */
{ "neg_tagged_variant", 0,
{ "neg_tagged_variant",
"package main;\n"
"export fn main() i32 = {\n"
" let x: (opaque | i32);\n"
" return 0;\n"
"};\n",
0 },
/* guard tagged — size((opaque | i32)). */
{ "neg_size_tagged", 0,
"package main;\n"
"export fn main() i32 = { return size((opaque | i32)): i32; };\n",
0 },
/* nested — size([4]opaque): the unsized leaf is one level down. Proves
* the wwstage's astunsized descends (cstage rejects at array constr). */
{ "neg_size_nested_array", 0,
"package main;\n"
"export fn main() i32 = { return size([4]opaque): i32; };\n",
0 },
/* nested — size(struct { x: opaque }): an unsized field one level down. */
{ "neg_size_nested_struct", 0,
"package main;\n"
"export fn main() i32 = { return size(struct { x: opaque }): i32; };\n",
0 },
/* pos control — `*opaque` is sized (8B): a local, a param, a return,
* and size()/align() of it all compile. Round-trips a real *i32. */
{ "pos_ptr_opaque", 1,
"package main;\n"
"fn id(p: *opaque) *opaque = { return p; };\n"
"export fn main() i32 = {\n"
" let n: i32 = 42;\n"
" let po: *opaque = (&n): *opaque;\n"
" let back: *i32 = id(po): *i32;\n"
" let w: i32 = size(*opaque): i32;\n"
" if (w != 8) { return 1; };\n"
" return *back;\n"
"};\n",
42 },
/* pos control — `[]opaque` is sized (24B header): a local + size()
* of it compile. size([]opaque) == 24. */
{ "pos_slice_opaque", 1,
"package main;\n"
"export fn main() i32 = {\n"
" let s: []opaque;\n"
" let h: i32 = size([]opaque): i32;\n"
" if (h != 24) { return 1; };\n"
" return s.len: i32 + 7;\n"
"};\n",
7 },
"\tlet x: (opaque | i32);\n"
"\treturn 0;\n"
"};\n" },
};
static int
run_row(const char *driver, const struct row *r, int i)
{
char src[128], tmpdir[128], outbin[128], rmcmd[160], cmd[2048];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wcopg_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
char tmpdir[64] = "/tmp/wcopg_XXXXXX";
if (mkdtemp(tmpdir) == NULL) {
perror("opaque-guard: mkdtemp");
return -1;
}
char src[128], outbin[128], sepwork[160], rmcmd[192], cmd[2048];
snprintf(src, sizeof src, "%s/wcopg_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/wcopg_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
snprintf(sepwork, sizeof sepwork, "%s.sepwork", outbin);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepwork);
int result = -1;
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
if (!f) goto cleanup;
fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s build -o %s %s >/dev/null 2>&1",
driver, outbin, src);
int rc = runwait(cmd);
int bad = (rc == 0);
if (bad) {
fprintf(stderr, "opaque-guard[%s]: build unexpectedly succeeded\n",
r->label);
}
result = bad ? -1 : 0;
if (r->kind == 0) {
/* Negative — build must fail. */
int bad = (rc == 0);
if (bad) {
fprintf(stderr,
"opaque-guard[%s]: build unexpectedly succeeded\n",
r->label);
}
runwait(rmcmd);
return bad ? -1 : 0;
cleanup: {
int cleanbad = 0;
if (unlink(src) != 0 && errno != ENOENT) cleanbad = 1;
if (unlink(outbin) != 0 && errno != ENOENT) cleanbad = 1;
if (runwait(rmcmd) != 0) cleanbad = 1;
if (rmdir(tmpdir) != 0) cleanbad = 1;
if (cleanbad) {
fprintf(stderr, "opaque-guard[%s]: temporary cleanup failed\n",
r->label);
result = -1;
}
/* Positive — build then run. */
if (rc != 0) {
fprintf(stderr, "opaque-guard[%s]: build failed\n", r->label);
runwait(rmcmd);
return -1;
}
int got = runwait(outbin);
runwait(rmcmd);
if (got != r->want) {
fprintf(stderr, "opaque-guard[%s]: exit=%d want=%d\n",
r->label, got, r->want);
return -1;
}
return 0;
return result;
}
int

View File

@@ -27,6 +27,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -142,32 +143,31 @@ main(void)
int n = 0, fail = 0;
for (int i = 0; rows[i].src; i++, n++) {
char tmpdir[64];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwsort_%d_%d", getpid(), i);
mkdir(tmpdir, 0755);
char tmpdir[64] = "/tmp/wwsort_XXXXXX";
if (mkdtemp(tmpdir) == NULL) {
perror("sort: mkdtemp");
fail++;
continue;
}
char rmcmd[128];
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
char src[128];
char src[128], outbin[128], sepwork[160], rmcmd[192];
snprintf(src, sizeof src, "%s/wwsort_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/out", tmpdir);
snprintf(sepwork, sizeof sepwork, "%s.sepwork", outbin);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepwork);
FILE *f = fopen(src, "wb");
if (f == NULL) { fail++; runwait(rmcmd); continue; }
if (f == NULL) { fail++; goto cleanup; }
fputs(rows[i].src, f);
fclose(f);
char outbin[128];
snprintf(outbin, sizeof outbin, "%s/out", tmpdir);
char cmd[2048];
snprintf(cmd, sizeof cmd, "%s/ww build -I %s/lib -o %s %s",
bin, cwd, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build failed\n", rows[i].label);
fail++;
runwait(rmcmd);
continue;
goto cleanup;
}
int got = runwait(outbin);
@@ -176,7 +176,19 @@ main(void)
rows[i].label, got, rows[i].want_exit);
fail++;
}
runwait(rmcmd);
cleanup: {
int cleanbad = 0;
if (unlink(src) != 0 && errno != ENOENT) cleanbad = 1;
if (unlink(outbin) != 0 && errno != ENOENT) cleanbad = 1;
if (runwait(rmcmd) != 0) cleanbad = 1;
if (rmdir(tmpdir) != 0) cleanbad = 1;
if (cleanbad) {
fprintf(stderr, "row[%s]: temporary cleanup failed\n",
rows[i].label);
fail++;
}
}
}
if (fail) {
fprintf(stderr, "%d/%d sort tests failed\n", fail, n);

View File

@@ -1,24 +1,23 @@
/*
* 975_dirs_run — execute the lib/dirs @test fixture under the C-side
* `ww run` driver and assert exit 0.
* `ww test` driver and assert exit 0.
*
* Workflow:
* 1. mkdtemp /tmp/wwdirs-XXXXXX — fresh per run, no cross-run
* stale state, no parallel-test conflicts.
* 2. setenv the four cohort env states (see lib/dirs/dirstest.ww
* file header for the contract).
* 3. exec `ww run lib/dirs/dirstest.ww`. The fixture reads
* 3. exec `ww test lib/dirs/dirstest.ww`. The fixture reads
* WW_TEST_TMPDIR + the XDG_/HOME envs and asserts.
* 4. system("rm -rf <tmpl>") to reclaim the tmpdir + the
* auto-created child dirs (dirs.* mkdir-recursive on the way
* down, so the directory tree is non-empty by test exit).
* 4. Remove the exact auto-created child dirs bottom-up, then the
* mkdtemp-owned root.
*
* Same wrapper shape as 970-974; cleanup-on-failure is rm-rf'd so a
* test failure never leaves residue in /tmp.
* Same wrapper shape as 970-974; cleanup runs on success and failure.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
@@ -54,7 +53,7 @@ main(void)
return 1;
}
/* Pre-arrange env. The child `ww run` inherits these. */
/* Pre-arrange env. The child `ww test` inherits these. */
char cfg[128];
snprintf(cfg, sizeof cfg, "%s/cfg", tmpl);
setenv("HOME", tmpl, 1);
@@ -70,15 +69,39 @@ main(void)
snprintf(cmd, sizeof cmd, "%s/ww test %s", bin, path);
int rc = runwait(cmd);
/* Cleanup regardless of pass/fail — no residue under /tmp. */
char rmcmd[256];
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpl);
(void)system(rmcmd);
int fail = 0;
if (rc != 0) {
fprintf(stderr, "dirs_run FAIL: %s exited %d\n", src, rc);
return 1;
fail = 1;
}
/* dirs.* recursively creates these exact directory chains. */
char p[256];
int cleanbad = 0;
snprintf(p, sizeof p, "%s/cfg/myapp", tmpl);
if (rmdir(p) != 0 && errno != ENOENT) cleanbad = 1;
snprintf(p, sizeof p, "%s/cfg", tmpl);
if (rmdir(p) != 0 && errno != ENOENT) cleanbad = 1;
snprintf(p, sizeof p, "%s/.cache/myapp", tmpl);
if (rmdir(p) != 0 && errno != ENOENT) cleanbad = 1;
snprintf(p, sizeof p, "%s/.cache", tmpl);
if (rmdir(p) != 0 && errno != ENOENT) cleanbad = 1;
snprintf(p, sizeof p, "%s/.local/share/myapp", tmpl);
if (rmdir(p) != 0 && errno != ENOENT) cleanbad = 1;
snprintf(p, sizeof p, "%s/.local/share", tmpl);
if (rmdir(p) != 0 && errno != ENOENT) cleanbad = 1;
snprintf(p, sizeof p, "%s/.local/state/myapp", tmpl);
if (rmdir(p) != 0 && errno != ENOENT) cleanbad = 1;
snprintf(p, sizeof p, "%s/.local/state", tmpl);
if (rmdir(p) != 0 && errno != ENOENT) cleanbad = 1;
snprintf(p, sizeof p, "%s/.local", tmpl);
if (rmdir(p) != 0 && errno != ENOENT) cleanbad = 1;
if (rmdir(tmpl) != 0) cleanbad = 1;
if (cleanbad) {
fprintf(stderr, "dirs_run FAIL: temporary cleanup failed\n");
fail = 1;
}
if (fail) return 1;
printf("dirs_run: %s ok\n", src);
return 0;
}

View File

@@ -15,6 +15,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -43,15 +44,24 @@ static const char *PROG =
static int
run_stage(const char *name, const char *driver)
{
char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[2048];
snprintf(tmpdir, sizeof tmpdir, "/tmp/dtl_%d_d", getpid());
mkdir(tmpdir, 0755);
char tmpdir[64] = "/tmp/dtl_XXXXXX";
if (mkdtemp(tmpdir) == NULL) {
perror("dirs_toolong: mkdtemp");
return 1;
}
char src[128], outbin[128], sepwork[160], rmcmd[192], cmd[2048];
char shorthome[128], shortcfg[160], shortprog[192];
snprintf(src, sizeof src, "%s/dtl_%d.ww", tmpdir, getpid());
snprintf(outbin, sizeof outbin, "%s/dtl_%d", tmpdir, getpid());
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
snprintf(sepwork, sizeof sepwork, "%s.sepwork", outbin);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepwork);
snprintf(shorthome, sizeof shorthome, "%s/home", tmpdir);
snprintf(shortcfg, sizeof shortcfg, "%s/.config", shorthome);
snprintf(shortprog, sizeof shortprog, "%s/prog", shortcfg);
int fail = 0;
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return 1; }
if (!f) { fail = 1; goto cleanup; }
fputs(PROG, f);
fclose(f);
@@ -59,16 +69,14 @@ run_stage(const char *name, const char *driver)
driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "dirs_toolong[%s]: build failed\n", name);
runwait(rmcmd);
return 1;
fail = 1;
goto cleanup;
}
int fail = 0;
/* short HOME — the normal path, must still exit 0. */
snprintf(cmd, sizeof cmd,
"env -u XDG_CONFIG_HOME HOME=/tmp/dtl_%d_home %s >/dev/null 2>&1",
getpid(), outbin);
"env -u XDG_CONFIG_HOME HOME=%s %s >/dev/null 2>&1",
shorthome, outbin);
if (runwait(cmd) != 0) {
fprintf(stderr, "dirs_toolong[%s]: short HOME did not exit 0\n",
name);
@@ -77,7 +85,7 @@ run_stage(const char *name, const char *driver)
/* ~260-byte HOME — must abort loudly (non-zero) and create no dir. */
char longhome[512];
int p = snprintf(longhome, sizeof longhome, "/tmp/dtl_%d_long_", getpid());
int p = snprintf(longhome, sizeof longhome, "%s/long_", tmpdir);
for (int i = 0; i < 260 && p < (int)sizeof longhome - 1; i++)
longhome[p++] = 'a';
longhome[p] = '\0';
@@ -96,13 +104,23 @@ run_stage(const char *name, const char *driver)
fprintf(stderr, "dirs_toolong[%s]: stray directory created\n",
name);
fail = 1;
snprintf(cmd, sizeof cmd, "rm -rf %s", longhome);
(void)system(cmd);
}
runwait(rmcmd);
snprintf(cmd, sizeof cmd, "rm -rf /tmp/dtl_%d_home", getpid());
(void)system(cmd);
cleanup: {
int cleanbad = 0;
if (unlink(src) != 0 && errno != ENOENT) cleanbad = 1;
if (unlink(outbin) != 0 && errno != ENOENT) cleanbad = 1;
if (runwait(rmcmd) != 0) cleanbad = 1;
if (rmdir(shortprog) != 0 && errno != ENOENT) cleanbad = 1;
if (rmdir(shortcfg) != 0 && errno != ENOENT) cleanbad = 1;
if (rmdir(shorthome) != 0 && errno != ENOENT) cleanbad = 1;
if (rmdir(tmpdir) != 0) cleanbad = 1;
if (cleanbad) {
fprintf(stderr, "dirs_toolong[%s]: temporary cleanup failed\n",
name);
fail = 1;
}
}
return fail;
}

View File

@@ -1,8 +1,8 @@
/*
* 976_stat_run — execute the lib/os stat fixture under the C-side
* `ww run` driver and assert exit 0.
* `ww test` driver and assert exit 0.
*
* Workflow (mirrors 975_dirs_run's mkdtemp + setenv + rm-rf shape):
* Workflow (mirrors 975_dirs_run's mkdtemp + exact-cleanup shape):
* 1. mkdtemp /tmp/wwstat-XXXXXX — fresh per run, no parallel-test
* contention.
* 2. Populate the scratch tree:
@@ -12,15 +12,15 @@
* 3. setenv WW_TEST_STAT_REGFILE / SYMLINK / SUBDIR / NOENT with
* the absolute paths; lib/os/stattest.ww reads them via
* os.getenv and exercises stat/lstat/fstat/exists.
* 4. exec `ww run lib/os/stattest.ww`.
* 5. system("rm -rf <tmp>") regardless of pass/fail.
* 4. exec `ww test lib/os/stattest.ww`.
* 5. Remove the exact children and then the mkdtemp-owned root.
*
* Same wrapper shape as 970-975; cleanup-on-failure is rm-rf'd so a
* regression never leaves residue under /tmp.
* Same wrapper shape as 970-975; cleanup runs on success and failure.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
@@ -62,46 +62,51 @@ main(void)
snprintf(symlink_path, sizeof symlink_path, "%s/symlink", tmpl);
snprintf(subdir, sizeof subdir, "%s/subdir", tmpl);
snprintf(noent, sizeof noent, "%s/does-not-exist", tmpl);
const char *src = "lib/os/stattest.ww";
int fail = 0, subdir_owned = 0;
/* Populate. */
int fd = open(regfile, O_WRONLY | O_CREAT | O_EXCL, 0644);
if (fd < 0) { perror("open regfile"); goto cleanup_fail; }
if (fd < 0) { perror("open regfile"); fail = 1; goto cleanup; }
if (write(fd, "hello world", 11) != 11) {
perror("write regfile"); close(fd); goto cleanup_fail;
perror("write regfile"); close(fd); fail = 1; goto cleanup;
}
close(fd);
if (symlink("./regfile", symlink_path) != 0) {
perror("symlink"); goto cleanup_fail;
perror("symlink"); fail = 1; goto cleanup;
}
if (mkdir(subdir, 0700) != 0) {
perror("mkdir subdir"); goto cleanup_fail;
perror("mkdir subdir"); fail = 1; goto cleanup;
}
subdir_owned = 1;
setenv("WW_TEST_STAT_REGFILE", regfile, 1);
setenv("WW_TEST_STAT_SYMLINK", symlink_path, 1);
setenv("WW_TEST_STAT_SUBDIR", subdir, 1);
setenv("WW_TEST_STAT_NOENT", noent, 1);
const char *src = "lib/os/stattest.ww";
char path[1024], cmd[2048];
snprintf(path, sizeof path, "%s/%s", cwd, src);
snprintf(cmd, sizeof cmd, "%s/ww test %s", bin, path);
int rc = runwait(cmd);
char rmcmd[256];
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpl);
(void)system(rmcmd);
if (rc != 0) {
fprintf(stderr, "stat_run FAIL: %s exited %d\n", src, rc);
return 1;
fail = 1;
}
cleanup: {
int cleanbad = 0;
if (unlink(symlink_path) != 0 && errno != ENOENT) cleanbad = 1;
if (unlink(regfile) != 0 && errno != ENOENT) cleanbad = 1;
if (subdir_owned && rmdir(subdir) != 0) cleanbad = 1;
if (rmdir(tmpl) != 0) cleanbad = 1;
if (cleanbad) {
fprintf(stderr, "stat_run FAIL: temporary cleanup failed\n");
fail = 1;
}
}
if (fail) return 1;
printf("stat_run: %s ok\n", src);
return 0;
cleanup_fail: {
char rmcmd[256];
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpl);
(void)system(rmcmd);
return 1;
} }
}

View File

@@ -21,11 +21,11 @@
* (mod_collect / mod_lookup_for_fn) stays dormant-but-load-bearing for
* genuinely package-less `//ww:module-reset` deps until #24b/#26.
*
* Asserts (combined `ww build`, BOTH driver stages; `--sep` reproduces
* identically):
* Asserts (default separate compilation, both driver stages):
* 1. Build + run, BOTH stages → exit 9 (the user's `main.run`, return 9),
* never aa.run (return 5).
* 2. cs==ww (rule 10): the combined `.s` is byte-identical between the
* 2. cs==ww (rule 10): the concatenated per-package `.s` is byte-identical
* between the
* two driver stages.
* 3. DISTINCTNESS: the `.s` has EXACTLY ONE `TEXT main.run` (the user's)
* AND EXACTLY ONE `TEXT aa.run` (the import) — distinct symbols, no dup.
@@ -36,6 +36,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
@@ -149,24 +150,38 @@ main(void)
const char *bin = absbin();
if (!bin) { fprintf(stderr, "84 FAIL: getcwd\n"); return 1; }
char td[64], cmd[8192];
int fail = 0;
snprintf(td, sizeof td, "/tmp/wwbare84_%d", getpid());
snprintf(cmd, sizeof cmd, "rm -rf %s", td);
runwait(cmd);
mkdir(td, 0755);
char td[] = "/tmp/wwbare84_XXXXXX";
char cmd[8192];
int fail = 0, aaowned = 0, aafile = 0, rootfile = 0;
int stage_started[2] = { 0, 0 };
if (mkdtemp(td) == NULL) {
perror("wwbare84: mkdtemp");
return 1;
}
/* All paths derive from `td` (a small fixed 64-byte buffer) so the
* snprintfs are provably non-truncating (warning-clean). */
char aadir[1024], aaww[1024], rootww[1024];
snprintf(aadir, sizeof aadir, "%s/aa", td);
mkdir(aadir, 0755);
snprintf(aaww, sizeof aaww, "%s/aa/aa.ww", td);
snprintf(rootww, sizeof rootww, "%s/root.ww", td);
if (write_file(aaww, aa_src) || write_file(rootww, root_src)) {
if (mkdir(aadir, 0755) != 0) {
perror(aadir);
fail++;
goto out;
}
aaowned = 1;
if (write_file(aaww, aa_src) != 0) {
fprintf(stderr, "84 FAIL: write fixture\n");
fail++;
goto out;
}
aafile = 1;
if (write_file(rootww, root_src) != 0) {
fprintf(stderr, "84 FAIL: write fixture\n");
fail++;
goto out;
}
rootfile = 1;
struct { const char *drv, *tag; char prog[1024], sfile[1024]; }
stg[] = { { "ww", "cs", {0}, {0} }, { "ww_ww", "ww", {0}, {0} } };
@@ -174,18 +189,20 @@ main(void)
for (int s = 0; s < 2; s++) {
snprintf(stg[s].prog, sizeof stg[s].prog, "%s/prog.%s", td, stg[s].tag);
snprintf(stg[s].sfile, sizeof stg[s].sfile, "%s/prog.%s.s", td, stg[s].tag);
/* #93 sep layout: `--sep -o <prog>` splits the asm across
/* #93 sep layout: `-o <prog>` splits the asm across
* <prog>.sepwork/<pkg>.s — the bare user `TEXT run,` lands in
* __root.s and the imported `TEXT aa.run,` in aa.s. Concat all
* the per-unit .s (sorted glob order is deterministic) into the
* __root.s and the imported `TEXT aa.run,` in aa.s. Concat those
* exact per-unit .s files in a fixed order into the
* flat <prog>.s the label-count + byte-id checks consume. The
* `-I %s` source path is preserved; WW_PKGCACHE is pinned under
* the tmpdir so the shared out/.pkgcache stays untouched. */
* `-I %s` source path is preserved, and all outputs stay under
* the tmpdir. */
snprintf(cmd, sizeof cmd,
"WW_PKGCACHE=%s/pkgc timeout 240 %s/%s build --sep -o %s "
"-I %s %s >/dev/null 2>&1 && cat %s.sepwork/*.s > %s",
td, bin, stg[s].drv, stg[s].prog, td, rootww,
stg[s].prog, stg[s].sfile);
"timeout 240 %s/%s build -o %s "
"-I %s %s >/dev/null 2>&1 && "
"cat %s.sepwork/__root.s %s.sepwork/aa.s > %s",
bin, stg[s].drv, stg[s].prog, td, rootww,
stg[s].prog, stg[s].prog, stg[s].sfile);
stage_started[s] = 1;
if (runwait(cmd) != 0) {
fprintf(stderr, "84 FAIL: %s build\n", stg[s].drv);
fail++;
@@ -210,15 +227,36 @@ main(void)
}
}
/* (2) cs==ww (rule 10): the combined .s is byte-identical. */
/* (2) cs==ww (rule 10): concatenated per-package .s is byte-identical. */
if (files_eq(stg[0].sfile, stg[1].sfile) != 0) {
fprintf(stderr, "84 FAIL: cs .s != ww .s (rule 10)\n");
fail++;
}
out:
snprintf(cmd, sizeof cmd, "rm -rf %s", td);
runwait(cmd);
{
int cleanfail = 0;
char path[1024];
const char *tags[] = { "cs", "ww" };
for (int i = 0; i < 2; i++) {
if (!stage_started[i]) continue;
snprintf(path, sizeof path, "%s/prog.%s.sepwork", td, tags[i]);
snprintf(cmd, sizeof cmd, "rm -rf %s", path);
if (runwait(cmd) != 0) cleanfail = 1;
snprintf(path, sizeof path, "%s/prog.%s", td, tags[i]);
if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1;
snprintf(path, sizeof path, "%s/prog.%s.s", td, tags[i]);
if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1;
}
if (rootfile && unlink(rootww) != 0 && errno != ENOENT) cleanfail = 1;
if (aafile && unlink(aaww) != 0 && errno != ENOENT) cleanfail = 1;
if (aaowned && rmdir(aadir) != 0) cleanfail = 1;
if (rmdir(td) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "84 FAIL: temporary cleanup failed\n");
fail++;
}
}
if (fail) {
fprintf(stderr, "84: %d check(s) failed\n", fail);
return 1;

View File

@@ -1,32 +1,27 @@
/*
* 989_c6soak_run — M3-tail commit-6 dual-path soak gate, phase-1 leg
* 989_c6soak_run — M3-tail commit-6 separate-compilation soak gate
* (#46 c6, rob-c6-spec.md Tier-A/B + §3 collision).
*
* The STANDING regression-gate for M4: separate-compilation must be
* behaviorally equivalent to the combined (embed-everything) build on
* real, multi-package code. The oracle is OBSERVABLE OUTPUT, not binary
* identity — a sep-linked binary and a combined-linked binary differ in
* symbol order/layout (rob-c6-spec §2 ★), so we run both and compare
* exit codes, never `cmp` the executables.
* The standing real-code regression gate for separate compilation. The
* oracle is observable output plus byte-identical per-package compiler
* artifacts across the C and WW driver stages.
*
* Targets (real lib chains, cheap → phase-1):
* Targets (real library chains):
* - utf8 : encoding.utf8 (DOTTED-path #57) + strings → decode + count
* runes ("héllo" = 5). Exercises the multi-component import
* the combined build resolves by leaf and sep must resolve
* by the full dotted path.
* resolution by the full dotted path.
* - collide: strings.contains AND bytes.contains — the SAME leaf `contains`
* exported by two packages (rob-c6-spec §3, the #48/#49 class
* on real code). sep path-qualifies them to DISTINCT symbols;
* the program prints both correct results; the §3 non-vacuity
* on real code). The driver path-qualifies them to DISTINCT
* symbols;
* the program computes both correct results; the §3 non-vacuity
* check below greps the per-pkg .s to prove the two labels are
* genuinely distinct (disambiguation is real, not luck).
*
* Asserts per target (all COLD — fresh per-stage WW_PKGCACHE, scratch
* wiped each run):
* 1. DUAL-PATH OUTPUT EQUIVALENCE (the teeth): combined-build run-exit ==
* sep-build run-exit == expected, for BOTH driver stages. This is the
* M4 behavioral proof: sep emits a program that behaves identically to
* the combined one.
* Asserts per target (direct per-stage builds use fresh output stems under
* an invocation-owned root; retained scratch is inspected, then removed by
* final carrier cleanup):
* 1. RUN-EXIT: both driver stages build and run to the expected result.
* 2. cs==ww (rule 10): the cstage `ww` and wwstage `ww_ww` sep-drivers emit
* byte-identical per-package .s and .wwi (the .wwi enters the compare
* set — rob-c6-spec). Both also link to a binary that runs to `expect`.
@@ -39,14 +34,14 @@
* equivalence) is FOLDED into the 990-997 bootstrap oracle (994_w6c_ww),
* not bolted on here (rob-c6-spec §2 / amendment-B).
*
* Light wwstage-driver test (CLAUDE.md rule 14): all intermediates are
* `-o`-redirected to /tmp, so it is phase-1 parallel-safe. Models
* All intermediates are `-o`-redirected to /tmp for isolation. Models
* 989_sepbuild_run.c / 989_sepdotpath_run.c conventions; 989 prefix per
* the sep-gate precedent.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
@@ -220,35 +215,25 @@ main(void)
snprintf(inc, sizeof inc, "-I %s/lib -I %s/lib/ww -I %s/selfhost/cmd/wcc",
cwd, cwd, cwd);
char td[64], cmd[8192];
char td[] = "/tmp/wwc6soak_XXXXXX";
char cmd[8192];
int fail = 0;
snprintf(td, sizeof td, "/tmp/wwc6soak_%d", getpid());
snprintf(cmd, sizeof cmd, "rm -rf %s", td);
runwait(cmd);
mkdir(td, 0755);
int source_created[2] = { 0, 0 };
int stage_started[2][2] = { { 0, 0 }, { 0, 0 } };
if (mkdtemp(td) == NULL) {
perror("wwc6soak: mkdtemp");
return 1;
}
for (int i = 0; roots[i].label; i++) {
struct root *r = &roots[i];
char rootww[1024];
snprintf(rootww, sizeof rootww, "%s/%s.ww", td, r->label);
if (write_file(rootww, r->src)) { fail++; continue; }
source_created[i] = 1;
/* 1. combined build (cstage driver) → run. */
char comb[1024];
snprintf(comb, sizeof comb, "%s/%s.comb", td, r->label);
snprintf(cmd, sizeof cmd,
"timeout 240 %s/ww build %s -o %s %s >/dev/null 2>&1",
bin, inc, comb, rootww);
if (runwait(cmd) != 0) {
fprintf(stderr, "c6soak FAIL: %s combined build\n", r->label);
fail++;
continue;
}
int comb_rc = runwait(comb);
/* sep build both stages, fresh per-stage WW_PKGCACHE so every
* package compiles COLD (a warm shared cache would skip the
* per-pkg .s/.wwi this gate inspects — mirrors 989_pkgcache). */
/* Both stages produce every per-package
* .s/.wwi artifact this gate inspects. */
struct { const char *drv, *tag; char prog[1024]; int rc; }
stg[] = { { "ww", "cs", {0}, -1 }, { "ww_ww", "ww", {0}, -1 } };
int built = 1;
@@ -257,16 +242,14 @@ main(void)
* `<label>.ww` SOURCE (else the build clobbers its own input). */
snprintf(stg[s].prog, sizeof stg[s].prog, "%s/%s.%s.bin",
td, r->label, stg[s].tag);
/* Cache keyed per (root,stage): a cache shared across roots
* would warm-hit a common package (strings) from an earlier
* root and skip emitting its .s into THIS root's sepwork. */
snprintf(cmd, sizeof cmd,
"WW_PKGCACHE='%s/cache.%s.%s' timeout 240 %s/%s build --sep "
"timeout 240 %s/%s build "
"%s -o %s %s >/dev/null 2>&1",
td, r->label, stg[s].tag, bin, stg[s].drv, inc,
bin, stg[s].drv, inc,
stg[s].prog, rootww);
stage_started[i][s] = 1;
if (runwait(cmd) != 0) {
fprintf(stderr, "c6soak FAIL: %s %s build --sep\n",
fprintf(stderr, "c6soak FAIL: %s %s build\n",
r->label, stg[s].drv);
fail++;
built = 0;
@@ -276,12 +259,11 @@ main(void)
}
if (!built) continue;
/* 1. dual-path OUTPUT equivalence: combined == sep (both stages). */
if (comb_rc != r->expect || stg[0].rc != r->expect ||
stg[1].rc != r->expect) {
fprintf(stderr, "c6soak FAIL: %s exits comb=%d sep-cs=%d "
"sep-ww=%d (expected %d)\n", r->label, comb_rc,
stg[0].rc, stg[1].rc, r->expect);
/* 1. Runtime behavior agrees across both stages. */
if (stg[0].rc != r->expect || stg[1].rc != r->expect) {
fprintf(stderr, "c6soak FAIL: %s exits cs=%d ww=%d "
"(expected %d)\n", r->label, stg[0].rc, stg[1].rc,
r->expect);
fail++;
}
@@ -311,13 +293,38 @@ main(void)
}
}
snprintf(cmd, sizeof cmd, "rm -rf %s", td);
runwait(cmd);
{
int cleanfail = 0;
char path[1024];
const char *labels[] = { "utf8", "collide" };
const char *tags[] = { "cs", "ww" };
for (int i = 0; i < 2; i++) {
for (int s = 0; s < 2; s++) {
if (!stage_started[i][s]) continue;
snprintf(path, sizeof path, "%s/%s.%s.bin.sepwork",
td, labels[i], tags[s]);
snprintf(cmd, sizeof cmd, "rm -rf %s", path);
if (runwait(cmd) != 0) cleanfail = 1;
snprintf(path, sizeof path, "%s/%s.%s.bin",
td, labels[i], tags[s]);
if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1;
}
if (source_created[i]) {
snprintf(path, sizeof path, "%s/%s.ww", td, labels[i]);
if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1;
}
}
if (rmdir(td) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "c6soak FAIL: temporary cleanup failed\n");
fail++;
}
}
if (fail) {
fprintf(stderr, "c6soak: %d check(s) failed\n", fail);
return 1;
}
printf("c6soak: dual-path combined==sep run-equivalence on real chains "
printf("c6soak: separate-compilation behavior on real chains "
"(encoding.utf8 dotted + strings/bytes same-leaf `contains`) — "
"cs==ww per-pkg .s/.wwi + §3 distinct labels\n");
return 0;

View File

@@ -27,14 +27,9 @@
* ([](str,*fn()void)) slips (a) but still silently shadows the
* synth table → reservation rejects it loud regardless of type.
*
* Rows (every row builds+runs on cstage `ww` and, when present, wwstage
* `ww_ww`; rule-10 — both stages must agree):
* row | shape | verdict
* ---------------------+------------------------------------+----------
* scalar_for_slice | takesslice(int) | REJECT [bug]
* str_for_int | takesint(str) | REJECT
* slice_borrow_ok | takesslice([3]int arr) | ok 0 (#258)
* scalar_arg_ok | takesint(5) | ok 7 (control)
* The twelve ordinary source rows now live in r989_callarg_* fixtures.
* This wrapper retains only -T package synthesis and multi-package nominal
* collision evidence that the single-case fixture grammar cannot encode.
*
* -T faces (bundle via `ww test -c`, then `<comp> -T <combined>` must
* loud-reject; the synth's run() callee is resolved from the bundled
@@ -50,6 +45,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -63,173 +59,6 @@ runwait(const char *cmd)
return -1;
}
struct row {
const char *label;
const char *src;
int expect_build; /* 1 = build+run to want_exit; 0 = must FAIL */
int want_exit; /* meaningful only when expect_build */
const char *diag; /* expected reject diagnostic (NULL for build rows) */
};
static const struct row rows[] = {
/* (1) THE BUG — int where []int is wanted → REJECT both stages.
* Pre-fix wwstage built rc=0 and read the int as a slice header. */
{ "scalar_for_slice",
"package main;\n"
"fn takesslice(xs: []int) void = { return; };\n"
"export fn main() int = {\n"
" let n: int = 5;\n"
" takesslice(n);\n"
" return 0;\n"
"};\n",
0, 0, "not assignable" },
/* (2) str where int is wanted → REJECT both stages. A second scalar
* shape: the general check is not array-specific. */
{ "str_for_int",
"package main;\n"
"fn takesint(x: int) void = { return; };\n"
"export fn main() int = {\n"
" let s: str = \"hi\";\n"
" takesint(s);\n"
" return 0;\n"
"};\n",
0, 0, "not assignable" },
/* (3) CONTROL — [3]int into a []int param is the legit #258 borrow
* (matching element) → ACCEPT both, run to 0. The general check must
* NOT regress the array→slice borrow it subsumes. */
{ "slice_borrow_ok",
"package main;\n"
"fn takesslice(xs: []int) int = { return len(xs): int; };\n"
"export fn main() int = {\n"
" let a: [3]int = [1, 2, 3];\n"
" if (takesslice(a) != 3) { return 1; };\n"
" return 0;\n"
"};\n",
1, 0 },
/* (4) CONTROL — a correctly-typed scalar arg → ACCEPT both, run to 7.
* Pins that the new check does not over-reject a valid scalar call. */
{ "scalar_arg_ok",
"package main;\n"
"fn takesint(x: int) int = { return x; };\n"
"export fn main() int = { return takesint(7); };\n",
1, 7 },
/* (5) scalar→aggregate at the LET context — `let xs: []int = 5` read
* the int as a 24B slice header (same silent-garbage class as the
* call-arg #24, via the SHARED isassignable). REJECT both. */
{ "let_slice_eq_scalar",
"package main;\n"
"export fn main() int = { let xs: []int = 5; return 0; };\n",
0, 0, "not assignable" },
/* (6) bare fn name into a fn-alias param → ACCEPT both (#34/c1': a
* bare fn rvalue types as its fn TYPE; the general check then sees a
* matched fn type). run to 7. */
{ "fn_bare_accept",
"package main;\n"
"type myfn = fn() int;\n"
"fn g() int = { return 7; };\n"
"fn use_it(f: myfn) int = { return f(); };\n"
"export fn main() int = { return use_it(g); };\n",
1, 7 },
/* (7) annotated `g: myfn` into the same param → ACCEPT both, run 7. */
{ "fn_cast_accept",
"package main;\n"
"type myfn = fn() int;\n"
"fn g() int = { return 7; };\n"
"fn use_it(f: myfn) int = { return f(); };\n"
"export fn main() int = { return use_it(g: myfn); };\n",
1, 7 },
/* (8) `&g` (*fn) into a bare-fn alias param → REJECT both. ww-cstage
* takes NO &-required rule for a bare-fn arg, and the *fn-vs-fn KIND
* mismatch is a confident reject (the assignableaddrfn union admits a
* genuine &fn only into a *fn / *alias slot, not a bare-fn alias). */
{ "fn_addr_reject",
"package main;\n"
"type myfn = fn() int;\n"
"fn g() int = { return 7; };\n"
"fn use_it(f: myfn) int = { return f(); };\n"
"export fn main() int = { return use_it(&g); };\n",
0, 0, "not assignable" },
/* (9) matched-signature fn rvalue into a fn slot → ACCEPT both, run 5.
* Pins #34's correct-stamp path (fn type vs fn type, equal sigs). */
{ "fn_match_sig_accept",
"package main;\n"
"fn g() int = { return 5; };\n"
"export fn main() int = { let p: fn() int = g; return p(); };\n",
1, 5 },
/* (10) MISMATCHED-signature fn rvalue into a fn slot → REJECT both
* (#34: `let p: fn() int = h` where h: fn() str — was a silent
* mis-accept via the lenient catch-all; the fn-type stamp now compares
* structurally and rejects). */
{ "fn_mismatch_sig_reject",
"package main;\n"
"fn h() str = { return \"\"; };\n"
"export fn main() int = { let p: fn() int = h; return 0; };\n",
0, 0, "not assignable" },
/* (11) aggregate<->aggregate KIND mismatch — a [3]int array arg into a
* `*int` param. Both aggregate, different kind -> confident reject (the
* array->slice borrow is the ONLY implicit array coercion; array->ptr
* is not). REJECT both. */
{ "aggr_array_into_ptr",
"package main;\n"
"fn takesptr(p: *int) void = { return; };\n"
"export fn main() int = {\n"
" let a: [3]int = [1, 2, 3];\n"
" takesptr(a);\n"
" return 0;\n"
"};\n",
0, 0, "not assignable" },
/* (12) CONTROL — a concrete value into a SPREAD-tagged param must NOT
* over-reject: cstage flattens `...inner` at resolve_type and accepts
* the int leaf; wwstage can't flatten, so it stays LENIENT on a spread
* variant (the faithful escape, mirroring tagged->tagged #115). ACCEPT
* both, run 0. Pins the new check does not regress spread unions. */
{ "spread_tagged_accept",
"package main;\n"
"type inner = (int | str);\n"
"fn take(x: (...inner | bool)) void = { return; };\n"
"export fn main() int = { take(42); return 0; };\n",
1, 0 },
};
/* run_build — build+run `src` via `driver`; returns the binary's exit
* code, or -1 on a build failure. */
static int
run_build(const char *driver, const struct row *r, int i)
{
char tmpdir[64], src[128], outbin[128], cmd[1024], rmcmd[160];
snprintf(tmpdir, sizeof tmpdir, "/tmp/cat_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/cat_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/cat_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -2; }
fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, src);
int brc = runwait(cmd);
int got = -1;
if (brc == 0) got = runwait(outbin);
runwait(rmcmd);
return brc == 0 ? got : -1;
}
static int
filehas(const char *path, const char *needle)
{
@@ -242,38 +71,6 @@ filehas(const char *path, const char *needle)
return strstr(buf, needle) != NULL;
}
/* build_should_fail — the build must FAIL on `driver` *and* emit `diag`.
* A crash (segfault) wraps as a nonzero "w6c failed" with no diagnostic,
* so the substring check distinguishes it from a clean reject (#20). All
* rows[] reject on "not assignable" (cstage spells the types, wwstage the
* shorter "ctx: not assignable (...)"); the core is shared. */
static int
build_should_fail(const char *driver, const char *src, const char *diag,
int i)
{
char tmpdir[64], s[128], outbin[128], cmd[1024], errf[160], rmcmd[160];
snprintf(tmpdir, sizeof tmpdir, "/tmp/catn_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(s, sizeof s, "%s/catn_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/catn_%d_%d", tmpdir, getpid(), i);
snprintf(errf, sizeof errf, "%s/err.txt", tmpdir);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(s, "wb");
if (!f) { runwait(rmcmd); return -1; }
fputs(src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s build -o %s %s >/dev/null 2>%s",
driver, outbin, s, errf);
int rc = runwait(cmd);
int hasmsg = filehas(errf, diag);
runwait(rmcmd);
/* build must NOT succeed AND must emit its diagnostic. */
return (rc != 0 && hasmsg) ? 0 : -1;
}
/* -T reject faces — a user fixture is bundled with lib/test via
* `ww test -c` (compiler-neutral; the synth `run()` callee resolves from
* the bundle), then `<comp> -T <combined>` must loud-reject. */
@@ -303,7 +100,7 @@ static const struct trow trows[] = {
"@test fn checkfoo() void = { return; };\n" },
};
/* tbundle_reject — #94 sep layout: `<drv> test --sep` carries -T into the
/* tbundle_reject — #94 sep layout: `<drv> test` carries -T into the
* root unit's w6c pass, where the __wwtests reservation / call-arg
* typecheck must loud-reject, so the driver build must exit nonzero.
* `stage` is the cs/ww label; `drv` is the matching driver (ww / ww_ww).
@@ -312,89 +109,114 @@ static int
tbundle_reject(const char *bin, const char *stage, const char *drv,
const struct trow *t, int i)
{
int pid = getpid();
char src[160], stem[160], cmd[4096];
snprintf(src, sizeof src, "/tmp/catt_%s_%d_%d.ww", stage, pid, i);
snprintf(stem, sizeof stem, "/tmp/catt_%s_%d_%d", stage, pid, i);
char td[128], src[160], stem[160], errf[176], cmd[4096];
snprintf(td, sizeof td, "/tmp/catt_%s_%d_XXXXXX", stage, i);
if (mkdtemp(td) == NULL) return -1;
snprintf(src, sizeof src, "%s/input.ww", td);
snprintf(stem, sizeof stem, "%s/prog", td);
snprintf(errf, sizeof errf, "%s/err.txt", td);
int result = -1;
int src_created = 0, command_started = 0;
FILE *f = fopen(src, "wb");
if (!f) return -1;
if (!f) goto out;
src_created = 1;
fputs(t->src, f);
fclose(f);
if (fclose(f) != 0) goto out;
char errf[176];
snprintf(errf, sizeof errf, "%s.err", stem);
snprintf(cmd, sizeof cmd,
"WW_PKGCACHE=%s.pkgc %s/%s test --sep -o %s %s >/dev/null 2>%s",
stem, bin, drv, stem, src, errf);
"%s/%s test -o %s %s >/dev/null 2>%s",
bin, drv, stem, src, errf);
command_started = 1;
int rc = runwait(cmd);
/* Both stages emit the reservation diagnostic; a crash would not (#20). */
int hasmsg = filehas(errf, "__wwtests is reserved by -T");
unlink(src);
unlink(errf);
snprintf(cmd, sizeof cmd, "rm -rf %s %s.sepwork %s.pkgc",
stem, stem, stem);
if (system(cmd)) {}
if (rc == 0) {
fprintf(stderr, "callarg_typecheck[%s][%s]: %s test --sep "
fprintf(stderr, "callarg_typecheck[%s][%s]: %s test "
"accepted (expected a loud reject)\n", stage, t->label, drv);
return -1;
}
if (!hasmsg) {
fprintf(stderr, "callarg_typecheck[%s][%s]: %s test --sep "
} else if (!hasmsg) {
fprintf(stderr, "callarg_typecheck[%s][%s]: %s test "
"nonzero exit but missing reservation diagnostic (a crash, "
"not a clean reject)\n", stage, t->label, drv);
return -1;
} else {
result = 0;
}
return 0;
out:
{
int cleanfail = 0;
char path[192];
if (command_started) {
snprintf(path, sizeof path, "%s.sepwork", stem);
snprintf(cmd, sizeof cmd, "rm -rf %s", path);
if (runwait(cmd) != 0) cleanfail = 1;
if (unlink(stem) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(errf) != 0 && errno != ENOENT) cleanfail = 1;
}
if (src_created && unlink(src) != 0 && errno != ENOENT) cleanfail = 1;
if (rmdir(td) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "callarg_typecheck[%s][%s]: temporary cleanup "
"failed\n", stage, t->label);
result = -1;
}
}
return result;
}
/* multimod_build_fail — write io2.ww (always; defines handle=(file|stream)),
* mod1.ww (when withmod1), and a main.ww with `mainbody`, then `drv build -I`
* the tree. Returns 0 when the build correctly FAILS (the arg is rejected),
* non-zero when it wrongly built. Drives the #24 cross-module collision rows
* that single-file rows[] can't express. */
* that single-file fixtures cannot express. */
static int
multimod_build_fail(const char *drv, const char *mainbody, int withmod1,
int tag)
{
int pid = getpid();
char dir[96], io2d[160], mod1d[160], p[224], cmd[2048], rm[256];
snprintf(dir, sizeof dir, "/tmp/catcoll_%d_%d", pid, tag);
char dir[128], io2d[160], mod1d[160], p[224], cmd[2048];
snprintf(dir, sizeof dir, "/tmp/catcoll_%d_XXXXXX", tag);
if (mkdtemp(dir) == NULL) return -1;
snprintf(io2d, sizeof io2d, "%s/io2", dir);
snprintf(mod1d, sizeof mod1d, "%s/mod1", dir);
mkdir(dir, 0755); mkdir(io2d, 0755);
if (withmod1) mkdir(mod1d, 0755);
snprintf(rm, sizeof rm, "rm -rf %s", dir);
int result = -1;
int io2owned = 0, mod1owned = 0;
int io2file = 0, mod1file = 0, mainfile = 0, command_started = 0;
if (mkdir(io2d, 0755) != 0) goto out;
io2owned = 1;
if (withmod1 && mkdir(mod1d, 0755) != 0) goto out;
if (withmod1) mod1owned = 1;
snprintf(p, sizeof p, "%s/io2.ww", io2d);
FILE *f = fopen(p, "wb");
if (!f) { runwait(rm); return -1; }
if (!f) goto out;
io2file = 1;
fputs("package io2;\n"
"export type vtable = struct { x: i32 };\n"
"export type stream = *vtable;\n"
"export type file = i32;\n"
"export type handle = (file | stream);\n"
"export fn take(h: handle) int = { return 7; };\n", f);
fclose(f);
if (fclose(f) != 0) goto out;
if (withmod1) {
snprintf(p, sizeof p, "%s/mod1.ww", mod1d);
f = fopen(p, "wb");
if (!f) { runwait(rm); return -1; }
if (!f) goto out;
mod1file = 1;
fputs("package mod1;\n"
"export type wbox = struct { y: i64 };\n"
"export type stream = *wbox;\n"
"export fn mk() stream = { return nil; };\n", f);
fclose(f);
if (fclose(f) != 0) goto out;
}
snprintf(p, sizeof p, "%s/main.ww", dir);
f = fopen(p, "wb");
if (!f) { runwait(rm); return -1; }
if (!f) goto out;
mainfile = 1;
fputs(mainbody, f);
fclose(f);
if (fclose(f) != 0) goto out;
char errf[128];
snprintf(errf, sizeof errf, "%s/err.txt", dir);
@@ -406,14 +228,49 @@ multimod_build_fail(const char *drv, const char *mainbody, int withmod1,
snprintf(cmd, sizeof cmd,
"cd %s && %s build -I %s main.ww >/dev/null 2>%s",
dir, drv, io2d, errf);
command_started = 1;
int rc = runwait(cmd);
/* Both collision rows reject on "not assignable"; a crash would not (#20). */
int hasmsg = filehas(errf, "not assignable");
runwait(rm);
/* build must NOT succeed AND must emit its diagnostic. */
return (rc != 0 && hasmsg) ? 0 : -1;
result = (rc != 0 && hasmsg) ? 0 : -1;
out:
{
int cleanfail = 0;
char path[256];
if (command_started) {
snprintf(path, sizeof path, "%s/main.sepwork", dir);
snprintf(cmd, sizeof cmd, "rm -rf %s", path);
if (runwait(cmd) != 0) cleanfail = 1;
snprintf(path, sizeof path, "%s/main", dir);
if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1;
snprintf(path, sizeof path, "%s/err.txt", dir);
if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1;
}
if (mainfile) {
snprintf(path, sizeof path, "%s/main.ww", dir);
if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1;
}
if (io2file) {
snprintf(path, sizeof path, "%s/io2/io2.ww", dir);
if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1;
}
if (mod1file) {
snprintf(path, sizeof path, "%s/mod1/mod1.ww", dir);
if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1;
}
if (mod1owned && rmdir(mod1d) != 0) cleanfail = 1;
if (io2owned && rmdir(io2d) != 0) cleanfail = 1;
if (rmdir(dir) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "callarg_typecheck[collide-%d]: temporary "
"cleanup failed\n", tag);
result = -2;
}
}
return result;
}
/* a []int SLICE passed where io2.handle = (file | stream) is wanted — NO
@@ -464,46 +321,10 @@ main(void)
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
struct { const char *name; const char *drv; int gated; }
drivers[] = {
{ "cstage", cdrv, 0 },
{ "wwstage", wdrv, 1 },
{ NULL, NULL, 0 },
};
int n = (int)(sizeof rows / sizeof rows[0]);
int tn = (int)(sizeof trows / sizeof trows[0]);
int total = 0, fail = 0;
for (int d = 0; drivers[d].name; d++) {
if (drivers[d].gated && access(drivers[d].drv, X_OK) != 0) {
fprintf(stderr, "callarg_typecheck: skip %s (no %s)\n",
drivers[d].name, drivers[d].drv);
continue;
}
for (int i = 0; i < n; i++) {
total++;
if (rows[i].expect_build) {
int got = run_build(drivers[d].drv, &rows[i], i);
if (got != rows[i].want_exit) {
fprintf(stderr, "callarg_typecheck[%s][%s]: "
"exit=%d want=%d\n", drivers[d].name,
rows[i].label, got, rows[i].want_exit);
fail++;
}
} else {
if (build_should_fail(drivers[d].drv, rows[i].src,
rows[i].diag, 100 + i) != 0) {
fprintf(stderr, "callarg_typecheck[%s][%s]: "
"built ok, expected a loud reject\n",
drivers[d].name, rows[i].label);
fail++;
}
}
}
}
/* -T faces: the reject fires inside each driver's --sep w6c/-T pass
/* -T faces: the reject fires inside each driver's w6c/-T pass
* (cstage via ww, wwstage via ww_ww). wwstage gated on ww_ww. */
for (int i = 0; i < tn; i++) {
total++;
@@ -523,17 +344,21 @@ main(void)
* (file|stream) tagged with no slice variant) REJECTS on BOTH stages —
* dual-stage row (cstage nominal + c3's shape-matched-lenient leg). */
total++;
if (multimod_build_fail(cdrv, COLLIDE_SHAPE_MISMATCH, 0, 1) != 0) {
fprintf(stderr, "callarg_typecheck[cstage][collide_shape_mismatch]:"
" built ok, expected reject\n");
int rowrc = multimod_build_fail(cdrv, COLLIDE_SHAPE_MISMATCH, 0, 1);
if (rowrc != 0) {
if (rowrc != -2)
fprintf(stderr, "callarg_typecheck[cstage]"
"[collide_shape_mismatch]: built ok, expected reject\n");
fail++;
}
if (access(wdrv, X_OK) == 0) { /* wwstage gated (ww_ww present) */
total++;
if (multimod_build_fail(wdrv, COLLIDE_SHAPE_MISMATCH, 0, 2) != 0) {
fprintf(stderr, "callarg_typecheck[wwstage]"
"[collide_shape_mismatch]: built ok, expected "
"reject (#24-B shape-matched-lenient)\n");
rowrc = multimod_build_fail(wdrv, COLLIDE_SHAPE_MISMATCH, 0, 2);
if (rowrc != 0) {
if (rowrc != -2)
fprintf(stderr, "callarg_typecheck[wwstage]"
"[collide_shape_mismatch]: built ok, expected "
"reject (#24-B shape-matched-lenient)\n");
fail++;
}
}
@@ -541,10 +366,12 @@ main(void)
* cstage; the wwstage over-accept is the documented #10/#66/#37 nominal
* residual (NOT asserted — would be dark until #37 lands). */
total++;
if (multimod_build_fail(cdrv, COLLIDE_SAME_SHAPE, 1, 3) != 0) {
fprintf(stderr, "callarg_typecheck[cstage][collide_same_shape]: "
"built ok, expected cstage to reject the same-leaf "
"collision (#37 cs-side pin)\n");
rowrc = multimod_build_fail(cdrv, COLLIDE_SAME_SHAPE, 1, 3);
if (rowrc != 0) {
if (rowrc != -2)
fprintf(stderr, "callarg_typecheck[cstage]"
"[collide_same_shape]: built ok, expected cstage to "
"reject the same-leaf collision (#37 cs-side pin)\n");
fail++;
}

View File

@@ -1,6 +1,6 @@
/*
* 989_coloimport_sep — #98 E3 flip-blocker: a co-located `_test` entry must
* not shadow the dir-package it imports under `ww test --sep`.
* 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
@@ -30,7 +30,7 @@
* `-I test/wcc/data/colo98` puts widget's parent on the fallback path,
* mirroring how `lib` holds the real `lib/<mod>/` dirs.
*
* Asserts (both driver stages, COLD per-stage WW_PKGCACHE):
* 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
@@ -43,13 +43,13 @@
* cstage `ww` and wwstage `ww_ww` sep-drivers, proving the `locatein`
* port is symmetric with `locate_import_in`.
*
* Light wwstage-driver test (CLAUDE.md rule 14): every intermediate is
* `-o`-redirected to /tmp, so it is phase-1 parallel-safe. Models
* Every intermediate is `-o`-redirected to /tmp for isolation. Models
* 989_septest_run conventions; 989 prefix per the sep-gate precedent.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
@@ -149,7 +149,7 @@ cmp_sepwork(const char *csdir, const char *wwdir, const char *label)
struct tcase {
const char *label;
const char *entry; /* the co-located `_test` entry built under --sep */
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 */
};
@@ -170,9 +170,10 @@ main(void)
char td[64], cmd[8192];
int fail = 0;
snprintf(td, sizeof td, "/tmp/wwcoloimport_%d", getpid());
snprintf(cmd, sizeof cmd, "rm -rf %s", td);
runwait(cmd);
mkdir(td, 0755);
if (mkdir(td, 0755) != 0) {
perror(td);
return 1;
}
for (int i = 0; cases[i].label; i++) {
struct tcase *t = &cases[i];
@@ -182,12 +183,12 @@ main(void)
for (int s = 0; s < 2; s++) {
char prog[1024];
snprintf(prog, sizeof prog, "%s/%s.%s.bin", td, t->label, stg[s].tag);
/* COLD per-(case,stage) cache: every package compiles fresh so
* the per-pkg .s/.wwi this gate inspects are produced. */
/* Each direct build produces the per-package .s/.wwi artifacts
* this gate inspects. */
snprintf(cmd, sizeof cmd,
"WW_PKGCACHE='%s/cache.%s.%s' %s/%s test --sep -I %s -o %s %s "
"%s/%s test -I %s -o %s %s "
">/dev/null 2>&1",
td, t->label, stg[s].tag, bin, stg[s].drv, t->incdir, prog,
bin, stg[s].drv, t->incdir, prog,
t->entry);
stg[s].rc = runwait(cmd);
}
@@ -223,14 +224,34 @@ main(void)
fail += cmp_sepwork(csdir, wwdir, t->label);
}
snprintf(cmd, sizeof cmd, "rm -rf %s", td);
runwait(cmd);
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 <mod>` to the "
"dir-package (not the sibling file) under `ww test --sep` on both driver "
"dir-package (not the sibling file) under `ww test` on both driver "
"stages + cs==ww per-pkg .s/.wwi\n");
return 0;
}

View File

@@ -1,6 +1,9 @@
/*
* 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 today lives ONLY in
* 910/997 onto a gate that survives the M4 flip (910/997 are deleted at
@@ -24,7 +27,7 @@
* 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 --sep` + run. The _vbu twin flips the decl
* 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 —
@@ -34,8 +37,7 @@
* dup_xpkg (same leaf in distinct packages = legal) RIDES on 696/697's
* dir-package coverage — no port (spec C2c).
*
* Light wwstage-driver test (CLAUDE.md rule 14): every modfn intermediate
* is `-o`-redirected to /tmp, so it is phase-1 parallel-safe; the dup +
* 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).
@@ -43,6 +45,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
@@ -115,118 +118,13 @@ file_contains(const char *path, const char *needle)
}
/*
* dup family — direct-w6c/w6c_ww. Each fixture is a flat single-package
* with one duplicate top-level decl; both stages must build-FAIL, and the
* diagnostic must name the duplicate (non-vacuity: a fixture that failed
* for an unrelated reason would pass an exit-only check). cstage prefixes
* line:col, wwstage does not, so the asserted substring is the shared tail.
* Duplicate-declaration and builtin-redeclaration runtime ownership moved to
* r989_declns_* fixtures. This wrapper retains only sep/package artifacts.
*/
static const struct {
const char *fixture;
const char *diag; /* shared "duplicate <kind> <name>" substring */
} dup_rows[] = {
{ "test/wcc/data/dup_fn.ww", "duplicate fn foo" },
{ "test/wcc/data/dup_type.ww", "duplicate type t" },
{ "test/wcc/data/dup_def.ww", "duplicate def D" },
{ "test/wcc/data/dup_let.ww", "duplicate let g" },
};
static int
dup_family(const char *bin)
{
int pid = getpid();
char err[256], cmd[4096];
snprintf(err, sizeof err, "/tmp/declns_dup_%d.err", pid);
int rc = 0;
const char *comps[] = { "w6c", "w6c_ww" };
for (size_t r = 0; r < sizeof dup_rows / sizeof dup_rows[0]; r++) {
for (size_t c = 0; c < sizeof comps / sizeof comps[0]; c++) {
snprintf(cmd, sizeof cmd, "%s/%s %s -o /dev/null 2>%s",
bin, comps[c], dup_rows[r].fixture, err);
if (runwait(cmd) == 0) {
fprintf(stderr, "declns FAIL: %s accepted %s (expected "
"build-fail)\n", comps[c], dup_rows[r].fixture);
rc = 1;
} else if (file_contains(err, dup_rows[r].diag) != 0) {
fprintf(stderr, "declns FAIL: %s rejected %s but not on "
"`%s` (wrong reason)\n", comps[c], dup_rows[r].fixture,
dup_rows[r].diag);
rc = 1;
}
}
}
unlink(err);
return rc;
}
/*
* builtin_redecl — the carve-out. Import-free + flat, so it feeds the
* direct w6c→w6a→w6l pipeline (mirrors 911's linkfail staging). The
* builtin `nomem` (= !void) wins, so g()'s i32 arm returns 7; both stages
* must accept AND run to that exact code.
*/
static const struct {
const char *comp;
const char *asmt;
const char *linkt;
} stages[] = {
{ "w6c", "w6a", "w6l" },
{ "w6c_ww", "w6a_ww", "w6l_ww" },
};
static int
builtin_redecl(const char *bin)
{
int pid = getpid();
char rt[1024], asmf[256], obj[256], exe[256], cmd[4096];
snprintf(rt, sizeof rt, "%s/../lib/libwwrt.a", bin);
int rc = 0;
for (size_t i = 0; i < sizeof stages / sizeof stages[0]; i++) {
snprintf(asmf, sizeof asmf, "/tmp/declns_br_%s_%d.s", stages[i].comp, pid);
snprintf(obj, sizeof obj, "/tmp/declns_br_%s_%d.o", stages[i].comp, pid);
snprintf(exe, sizeof exe, "/tmp/declns_br_%s_%d.exe", stages[i].comp, pid);
int ok = 1;
snprintf(cmd, sizeof cmd,
"%s/%s test/wcc/data/builtin_redecl_ok.ww -o %s 2>/dev/null",
bin, stages[i].comp, asmf);
if (ok && runwait(cmd) != 0) {
fprintf(stderr, "declns FAIL: %s rejected builtin_redecl "
"(carve-out must accept)\n", stages[i].comp);
ok = 0; rc = 1;
}
snprintf(cmd, sizeof cmd, "%s/%s -o %s %s 2>/dev/null",
bin, stages[i].asmt, obj, asmf);
if (ok && runwait(cmd) != 0) {
fprintf(stderr, "declns FAIL: %s asm of builtin_redecl\n",
stages[i].asmt);
ok = 0; rc = 1;
}
snprintf(cmd, sizeof cmd, "%s/%s -o %s %s %s 2>/dev/null",
bin, stages[i].linkt, exe, obj, rt);
if (ok && runwait(cmd) != 0) {
fprintf(stderr, "declns FAIL: %s link of builtin_redecl\n",
stages[i].linkt);
ok = 0; rc = 1;
}
if (ok) {
int got = runwait(exe);
if (got != 7) {
fprintf(stderr, "declns FAIL: builtin_redecl (%s) "
"exit=%d want=7\n", stages[i].comp, got);
rc = 1;
}
}
unlink(asmf); unlink(obj); unlink(exe);
}
return rc;
}
/*
* modfn coexist — the two dir-package layouts. Build each via `ww build
* --sep` + run, both driver stages, expect exit 6 (aa()=1 + aa.helper()=5).
* ` + 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 <prog>.sepwork in `td` for the byte-id
* compares below.
@@ -256,14 +154,14 @@ 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 < sizeof stages / sizeof stages[0]; s++) {
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 --sep -o %s %s 2>/dev/null",
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 --sep %s\n", drv,
fprintf(stderr, "declns FAIL: %s build %s\n", drv,
modfn_lays[l].lay);
rc = 1;
continue;
@@ -363,14 +261,13 @@ main(void)
if (!bin) { fprintf(stderr, "declns FAIL: getcwd\n"); return 1; }
int fail = 0;
fail += dup_family(bin);
fail += builtin_redecl(bin);
char td[64], cmd[256];
snprintf(td, sizeof td, "/tmp/wwdeclns_%d", getpid());
snprintf(cmd, sizeof cmd, "rm -rf %s", td);
runwait(cmd);
mkdir(td, 0755);
if (mkdir(td, 0755) != 0) {
perror(td);
return 1;
}
fail += modfn_build(bin, td);
fail += modfn_cs_eq_ww(td, "coexist");
@@ -378,15 +275,34 @@ main(void)
fail += modfn_order_id(td);
fail += modfn_coexist_present(td);
snprintf(cmd, sizeof cmd, "rm -rf %s", td);
runwait(cmd);
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: dup fn/type/def/let build-fail (both stages) + "
"builtin_redecl accept→7 + modfn dir-pkg coexist→6 (cs==ww, "
printf("declns: modfn dir-pkg coexist→6 (cs==ww, "
"vbu byte-id to use-before-value), both stages (#23/#30)\n");
return 0;
}

View File

@@ -1,12 +1,11 @@
/*
* 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 --sep`,
* 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`. Under COMBINED that predicate is
* exact — only the root's main is imported==0. Under SEP each package is its
* own w6c unit, composed with a path-carrying `//ww:module-reset <path>`
* keys on `leaf=="main" && imported==0`. Each package is its own w6c unit,
* composed with a path-carrying `//ww:module-reset <path>`
* (#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.
@@ -25,7 +24,7 @@
* 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, COLD per-stage WW_PKGCACHE):
* 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`).
@@ -33,16 +32,14 @@
* `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.
* 4. COMBINED unaffected — `ww build` (no --sep) still links+runs to the
* same exit on both stages (the fix must not change combined behavior).
*
* Light wwstage-driver test (CLAUDE.md rule 14): every intermediate is
* `-o`-redirected to /tmp, so phase-1 parallel-safe. Models 989_coloimport_sep
* Every intermediate is `-o`-redirected to /tmp for isolation. Models 989_coloimport_sep
* conventions; 989 prefix per the sep-gate precedent.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
@@ -182,7 +179,7 @@ file_has(const char *path, const char *needle)
struct tcase {
const char *label;
const char *entry; /* root entry built under --sep */
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 */
@@ -207,9 +204,10 @@ main(void)
char td[64], cmd[8192];
int fail = 0;
snprintf(td, sizeof td, "/tmp/wwdepmain_%d", getpid());
snprintf(cmd, sizeof cmd, "rm -rf %s", td);
runwait(cmd);
mkdir(td, 0755);
if (mkdir(td, 0755) != 0) {
perror(td);
return 1;
}
for (int i = 0; cases[i].label; i++) {
struct tcase *t = &cases[i];
@@ -222,9 +220,9 @@ main(void)
char prog[1024];
snprintf(prog, sizeof prog, "%s/%s.%s.bin", td, t->label, stg[s].tag);
snprintf(cmd, sizeof cmd,
"WW_PKGCACHE='%s/cache.%s.%s' %s/%s build --sep -o %s %s "
"%s/%s build -o %s %s "
">/dev/null 2>&1 && %s",
td, t->label, stg[s].tag, bin, stg[s].drv, prog, t->entry, prog);
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",
@@ -256,30 +254,36 @@ main(void)
/* 3. cs==ww (rule 10): per-package .s/.wwi byte-identical. */
fail += cmp_sepwork(csdir, wwdir, t->label);
/* 4. COMBINED unaffected: `ww build` (no --sep) still runs to exit. */
}
for (int i = 0; cases[i].label; i++) {
const char *tags[] = { "cs", "ww" };
for (int s = 0; s < 2; s++) {
char prog[1024];
snprintf(prog, sizeof prog, "%s/%s.%s.cmb", 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);
int rc = runwait(cmd);
if (rc != t->exit) {
fprintf(stderr, "depmain FAIL: %s combined %s exit=%d "
"(expected %d)\n", t->label, stg[s].tag, rc, t->exit);
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++;
}
}
}
snprintf(cmd, sizeof cmd, "rm -rf %s", td);
runwait(cmd);
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 --sep` (only the root entry stays bare) on both driver "
"stages + cs==ww per-pkg .s/.wwi + combined unaffected\n");
"under `ww build` (only the root entry stays bare) on both driver "
"stages + cs==ww per-pkg .s/.wwi\n");
return 0;
}

View File

@@ -29,6 +29,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
@@ -74,10 +75,10 @@ build_run(const char *driver, const char *src, const char *libdir,
const char *tmpdir, char *outbin, size_t obsz)
{
char cmd[1024];
snprintf(cmd, sizeof cmd, "cd %s && %s build -o %s/main %s -L %s -l c "
"2>/dev/null", tmpdir, driver, tmpdir, src, libdir);
if (runwait(cmd) != 0) return -1;
snprintf(outbin, obsz, "%s/main", tmpdir);
snprintf(cmd, sizeof cmd, "cd %s && %s build -o %s %s -L %s -l c "
"2>/dev/null", tmpdir, driver, outbin, src, libdir);
if (runwait(cmd) != 0) return -1;
return runwait(outbin);
}
@@ -111,12 +112,19 @@ main(void)
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
int have_ww = (access(wdrv, X_OK) == 0);
char td[] = "/tmp/dynent_XXXXXX";
if (mkdtemp(td) == NULL) {
perror("dynentry mkdtemp");
return 1;
}
int fail = 0, total = 0;
/* ---- big: ~160 gated dead PLT calls, return 42 ---- */
char bigsrc[80];
snprintf(bigsrc, sizeof bigsrc, "/tmp/dynent_big_%d.ww", getpid());
char bigsrc[1024], smallsrc[1024];
snprintf(bigsrc, sizeof bigsrc, "%s/big.ww", td);
snprintf(smallsrc, sizeof smallsrc, "%s/small.ww", td);
FILE *f = fopen(bigsrc, "wb");
if (!f) return 1;
if (!f) { perror(bigsrc); fail++; goto out; }
fputs("package main;\n", f);
for (int i = 0; i < ns; i++)
fprintf(f, "@symbol(\"%s\") fn s%03d() void;\n", syms[i], i);
@@ -124,31 +132,58 @@ main(void)
for (int i = 0; i < ns; i++)
fprintf(f, "\tif (g != 0) { s%03d(); };\n", i);
fputs("\treturn 42;\n};\n", f);
fclose(f);
int writebad = ferror(f);
if (fclose(f) != 0) writebad = 1;
if (writebad) {
perror(bigsrc);
fail++;
goto out;
}
/* ---- small: 1 dyn sym, headers fit the page ---- */
char smallsrc[80];
snprintf(smallsrc, sizeof smallsrc, "/tmp/dynent_small_%d.ww", getpid());
f = fopen(smallsrc, "wb");
if (!f) return 1;
if (!f) {
perror(smallsrc);
fail++;
goto out;
}
fputs("package main;\n"
"@symbol(\"getpid\") fn cgetpid() i32;\n"
"let g: i32 = 0;\n"
"export fn main() i32 = { if (g != 0) { cgetpid(); }; return 42; };\n",
f);
fclose(f);
writebad = ferror(f);
if (fclose(f) != 0) writebad = 1;
if (writebad) {
perror(smallsrc);
fail++;
goto out;
}
struct { const char *label; const char *src; } cases[] = {
{ "big", bigsrc }, { "small", smallsrc },
};
int fail = 0, total = 0;
for (int ci = 0; ci < 2; ci++) {
total++;
char ctd[80], wtd[80], cob[160], wob[160];
snprintf(ctd, sizeof ctd, "/tmp/dynent_c_%d_%d", getpid(), ci);
snprintf(wtd, sizeof wtd, "/tmp/dynent_w_%d_%d", getpid(), ci);
mkdir(ctd, 0755); mkdir(wtd, 0755);
char ctd[1024], wtd[1024], cob[1100], wob[1100];
snprintf(ctd, sizeof ctd, "%s/c_%d", td, ci);
snprintf(wtd, sizeof wtd, "%s/w_%d", td, ci);
if (mkdir(ctd, 0755) != 0) {
perror(ctd);
fail++;
continue;
}
if (mkdir(wtd, 0755) != 0) {
perror(wtd);
if (rmdir(ctd) != 0)
fprintf(stderr, "dynentry[%s]: setup cleanup failed\n",
cases[ci].label);
fail++;
continue;
}
snprintf(cob, sizeof cob, "%s/main", ctd);
snprintf(wob, sizeof wob, "%s/main", wtd);
int crc = build_run(cdrv, cases[ci].src, libdir, ctd, cob, sizeof cob);
if (crc != 42) {
@@ -170,13 +205,38 @@ main(void)
cases[ci].label);
fail++;
}
unlink(wob);
}
unlink(cob);
rmdir(ctd); rmdir(wtd);
char clean[2400];
int cleanfail = 0;
if (unlink(cob) != 0 && errno != ENOENT) cleanfail = 1;
if (unlink(wob) != 0 && errno != ENOENT) cleanfail = 1;
snprintf(clean, sizeof clean, "rm -rf %s/main.sepwork", ctd);
if (runwait(clean) != 0) cleanfail = 1;
snprintf(clean, sizeof clean, "rm -rf %s/main.sepwork", wtd);
if (runwait(clean) != 0) cleanfail = 1;
if (rmdir(ctd) != 0) cleanfail = 1;
if (rmdir(wtd) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "dynentry[%s]: temporary cleanup failed\n",
cases[ci].label);
fail++;
}
}
unlink(bigsrc); unlink(smallsrc);
out:
if (unlink(bigsrc) != 0 && errno != ENOENT) {
perror(bigsrc);
fail++;
}
if (unlink(smallsrc) != 0 && errno != ENOENT) {
perror(smallsrc);
fail++;
}
if (rmdir(td) != 0) {
perror(td);
fail++;
}
if (fail) {
fprintf(stderr, "dynentry_run: %d/%d check(s) failed\n", fail, total);

View File

@@ -12,11 +12,12 @@
* module nears 256 files). THE FIX (CAP-SEMANTICS rule: the cstage twin grows,
* so grow-dynamic): mirror the realloc-doubling growth in enumeratedir.
*
* #94 sep layout: the flip stops emitting a single <stem>.combined.ww; under
* `--sep` each package is enumerated into its OWN <stem>.sepwork/<pkg>.unit.ww
* #94 sep layout: separate compilation stops emitting a single
* <stem>.combined.ww; each package is enumerated into its OWN
* <stem>.sepwork/<pkg>.unit.ww
* (the bigmod dir → bigmod.unit.ww, holding one `package bigmod;` clause per
* enumerated source file — the exact set the combined.ww used to hold for that
* package). So the gate now drives `ww build --sep` / `ww_ww build --sep` and
* package). So the gate now drives `ww build` / `ww_ww build` and
* re-targets the enumeration check onto bigmod.unit.ww: each stage must enroll
* `nfiles` package clauses, and the two stages' bigmod.unit.ww must be byte-
* identical. A cap-drop on one side shrinks that stage's bigmod.unit.ww — the
@@ -30,13 +31,13 @@
* big_300 was RED pre-c3 (ww enrolled 256, cs 300 — divergent unit).
* small_5 pins the common ≤cap path stays byte-identical.
*
* All intermediates redirect under each stage's -o stem and WW_PKGCACHE is
* pinned to the /tmp scratch, so out/.pkgcache and the source tree stay clean
* and the gate runs parallel-safe.
* All intermediates redirect under each stage's -o stem in /tmp, so out/
* and the source tree stay clean and the gate runs parallel-safe.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -64,32 +65,60 @@ count_pkgs(const char *path)
return n;
}
/* Returns 0 if both --sep driver builds enrolled `nfiles` package clauses into
/* Returns 0 if both driver builds enrolled `nfiles` package clauses into
* their bigmod.unit.ww AND the two units are byte-identical; non-zero
* (and prints) otherwise. */
static int
one_row(const char *label, const char *cdrv, const char *wdrv, int nfiles,
int have_ww)
{
char dir[64], main_ww[128], cstem[128], wstem[128];
char dir[64], bigdir[128], main_ww[128], cstem[128], wstem[128];
char cunit[192], wunit[192], cmd[1024], fn[160];
snprintf(dir, sizeof dir, "/tmp/encap_%d_%s", getpid(), label);
snprintf(cmd, sizeof cmd, "rm -rf %s && mkdir -p %s/bigmod", dir, dir);
system(cmd);
if (mkdir(dir, 0755) != 0) {
perror(dir);
return 1;
}
int fail = 0, have_bigdir = 0;
snprintf(bigdir, sizeof bigdir, "%s/bigmod", dir);
if (mkdir(bigdir, 0755) != 0) {
perror(bigdir);
fail++;
goto cleanup;
}
have_bigdir = 1;
for (int i = 0; i < nfiles; i++) {
snprintf(fn, sizeof fn, "%s/bigmod/f%03d.ww", dir, i);
FILE *f = fopen(fn, "wb");
if (!f) return 1;
fprintf(f, "package bigmod;\nexport fn f%03d() i32 = "
"{ return %d; };\n", i, i);
fclose(f);
if (!f) {
perror(fn);
fail++;
goto cleanup;
}
int werr = fprintf(f, "package bigmod;\nexport fn f%03d() i32 = "
"{ return %d; };\n", i, i) < 0;
if (fclose(f) != 0) werr = 1;
if (werr) {
perror(fn);
fail++;
goto cleanup;
}
}
snprintf(main_ww, sizeof main_ww, "%s/main.ww", dir);
FILE *mf = fopen(main_ww, "wb");
if (!mf) return 1;
fputs("package main;\nimport bigmod;\nfn main() void = {};\n", mf);
fclose(mf);
if (!mf) {
perror(main_ww);
fail++;
goto cleanup;
}
int werr = fputs("package main;\nimport bigmod;\nfn main() void = {};\n", mf) == EOF;
if (fclose(mf) != 0) werr = 1;
if (werr) {
perror(main_ww);
fail++;
goto cleanup;
}
snprintf(cstem, sizeof cstem, "%s/X_c", dir);
snprintf(wstem, sizeof wstem, "%s/X_w", dir);
@@ -97,10 +126,9 @@ one_row(const char *label, const char *cdrv, const char *wdrv, int nfiles,
snprintf(cunit, sizeof cunit, "%s.sepwork/bigmod.unit.ww", cstem);
snprintf(wunit, sizeof wunit, "%s.sepwork/bigmod.unit.ww", wstem);
int fail = 0;
snprintf(cmd, sizeof cmd,
"WW_PKGCACHE=%s/pc %s build --sep -o %s %s 2>/dev/null",
dir, cdrv, cstem, main_ww);
"%s build -o %s %s 2>/dev/null",
cdrv, cstem, main_ww);
if (runwait(cmd) != 0) {
fprintf(stderr, "enumcap[cstage][%s]: build failed\n", label);
fail++;
@@ -114,8 +142,8 @@ one_row(const char *label, const char *cdrv, const char *wdrv, int nfiles,
if (have_ww) {
snprintf(cmd, sizeof cmd,
"WW_PKGCACHE=%s/pw %s build --sep -o %s %s 2>/dev/null",
dir, wdrv, wstem, main_ww);
"%s build -o %s %s 2>/dev/null",
wdrv, wstem, main_ww);
if (runwait(cmd) != 0) {
fprintf(stderr, "enumcap[wwstage][%s]: build failed\n", label);
fail++;
@@ -136,8 +164,52 @@ one_row(const char *label, const char *cdrv, const char *wdrv, int nfiles,
}
}
snprintf(cmd, sizeof cmd, "rm -rf %s", dir);
system(cmd);
cleanup:
if (have_bigdir) {
for (int i = 0; i < nfiles; i++) {
snprintf(fn, sizeof fn, "%s/bigmod/f%03d.ww", dir, i);
if (unlink(fn) != 0 && errno != ENOENT) {
perror(fn);
fail++;
}
}
}
snprintf(main_ww, sizeof main_ww, "%s/main.ww", dir);
if (unlink(main_ww) != 0 && errno != ENOENT) {
perror(main_ww);
fail++;
}
snprintf(cstem, sizeof cstem, "%s/X_c", dir);
snprintf(wstem, sizeof wstem, "%s/X_w", dir);
if (unlink(cstem) != 0 && errno != ENOENT) {
perror(cstem);
fail++;
}
if (unlink(wstem) != 0 && errno != ENOENT) {
perror(wstem);
fail++;
}
char work[192];
snprintf(work, sizeof work, "%s.sepwork", cstem);
snprintf(cmd, sizeof cmd, "rm -rf %s", work);
if (runwait(cmd) != 0) {
fprintf(stderr, "enumcap[%s]: cleanup failed: %s\n", label, work);
fail++;
}
snprintf(work, sizeof work, "%s.sepwork", wstem);
snprintf(cmd, sizeof cmd, "rm -rf %s", work);
if (runwait(cmd) != 0) {
fprintf(stderr, "enumcap[%s]: cleanup failed: %s\n", label, work);
fail++;
}
if (have_bigdir && rmdir(bigdir) != 0) {
perror(bigdir);
fail++;
}
if (rmdir(dir) != 0) {
perror(dir);
fail++;
}
return fail;
}

Some files were not shown because too many files have changed in this diff Show More