diff --git a/internal/wwfixture/types.ww b/internal/wwfixture/types.ww index 431872f4..254211de 100644 --- a/internal/wwfixture/types.ww +++ b/internal/wwfixture/types.ww @@ -1,13 +1,13 @@ package wwfixture; def protocolversion: i32 = 1; -def corpuscount: i32 = 1641; -def errorcount: i32 = 337; +def corpuscount: i32 = 1643; +def errorcount: i32 = 338; def compilecount: i32 = 18; def runcount: i32 = 198; -def runexitcount: i32 = 1088; -def nativecount: i32 = 3282; -def corpushash: str = "d518a094a11081af773549c53a6830c510685ef44548c8ea01400e0661c37e65"; +def runexitcount: i32 = 1089; +def nativecount: i32 = 3286; +def corpushash: str = "c2bfc683a76c7bffa727612f102893c7dbfdb9e73e56212eed8687b8087d7870"; type directive = enum i32 { ERROR = 0, diff --git a/test/wcc/732_def_const_fold.c b/test/wcc/732_def_const_fold.c deleted file mode 100644 index 0db1b6ea..00000000 --- a/test/wcc/732_def_const_fold.c +++ /dev/null @@ -1,360 +0,0 @@ -/* - * 732_def_const_fold — top-level `def` rhs const-fold extended to - * sibling/imported def references, casts, and arithmetic (PROJECT #88). - * - * Predecessor 631_def_neg_global (retired; its rows live as the - * r56_def_* corpus fixtures under the blanket byteid sweep) lifted the - * LITERAL fold (INTLIT/RUNELIT/TRUE/FALSE/NIL + unary +/-/~) into the - * shared fold_int_literal / foldintliteral helper; it explicitly left - * N_CAST, sibling-ident, and N_BIN OUT OF SCOPE. #88 brings - * them in: a Hare-faithful `def SCHAR_MAX = types::I8_MAX;` or - * `def MASK: i32 = (1 << 7) - 1;` was previously inexpressible — the rhs - * folded to nothing, no DATA row was emitted, and any reference failed - * to link. - * - * The fix folds at CHECK time (both stages' pass-2 N_DEF arm) via a new - * eval_def_const / evaldefconst that reuses the shared fold_binop arith - * core (factored out of eval_enum_value / enumvalfold) and the checker's - * EXISTING scope lookup (scope_lookup_prefer for a sibling ref, - * scope_lookup_in_module for a `mod.NAME` qualified ref). On success the - * rhs is stamped to an N_INTLIT in place, so cgen's literal-only - * emit_defs / emitdefconstants lays down the DATA row with no codegen - * change. The fold is GATED on the plain literal fold missing first, so - * every pre-#88 def keeps its node and the emitted bytes stay - * byte-identical (the bootstrap has zero def-ref-def, so 990-997 never - * touch this latent path — hence this targeted fixture). - * - * Coverage: - * 1. EXEC (same-module, via the `ww` driver, + `ww_ww` if built): - * sibling-ref, sibling+arith, def->def->def chain, shift+arith, - * and a widening cast — 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. - * 3. CROSS-MODULE (#88 a2): a combined multi-`package` source (the - * driver's internal concat form, fed straight to w6c like - * 728_match_4arm_cross_module) where `def K = a.J + 1;` resolves - * the imported `a.J`. Assert the folded DATA row value AND byte-id. - * 4. FAIL-LOUD (rule 7): a same-module cycle (def A=B; def B=A), a - * cross-module cycle, and a narrowing cast whose value overflows - * the target must each fail the build on BOTH stages (never a - * silent missing row / silent truncation). - */ -#include -#include -#include -#include -#include -#include "wwtestpkg.h" - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -/* ---- 1. same-module EXEC rows (compile+link+run) -------------------- */ - -/* 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) - * folded to nothing. */ - { "sibling-ref", - "def A: i32 = 7;\n" - "def B: i32 = A;\n" - "fn main() i32 = {\n" - "\tlet x: i32 = B;\n" - "\tif (x == 7) { return 42; };\n" - "\treturn 1;\n" - "};\n" }, - - /* Sibling + arithmetic: the headline `def B = A + 1;`. */ - { "sibling-add", - "def A: i32 = 5;\n" - "def B: i32 = A + 1;\n" - "fn main() i32 = {\n" - "\tlet x: i32 = B;\n" - "\tif (x == 6) { return 42; };\n" - "\treturn 1;\n" - "};\n" }, - - /* def -> def -> def chain: C resolves through B through A. Pins - * the recursive resolve (and that the depth guard doesn't trip on - * a legitimate short chain). */ - { "def-chain", - "def A: i32 = 5;\n" - "def B: i32 = A + 1;\n" - "def C: i32 = B * A;\n" - "fn main() i32 = {\n" - "\tlet x: i32 = C;\n" - "\tif (x == 30) { return 42; };\n" - "\treturn 1;\n" - "};\n" }, - - /* Pure arithmetic with a shift: `(1 << 7) - 1` == 127. Exercises - * the fold_binop shift + subtract path off any sibling ref. */ - { "shift-arith", - "def MASK: i32 = (1 << 7) - 1;\n" - "fn main() i32 = {\n" - "\tlet x: i32 = MASK;\n" - "\tif (x == 127) { return 42; };\n" - "\treturn 1;\n" - "};\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 - * to 5 in an 8-byte slot. */ - { "widening-cast", - "def A: i32 = 5;\n" - "def W: i64 = A: i64;\n" - "fn main() i32 = {\n" - "\tlet x: i64 = W;\n" - "\tif (x == 5i64) { return 42; };\n" - "\treturn 1;\n" - "};\n" }, -}; - -/* ---- shared .s emit + slurp ----------------------------------------- */ - -static int -emit_s(const char *w6c, const char *src, char *out_s, size_t cap) -{ - char cmd[1024]; - snprintf(cmd, sizeof cmd, "timeout 180 %s -o %s %s 2>/dev/null", - w6c, out_s, src); - return runwait(cmd); -} - -static int -slurp(const char *path, char *buf, size_t cap) -{ - FILE *f = fopen(path, "rb"); - if (!f) return -1; - size_t n = fread(buf, 1, cap - 1, f); - fclose(f); - buf[n] = '\0'; - return (int)n; -} - -/* ---- 2. byte-identity of cstage vs wwstage .s ----------------------- */ - -static int -asm_byte_identical(const char *bin, const char *src, const char *label, int i) -{ - char wwsrc[64], cs[64], ws[64]; - snprintf(wwsrc, sizeof wwsrc, "/tmp/dcf_bi_%d_%d.ww", getpid(), i); - snprintf(cs, sizeof cs, "/tmp/dcf_bi_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/dcf_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); - 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); - return -1; - } - - FILE *fc = fopen(cs, "rb"); - FILE *fw = fopen(ws, "rb"); - int rc = 0; - 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, "row[%s]: cstage vs wwstage asm differs\n", - label); - unlink(wwsrc); unlink(cs); unlink(ws); - return rc; -} - -/* ---- 3. cross-module fold: a combined multi-package source ---------- - * Fed straight to w6c (the driver's internal concat form — `ww build` - * can't expand an already-concatenated source). `def K = a.J + 1;` - * resolves the imported `a.J` (41) through scope_lookup_in_module and - * folds to 42, whose DATA row first byte is '*' (0x2a). */ -static const char xmod_src[] = - "package a;\n" - "export def J: i32 = 41;\n" - "package b;\n" - "import a;\n" - "def K: i32 = a.J + 1;\n" - "export fn main() i32 = {\n" - "\tlet k: i32 = K;\n" - "\tif (k == 42) { return 42; };\n" - "\treturn 1;\n" - "};\n"; - -/* Assert the folded DATA row for symbol `b.K` is present with value 42 - * (first byte '*'). Returns 0 on success. */ -static int -xmod_fold_present(const char *w6c, const char *stage) -{ - char src[64], s[64], buf[1 << 15]; - snprintf(src, sizeof src, "/tmp/dcf_xm_%d_%s.ww", getpid(), stage); - snprintf(s, sizeof s, "/tmp/dcf_xm_%d_%s.s", getpid(), stage); - - FILE *f = fopen(src, "wb"); - if (!f) return -1; - wwtest_fputs(xmod_src, f); - fclose(f); - - if (emit_s(w6c, src, s, sizeof s) != 0) { - fprintf(stderr, "xmod[%s]: w6c emit failed\n", stage); - unlink(src); - return -1; - } - int rc = 0; - if (slurp(s, buf, sizeof buf) < 0) { - rc = -1; - } else if (strstr(buf, "b.K(SB),\"*") == NULL) { - fprintf(stderr, - "xmod[%s]: no folded `DATA b.K(SB),\"*` (value 42) row\n", - stage); - rc = -1; - } - unlink(src); unlink(s); - return rc; -} - -/* ---- 4. fail-loud rows: must fail the build on BOTH stages ---------- */ - -struct failrow { const char *label; const char *src; }; - -static const struct failrow fail_rows[] = { - /* 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" - "import b;\n" - "export def J: i32 = b.K;\n" - "package b;\n" - "import a;\n" - "export def K: i32 = a.J;\n" - "export fn main() i32 = { return 0; };\n" }, -}; - -/* Compile via w6c only (no link/run); success means the build FAILED as - * required (nonzero w6c exit). */ -static int -compile_fails(const char *w6c, const struct failrow *r, const char *stage, int i) -{ - char src[64], s[64], cmd[1024]; - snprintf(src, sizeof src, "/tmp/dcf_fl_%d_%d.ww", getpid(), i); - snprintf(s, sizeof s, "/tmp/dcf_fl_%d_%d.s", getpid(), i); - - FILE *f = fopen(src, "wb"); - if (!f) return -1; - wwtest_fputs(r->src, f); - fclose(f); - - /* A regressed depth guard would HANG the cycle rows rather than fail - * loud (rule 7). timeout's 124 is distinct from a clean guard failure - * -- fold it back to a TEST failure so a hang can't pass as fail-loud. */ - snprintf(cmd, sizeof cmd, "timeout 180 %s -o %s %s 2>/dev/null", - w6c, s, src); - int rc = runwait(cmd); - unlink(src); unlink(s); - if (rc == 0) { - fprintf(stderr, - "failrow[%s][%s]: w6c exited 0 (expected loud failure)\n", - r->label, stage); - return -1; - } - if (rc == 124) { - fprintf(stderr, - "failrow[%s][%s]: w6c timed out (depth guard hung?)\n", - r->label, stage); - return -1; - } - return 0; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - char absbin[1024]; - if (bin[0] != '/') { - char cwd[1024]; - if (getcwd(cwd, sizeof cwd) == NULL) return 1; - snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); - bin = absbin; - } - - char 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; - - int nexec = (int)(sizeof exec_rows / sizeof exec_rows[0]); - /* 2. byte-id on every exec row (only when wwstage is built) */ - 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) - fail++; - } - } - - /* 3. cross-module fold: DATA row value on cstage, + byte-id */ - total++; - if (xmod_fold_present(w6c, "cstage") != 0) fail++; - if (have_ww) { - total++; - if (xmod_fold_present(w6c_ww, "wwstage") != 0) fail++; - total++; - if (asm_byte_identical(bin, xmod_src, "xmod-fold", 900) != 0) - fail++; - } - - /* 4. fail-loud on BOTH stages */ - int nfail = (int)(sizeof fail_rows / sizeof fail_rows[0]); - for (int i = 0; i < nfail; i++) { - total++; - if (compile_fails(w6c, &fail_rows[i], "cstage", i) != 0) fail++; - if (have_ww) { - total++; - if (compile_fails(w6c_ww, &fail_rows[i], "wwstage", - 1000 + i) != 0) - fail++; - } - } - - if (fail) { - fprintf(stderr, - "def_const_fold: %d/%d fixtures failed\n", fail, total); - return 1; - } - printf("def_const_fold: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/data/r732_def_xmod_cycle/case.ww b/test/wcc/data/r732_def_xmod_cycle/case.ww new file mode 100644 index 00000000..e25fe5ab --- /dev/null +++ b/test/wcc/data/r732_def_xmod_cycle/case.ww @@ -0,0 +1,9 @@ +//ww:error "def value: reference chain too deep" +// migrated from test/wcc/732_def_const_fold.c: cross-module def cycle (a.J -> b.K -> a.J) must fail loud, not hang or emit silently. +package a; +import b; +export def J: i32 = b.K; +package b; +import a; +export def K: i32 = a.J; +export fn main() i32 = { return 0; }; diff --git a/test/wcc/data/r732_def_xmod_fold/case.ww b/test/wcc/data/r732_def_xmod_fold/case.ww new file mode 100644 index 00000000..9b9abfc5 --- /dev/null +++ b/test/wcc/data/r732_def_xmod_fold/case.ww @@ -0,0 +1,12 @@ +//ww:run-exit 42 +// migrated from test/wcc/732_def_const_fold.c: cross-module def-RHS fold (`def K = a.J + 1`) must emit the folded DATA row; run-exit links AND reads the value. +package a; +export def J: i32 = 41; +package b; +import a; +def K: i32 = a.J + 1; +export fn main() i32 = { + let k: i32 = K; + if (k == 42) { return 42; }; + return 1; +};