diff --git a/Makefile b/Makefile index 5d7ffc74..9972a58a 100644 --- a/Makefile +++ b/Makefile @@ -383,24 +383,14 @@ UNIT_SOURCES = test/wcc/000_smoke.c test/wcc/100_lex.c \ # inline sources with no corpus twin, and the wwstage-driver-leg gates. # 61 pure data-fixture byte-id carriers were retired into test-data-byteid # (fold-4 migrate-and-retire; audit 2026-08-07). -BYTEID_WRAPPER_SOURCES = test/wcc/631_def_neg_global.c \ - test/wcc/753_convwrap_audit.c \ +BYTEID_WRAPPER_SOURCES = test/wcc/753_convwrap_audit.c \ test/wcc/754_slice_of_slice_index.c test/wcc/755_amp_dot_idx.c \ - test/wcc/756_alias_chain_unwrap.c \ test/wcc/758_cgalloc_str_field.c \ - test/wcc/785_structvariant_largeunion_return.c \ - test/wcc/786_narrow_alias_deref_store.c \ - test/wcc/789_named_ptr_alias_variant_widen.c \ - test/wcc/790_single_field_struct_zeroinit.c \ - test/wcc/795_xmod_valglobal_run.c \ - test/wcc/796_xmod_valglobal_dot_run.c \ - test/wcc/798_tuple_sret_callee.c \ - test/wcc/844_size_untyped_int.c \ - test/wcc/926_tagscr_sizes_run.c test/wcc/930_free_noop_run.c \ + test/wcc/930_free_noop_run.c \ test/wcc/946_structparam_run.c \ test/wcc/946_structret_run.c \ - test/wcc/989_m1mangle_sym.c test/wcc/989_m1union_run.c \ - test/wcc/989_m2wwi_run.c test/wcc/989_structlocal_frame.c \ + test/wcc/989_m1mangle_sym.c \ + test/wcc/989_m2wwi_run.c \ test/wcc/989_wwileaf_run.c test/wcc/989_lib_byteid.c BOOTSTRAP_WRAPPER_SOURCES = test/wcc/950_selfcheck.c \ test/wcc/991_w6a_ww.c \ diff --git a/docs/test-system-v2.md b/docs/test-system-v2.md index 47881653..301427a5 100644 --- a/docs/test-system-v2.md +++ b/docs/test-system-v2.md @@ -32,21 +32,21 @@ categories out of the ordinary developer target. | Fixed point and self-host | `test-bootstrap` | | Host linker/platform behavior | `test-platform` | -The live declarative compiler corpus has 1,487 fixtures and 2,974 C/WW cells: +The live declarative compiler corpus has 1,495 fixtures and 2,990 C/WW cells: 338 expected rejections (314 shared and 24 stage-specific), 17 compile-only -successes, 191 exit-zero programs, and 941 explicit-exit programs. +successes, 198 exit-zero programs, and 942 explicit-exit programs. -147 native C carriers remain. They are partitioned exactly once as five -in-process units, 24 byte/artifact gates, six bootstrap gates, one platform +134 native C carriers remain. They are partitioned exactly once as five +in-process units, 11 byte/artifact gates, six bootstrap gates, one platform gate, and 111 residual compiler, package-layout, ABI, diagnostic-observer, driver, linker, or FFI gates. Rows migrated to fixtures or native `@test` owners were removed from those carriers; there is no compatibility execution -path for retired rows. 61 former byte/artifact carriers whose only assertion -was a cstage-vs-wwstage `.s` byte-compare of corpus fixtures were retired -into the blanket `test-data-byteid` comparator; the survivors observe -something the blanket cannot: assembly patterns, symbol tables, frame -layouts, `.wwi` round-trips, inline sources with no corpus twin, or the -wwstage-driver leg. Wwstage-driver-leg byte identity (`ww_ww` versus `ww` +path for retired rows. 74 former byte/artifact carriers whose only assertion +was a cstage-vs-wwstage `.s` byte-compare of sources now living in the +corpus were retired into the blanket `test-data-byteid` comparator (the +last 13 had their remaining inline sources added as fixtures first); the +survivors observe something the blanket cannot: assembly patterns, symbol +tables, `.wwi` round-trips, or the wwstage-driver leg. Wwstage-driver-leg byte identity (`ww_ww` versus `ww` over the emitted `.s` set) has one owner, `989_lib_byteid`, whose unit sweep spans lib test fixtures, import probes, and a zero-dep root-only build; the former 815/940/951 driver-parity carriers were folded into it, @@ -228,8 +228,8 @@ parity is owned by the fixture corpus itself. Known cs/ww divergences are pinned in `DATABYTEID_DIVERGED` with the `989_lib_byteid` discipline: a pinned fixture must still build on both stages and still differ, so a compiler fix fails the gate demanding graduation rather than silently -widening coverage. The full sweep compares 915 fixtures in about a minute -and is scratch-rooted under `out/`, not `/tmp`. +widening coverage. The full sweep compares the 1,157 non-error fixtures +in about a minute and is scratch-rooted under `out/`, not `/tmp`. Byte identity is an explicit proof gate. It is not a prerequisite of `test` or `test-commit`. diff --git a/internal/wwfixture/types.ww b/internal/wwfixture/types.ww index a22d9ca1..60570d0d 100644 --- a/internal/wwfixture/types.ww +++ b/internal/wwfixture/types.ww @@ -1,13 +1,13 @@ package wwfixture; def protocolversion: i32 = 1; -def corpuscount: i32 = 1487; +def corpuscount: i32 = 1495; def errorcount: i32 = 338; def compilecount: i32 = 17; -def runcount: i32 = 191; -def runexitcount: i32 = 941; -def nativecount: i32 = 2974; -def corpushash: str = "2520ada0b1687fab6eed6073a263c620fa4cbab6dc8156f7d51bcf8b14a9ec4a"; +def runcount: i32 = 198; +def runexitcount: i32 = 942; +def nativecount: i32 = 2990; +def corpushash: str = "b8ccfce9c8ca9b47c9f84f446ae6cf412d1964028b2f643dc386318e5384bce1"; type directive = enum i32 { ERROR = 0, diff --git a/test/wcc/631_def_neg_global.c b/test/wcc/631_def_neg_global.c deleted file mode 100644 index 147a0ca8..00000000 --- a/test/wcc/631_def_neg_global.c +++ /dev/null @@ -1,235 +0,0 @@ -/* - * 631_def_neg_global — top-level `def X: T = N;` for non-trivially- - * literal N. The pre-fix bug (#24): `def NEG: i32 = -100;` parsed - * the rhs as N_UN(TK_MINUS, N_INTLIT) and both stages' emit_defs / - * emitdefconstants skipped any non-leaf-literal shape — no DATA row - * was emitted, and any reference to NEG failed to link with - * "undefined reference". Workaround in tree had been bundling such - * flags into an enum (see the `at` enum at lib/os/os.ww:393). - * - * The fix lifts the literal-fold core into a shared helper - * (fold_int_literal / foldintliteral) that handles - * INTLIT/RUNELIT/TRUE/FALSE/NIL plus a unary +/-/~ wrapper over the - * same. emit_defs (cstage cmd/w6c/cgen.c) and emitdefconstants - * (wwstage selfhost/cmd/wcc/cgen.ww) both gate on it. The shared - * helper is reused by eval_enum_value / enumevalmember so the fold - * logic lives in one place per stage. - * - * Rows pin: - * - negative-i32: the headline bug. exit=42. - * - positive-i32: regression check; the same emit path must still - * produce a DATA row for an unwrapped literal. - * - tilde-i32: unary ~ over N_INTLIT (the other op in the unary - * whitelist beyond TK_MINUS / TK_PLUS). - * - negative-i64: 8-byte slot via the same path; verifies sign - * extension through the i64 load. - * - negative-u32-cast: `def X: u32 = (-1): u32;` exercises an - * N_CAST wrapping the N_UN. The fold gate doesn't peel N_CAST - * — but cgen sees the cast and consumes it on the read side — - * so this is OUT OF SCOPE for the gate; we use u32 differently. - * Instead the u32 row uses a tilde to get the all-ones pattern: - * `def X: u32 = ~0u32;` truncates cleanly into a u32 slot and - * reads back as 0xFFFFFFFF. - * - unary-plus-i32: `def X: i32 = +5;` — TK_PLUS noop, completes - * the unary whitelist coverage. - * - * 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 -#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; -} - -struct row { const char *label; const char *src; }; - -static const struct row rows[] = { - /* The headline #24 case: N_UN(TK_MINUS, N_INTLIT) rhs. - * Pre-fix this failed at link time with - * "undefined reference to '.NEG'". */ - { "negative-i32", - "def NEG: i32 = -100;\n" - "fn main() i32 = {\n" - "\tlet x: i32 = NEG;\n" - "\tif (x == -100) { return 42; };\n" - "\treturn 1;\n" - "};\n" }, - - /* Regression check: an unwrapped N_INTLIT rhs must still - * emit a DATA row. If the fold-gate rewrite accidentally - * narrows the whitelist, this row catches it. */ - { "positive-i32", - "def POS: i32 = 100;\n" - "fn main() i32 = {\n" - "\tlet x: i32 = POS;\n" - "\tif (x == 100) { return 42; };\n" - "\treturn 1;\n" - "};\n" }, - - /* Unary tilde over N_INTLIT — the other arithmetic op in - * the unary whitelist. ~0 is -1 in i32 two's complement; - * reading and comparing as i32 pins the slot's full - * sign-extended form. */ - { "tilde-i32", - "def NTIL: i32 = ~0;\n" - "fn main() i32 = {\n" - "\tlet x: i32 = NTIL;\n" - "\tif (x == -1) { return 42; };\n" - "\treturn 1;\n" - "};\n" }, - - /* 8-byte slot via the same fold path. Catches any width- - * specific bug in the DATA-row emit (DATA always writes - * 8 bytes; the i64 typed def is the natural cardinality - * match for that slot). */ - { "negative-i64", - "def NEG: i64 = -1234567890i64;\n" - "fn main() i32 = {\n" - "\tlet x: i64 = NEG;\n" - "\tif (x == -1234567890i64) { return 42; };\n" - "\treturn 1;\n" - "};\n" }, - - /* Unsigned (u32) slot. `~0u32` is all-ones; reading it - * back through a u32 local and comparing against the - * literal pins both the fold (TK_TILDE) and the - * sign-vs-zero-extend on load. */ - { "tilde-u32", - "def UMAX: u32 = ~0u32;\n" - "fn main() i32 = {\n" - "\tlet x: u32 = UMAX;\n" - "\tif (x == 4294967295u32) { return 42; };\n" - "\treturn 1;\n" - "};\n" }, - - /* Unary plus — TK_PLUS noop in the fold. Completes the - * unary whitelist coverage. */ - { "unary-plus-i32", - "def P: i32 = +5;\n" - "fn main() i32 = {\n" - "\tlet x: i32 = P;\n" - "\tif (x == 5) { return 42; };\n" - "\treturn 1;\n" - "};\n" }, -}; - -/* 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 - * doesn't), the DATA row differs and ww2!=ww3 byte-id breaks. */ -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/dng_asm_%d_%d.ww", getpid(), i); - snprintf(cs, sizeof cs, "/tmp/dng_asm_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/dng_asm_%d_%d_w.s", getpid(), i); - - FILE *f = fopen(src, "wb"); - if (!f) return -1; - 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); - 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); - goto cleanup; - } - - FILE *fc = fopen(cs, "rb"); - FILE *fw = fopen(ws, "rb"); - 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", - r->label); - -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; -} - -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; - } - - /* 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/w6c_ww", bin); - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - - /* Asm byte-identity diff, only when wwstage is built. */ - if (access(wdrv, X_OK) == 0) { - for (int i = 0; i < n; i++) { - total++; - if (asm_byte_identical(bin, &rows[i], i) != 0) - fail++; - } - } - - if (fail) { - fprintf(stderr, - "def_neg_global: %d/%d fixtures failed\n", fail, total); - return 1; - } - printf("def_neg_global: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/732_def_const_fold.c b/test/wcc/732_def_const_fold.c index a8b9cf35..0db1b6ea 100644 --- a/test/wcc/732_def_const_fold.c +++ b/test/wcc/732_def_const_fold.c @@ -2,10 +2,11 @@ * 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 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 (see 631's header, "negative-u32-cast"). #88 brings + * 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 diff --git a/test/wcc/756_alias_chain_unwrap.c b/test/wcc/756_alias_chain_unwrap.c deleted file mode 100644 index 1bace48f..00000000 --- a/test/wcc/756_alias_chain_unwrap.c +++ /dev/null @@ -1,244 +0,0 @@ -/* - * 756_alias_chain_unwrap — sentinel for #22 alias-chain SRET/N_DOT - * wedge. Pre-fix both stages single-peeled `if (t->kind == TY_NAMED) - * t = t->under` in cg_sret_retsize / N_LET sizing / cgreturn / N_DOT - * field-access (cstage) and structlookup-on-N_TNAME (wwstage); a - * `type b = a;` over an alias-of-struct stacked two TY_NAMED layers - * and the single peel bottomed out at the inner alias — still - * TY_NAMED, not TY_STRUCT. Outcome: SRET-shaped returns silently - * routed through the scalar-AX ABI (caller's receive slot - * corrupted), let-init slots under-sized, and `s.field` reads - * collapsed to base+0 instead of base+field_offset. - * - * Polarity (rule 10, mutual-symmetric): cstage's hot-path peels - * graduated to a transitive `while (t->kind == TY_NAMED) t = t->under` - * walk; wwstage's structlookup-misses fall through to aliaslookup + - * recurse, mirroring slotsize's existing N_TNAME arm (cgenutil.ww - * line 1955). The strings.tokenize wrapper shape (`type tokenizer = - * bytes::tokenizer;`) is the original surfacing site (#22). - * - * row | shape | gate - * ---------------------+----------------------------------------+-------- - * r1_i32_struct | direct TY_STRUCT, no alias | works pre-fix - * r2_slice_single | `type a = struct{[]u8,[]u8,i64}` | wedge pre-fix (RC=12) - * | aliased once → `type b = a;` | - * r3_slice_direct | direct struct, slice fields | works pre-fix - * r4_i32_double | `type b = a; type a = struct{i32...}` | wedge pre-fix (RC=14) - * | aliased twice → `type c = b;` | - * - * Runtime ownership moved to the matching r756_* fixtures. This carrier - * retains the cstage/wwstage assembly byte-identity discriminator. - */ -#include -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -struct row { const char *label; const char *src; }; - -static const struct row rows[] = { - /* r1: direct i32-fields struct, no alias. Baseline — single - * TY_NAMED layer (the type itself) suffices for the existing - * single-peel pattern. */ - { "r1_i32_struct", - "package main;\n" - "type r1 = struct {\n" - "\ta: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32,\n" - "\th: i32, i: i32, j: i32, k: i32, l: i32, m: i32, p: i32,\n" - "};\n" - "fn make_r1() r1 = {\n" - "\tlet b: r1;\n" - "\tb.p = 99i32;\n" - "\treturn b;\n" - "};\n" - "export fn main() i32 = {\n" - "\tlet s: r1 = make_r1();\n" - "\tif (s.p != 99i32) { return 11; };\n" - "\treturn 0;\n" - "};\n", - }, - /* r2: slice-fields struct aliased once. Pre-fix wedge: - * `type r2_alias = r2_struct;` stacks two TY_NAMED layers, the - * single peel lands on the inner alias and the SRET gate falls - * through to scalar-AX. RC=12 pre-fix on both stages. */ - { "r2_slice_single", - "package main;\n" - "type r2_struct = struct { in: []u8, delim: []u8, p: i64 };\n" - "type r2_alias = r2_struct;\n" - "fn make_r2() r2_alias = {\n" - "\tlet b: r2_struct;\n" - "\tb.p = 99i64;\n" - "\treturn b;\n" - "};\n" - "export fn main() i32 = {\n" - "\tlet s: r2_alias = make_r2();\n" - "\tif (s.p != 99i64) { return 12; };\n" - "\treturn 0;\n" - "};\n", - }, - /* r3: slice-fields struct, no alias — baseline that proves the - * slice-payload sret discipline itself is unaffected. */ - { "r3_slice_direct", - "package main;\n" - "type r3 = struct { in: []u8, delim: []u8, p: i64 };\n" - "fn make_r3() r3 = {\n" - "\tlet b: r3;\n" - "\tb.p = 99i64;\n" - "\treturn b;\n" - "};\n" - "export fn main() i32 = {\n" - "\tlet s: r3 = make_r3();\n" - "\tif (s.p != 99i64) { return 13; };\n" - "\treturn 0;\n" - "};\n", - }, - /* r4: i32-fields struct double-aliased. Same wedge as r2 but - * chain length 3 — extra TY_NAMED layer between the alias and - * the struct. Confirms the discriminator is the chain length, - * not the field shape. RC=14 pre-fix on both stages. */ - { "r4_i32_double", - "package main;\n" - "type r4_struct = struct {\n" - "\ta: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32,\n" - "\th: i32, i: i32, j: i32, k: i32, l: i32, m: i32, p: i32,\n" - "};\n" - "type r4_base = r4_struct;\n" - "type r4_alias = r4_base;\n" - "fn make_r4() r4_alias = {\n" - "\tlet b: r4_alias;\n" - "\tb.p = 99i32;\n" - "\treturn b;\n" - "};\n" - "export fn main() i32 = {\n" - "\tlet s: r4_alias = make_r4();\n" - "\tif (s.p != 99i32) { return 14; };\n" - "\treturn 0;\n" - "};\n", - }, -}; - -static int -emit_s(const char *w6c, const struct row *r, int i, char *out_s, size_t cap) -{ - char src[96], cmd[1024]; - snprintf(src, sizeof src, "/tmp/acu_s_%d_%d.ww", getpid(), i); - snprintf(out_s, cap, "/tmp/acu_s_%d_%d_%s.s", - getpid(), i, w6c[strlen(w6c) - 1] == 'w' ? "ww" : "c"); - - FILE *f = fopen(src, "wb"); - if (!f) return -1; - fputs(r->src, f); - fclose(f); - - snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, out_s, src); - int rc = runwait(cmd); - 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; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - char absbin[512]; - 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 cw6c[640], ww6c[640]; - snprintf(cw6c, sizeof cw6c, "%s/w6c", bin); - snprintf(ww6c, sizeof ww6c, "%s/w6c_ww", bin); - - 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++) { - if (!have_ww) continue; - - /* byte-id cstage vs wwstage */ - total++; - char cs_path[160], ws_path[160]; - /* 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++; - 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]; - snprintf(cmpcmd, sizeof cmpcmd, "cmp -s %s %s", - cs_path, ws_path); - if (runwait(cmpcmd) != 0) { - fprintf(stderr, - "alias_chain[byte-id][%s]: cstage vs wwstage asm differs\n", - rows[i].label); - fail++; - } - 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) { - fprintf(stderr, - "alias_chain: %d/%d fixtures failed\n", fail, total); - return 1; - } - printf("alias_chain: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/785_structvariant_largeunion_return.c b/test/wcc/785_structvariant_largeunion_return.c deleted file mode 100644 index fc6ca968..00000000 --- a/test/wcc/785_structvariant_largeunion_return.c +++ /dev/null @@ -1,258 +0,0 @@ -/* - * 785_structvariant_largeunion_return — project #9 close. Pins that - * RETURNING a STRUCT variant of a LARGE (>4-eightbyte) tagged union - * constructs the struct and lays it into the union payload slot, on - * BOTH stages byte-identically (rule-10). - * - * THE BUG (wwstage-only, cs!=ww): wwstage's return-widen DISPATCH - * (cgreturn needswiden -> cgwidentaggedstore, selfhost/cmd/wcc/ - * cgenstmt.ww + cgenutil.ww) is name-keyed via structlookup. An - * error-struct (`type big = !struct{...}`, the errors.opaque_ shape) - * was NEVER registered in c.structs — collectstructs only registered - * a bare N_TSTRUCT body, not the N_TBANG-wrapped one — so structlookup - * missed it, needswiden stayed false, and the return collapsed to a - * register move (frame $32) that DROPPED both the `x.f = &impl` field - * store and the 32B struct -> 40B union-payload copy (wrong runtime - * value AND 34-line asm divergence vs cstage's correct $80 frame). - * cstage is tinfo-based (peel src->type -> TY_STRUCT, cgen.c:2058) and - * needs no table, so it built correctly. - * - * THE FIX (A1, collectstructs N_TBANG peel): register error-structs - * like any struct so the existing already-cstage-mirrored widen arms - * fire. Zero byte-id delta on the prior corpus (no committed source - * defined a `!struct` before errors.opaque_). The strategic fix is - * #222's sret cutover (deletes this name-keyed dispatch); #10 tracks - * retiring the c.structs table. - * - * GATE-BLIND class: byte-id alone is necessary-not-sufficient — a - * dropped store keeps cs==ww on self-consistent garbage. So the - * struct_variant row READS BACK BOTH the tag (match arm) AND the - * widened field (calls through z.f), and asserts the runtime exit. - * - * row | shape | exit | byte-id - * -----------------+----------------------------------------+------+-------- - * struct_variant | mkbig: x.f=&impl; return x (struct | 42 | cs==ww - * | variant of 40B union); main matches | | - * | big, calls (*z.f)(&1)=1+41 — proves | | - * | the field survived the widen | | - * namedvoid_ctrl | mksmall: return y (named-void variant | 22 | cs==ww - * | of the same 40B union) — the path | | - * | that was correct pre-fix; guards we | | - * | did not regress it | | - * - * GATE POLARITY: must stay GREEN. Red means the struct-variant widen - * dropped its construction again, or the named-void variant regressed. - * GRADUATES nothing — #222's residual 5th-eightbyte truncation on the - * 40B return is symmetric (cs==ww) and unread by this match path. - */ -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -#define STAGE_CS 1 -#define STAGE_WW 2 - -struct row { - const char *label; - const char *src; - int want_exit; - int stage_mask; - int byte_id; -}; - -static const struct row rows[] = { - /* POSITIVE byte-id + runtime: struct variant of a 40B union is - * constructed, the &fn field stored, returned, then matched and - * called through — exit proves the widened field survived. */ - { "struct_variant", - "package main;\n" - "type a = !void; type b = !void;\n" - "type big = !struct { f: *fn(p: *i32) i32, data: [3]u64 };\n" - "type u = !(a | b | big);\n" - "fn impl(p: *i32) i32 = { return *p + 41i32; };\n" - "fn mkbig() u = { let x: big; x.f = (&impl): *fn(p: *i32) i32; return x; };\n" - "export fn main() i32 = {\n" - " let r = mkbig();\n" - " match (r) {\n" - " case a => return 90;\n" - " case b => return 91;\n" - " case let z: big => { let n: i32 = 1i32; let fp = z.f; return (*fp)(&n): i32; };\n" - " };\n" - "};\n", - 42, STAGE_CS | STAGE_WW, 1 }, - - /* CONTROL byte-id + runtime: named-void variant of the SAME 40B - * union — the path that was already correct pre-fix. */ - { "namedvoid_ctrl", - "package main;\n" - "type a = !void; type b = !void;\n" - "type big = !struct { f: *fn(p: *i32) i32, data: [3]u64 };\n" - "type u = !(a | b | big);\n" - "fn mksmall() u = { let y: b; return y; };\n" - "export fn main() i32 = {\n" - " let r = mksmall();\n" - " match (r) {\n" - " case a => return 90;\n" - " case b => return 22;\n" - " case big => return 9;\n" - " };\n" - "};\n", - 22, STAGE_CS | STAGE_WW, 1 }, -}; - -/* 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]; - 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; } - if (rmdir(tmpdir) != 0) { perror(tmpdir); bad = 1; } - return bad; -} - -static int -write_source(const char *path, const char *src) -{ - FILE *f = fopen(path, "wb"); - if (!f) return -1; - if (fputs(src, f) == EOF) { - fclose(f); - return -1; - } - if (fclose(f) != 0) return -1; - return 0; -} - -static int -build_via_driver(const char *driver, const char *tmpdir, const char *src) -{ - char cmd[2048]; - /* #93 sep layout: emit asm to .sepwork/__root.s; pin - * all outputs stay under tmpdir. */ - snprintf(cmd, sizeof cmd, - "cd %s && b='%s'; timeout 180 %s build -S " - "-o \"${b%%.ww}\" \"$b\" 2>/dev/null", - tmpdir, src, driver); - return runwait(cmd); -} - -/* 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, const struct row *r, - int seq) -{ - char src[512], tdc[256], tdw[256], base[64], cs[512], ws[512]; - 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"); - 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) 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 (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"); - if (fc && fw) { - rc = 0; - 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); -out: - 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; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - 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); - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0, seq = 0; - int wwpresent = (access(wdrv, X_OK) == 0); - - for (int i = 0; i < n; i++) { - const struct row *r = &rows[i]; - - if (wwpresent && 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) { - fprintf(stderr, "structvariant: %d/%d checks failed\n", fail, total); - return 1; - } - printf("structvariant: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/786_narrow_alias_deref_store.c b/test/wcc/786_narrow_alias_deref_store.c deleted file mode 100644 index b661b411..00000000 --- a/test/wcc/786_narrow_alias_deref_store.c +++ /dev/null @@ -1,258 +0,0 @@ -/* - * 786_narrow_alias_deref_store — project #11 close. Pins that a narrow - * deref-store through a pointer to a `!`-flagged integer ALIAS - * (`type errno = !i32`) narrows to the alias's underlying width on - * BOTH stages byte-identically (rule-10). - * - * THE BUG (wwstage-only, cs!=ww): the deref-store `*p=v` width logic - * (selfhost/cmd/wcc/cgenexpr.ww) was name-keyed — `primsize(pe.str)` - * on the syntactic pointee node. For `*errno` the node is N_TNAME - * "errno"; primsize("errno")==0 (not a primitive name), so it fell to - * the MOVQ (8-byte) default and wrote 8 bytes through a 4-byte pointee - * — clobbering the adjacent 4 bytes. cstage is type-resolved (peel the - * pointer's ->type NAMED -> TY_PTR -> sub -> NAMED -> i32, cgen.c:4647- - * 4652) so it emits MOVL (4-byte). Plain `*i32` was byte-id (primsize - * "i32"==4); only the alias missed the peel. - * - * THE FIX (#11): primsize-first, and when it returns 0 fall back to - * typenodeprimresolved (the existing N_TBANG/N_TENUM/N_TNAME alias - * walker) to reach the underlying width -> MOVL. The errno port's - * opaque_ store is exactly this `let p = (&x.data): *errno; *p = e;` - * ident-pointer shape. - * - * GATE-BLIND class (the int-cast-no-truncate family): a too-wide store - * is invisible unless something reads the over-written neighbour. So - * the runtime row pre-seeds the HIGH 4 bytes of a u64 with a sentinel, - * stores a small i32 through `*(!i32-alias)` into the LOW 4 bytes, and - * reads the high word back: 42 iff the sentinel survived (MOVL), 7 iff - * an 8-byte MOVQ zeroed it. Layout-independent (single u64, no field- - * packing assumption); the [3]u64 carrier keeps the local multi-word - * to dodge the unrelated single-slot zero-init divergence (#213). - * - * row | shape | exit | byte-id - * -----------------+----------------------------------------+------+-------- - * alias_narrow | *(!i32-alias) deref-store into the low | 42 | cs==ww - * | half of a sentinel'd u64; high word | | (MOVL) - * | read back proves no over-write | | - * plain_i32_ctrl | same via a plain *i32 (no alias) — the | 42 | cs==ww - * | path that already narrowed; guards | | (MOVL) - * | the non-alias case stays byte-id | | - * - * GATE POLARITY: must stay GREEN. Red means the alias narrow-store - * regressed to MOVQ (byte-id break + over-write), or the plain *i32 - * narrow regressed. - */ -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -#define STAGE_CS 1 -#define STAGE_WW 2 - -struct row { - const char *label; - const char *src; - int want_exit; - int stage_mask; - int byte_id; -}; - -static const struct row rows[] = { - /* POSITIVE byte-id + runtime: deref-store through *(!i32-alias). */ - { "alias_narrow", - "package main;\n" - "type errno = !i32;\n" - "type box = struct { data: [3]u64 };\n" - "fn store(v: errno) box = {\n" - " let b: box;\n" - " b.data[0] = 0xFFFFFFFF00000000u64;\n" - " let p = (&b.data): *errno;\n" - " *p = v;\n" - " return b;\n" - "};\n" - "export fn main() i32 = {\n" - " let r = store(7i32: errno);\n" - " let hi: u64 = r.data[0] >> 32u64;\n" - " if (hi == 0u64) { return 7; };\n" - " return 42;\n" - "};\n", - 42, STAGE_CS | STAGE_WW, 1 }, - - /* CONTROL byte-id + runtime: same shape via a PLAIN *i32 (no alias) - * — the path that already narrowed pre-fix; guards we did not break - * it. */ - { "plain_i32_ctrl", - "package main;\n" - "type box = struct { data: [3]u64 };\n" - "fn store(v: i32) box = {\n" - " let b: box;\n" - " b.data[0] = 0xFFFFFFFF00000000u64;\n" - " let p = (&b.data): *i32;\n" - " *p = v;\n" - " return b;\n" - "};\n" - "export fn main() i32 = {\n" - " let r = store(7i32);\n" - " let hi: u64 = r.data[0] >> 32u64;\n" - " if (hi == 0u64) { return 7; };\n" - " return 42;\n" - "};\n", - 42, STAGE_CS | STAGE_WW, 1 }, -}; - -/* 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]; - 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; } - if (rmdir(tmpdir) != 0) { perror(tmpdir); bad = 1; } - return bad; -} - -static int -write_source(const char *path, const char *src) -{ - FILE *f = fopen(path, "wb"); - if (!f) return -1; - if (fputs(src, f) == EOF) { - fclose(f); - return -1; - } - if (fclose(f) != 0) return -1; - return 0; -} - -static int -build_via_driver(const char *driver, const char *tmpdir, const char *src) -{ - char cmd[2048]; - /* #93 sep layout: emit asm to .sepwork/__root.s; pin - * all outputs stay under tmpdir. */ - snprintf(cmd, sizeof cmd, - "cd %s && b='%s'; timeout 180 %s build -S " - "-o \"${b%%.ww}\" \"$b\" 2>/dev/null", - tmpdir, src, driver); - return runwait(cmd); -} - -/* 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, const struct row *r, - int seq) -{ - char src[512], tdc[256], tdw[256], base[64], cs[512], ws[512]; - 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"); - 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) 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 (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"); - if (fc && fw) { - rc = 0; - 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); -out: - 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; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - 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); - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0, seq = 0; - int wwpresent = (access(wdrv, X_OK) == 0); - - for (int i = 0; i < n; i++) { - const struct row *r = &rows[i]; - - if (wwpresent && 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) { - fprintf(stderr, "narrow_alias: %d/%d checks failed\n", fail, total); - return 1; - } - printf("narrow_alias: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/789_named_ptr_alias_variant_widen.c b/test/wcc/789_named_ptr_alias_variant_widen.c deleted file mode 100644 index 46577a4e..00000000 --- a/test/wcc/789_named_ptr_alias_variant_widen.c +++ /dev/null @@ -1,235 +0,0 @@ -/* - * 789_named_ptr_alias_variant_widen — project #15 close. Pins that - * widening a BARE pointer into a tagged-union variant that is a NAMED - * ALIAS of that pointer type computes the correct variant tag, on BOTH - * stages byte-identically (rule-10). - * - * THE BUG (cgen, BOTH stages): cg_variant_match (cmd/w6c/cgen.c) / - * cgvariantmatch (selfhost/cmd/wcc/cgenutil.ww), in the "exactly one - * side NAMED" branch, only matched TAGGED-vs-TAGGED and otherwise - * returned no-match. So widening a bare `*vtable` into the `stream` - * (= *vtable) variant of `handle = (file | stream)` matched NO variant - * → the tag-selection (cg_tag_for_variant / flatvariantidxt) defaulted - * to tag 0 (= file). The eFinal `io.write(&cgoutstream.vt, buf)` in the - * compiler's own emit path (cgen.ww:745, `&cgoutstream.vt` a bare - * *io.vtable widening into io.handle) then dispatched the fd arm, - * wrote asm to a garbage fd, and the w6c_ww it built miscompiled every - * program (#15 — a "scale" red herring; it is per-call deterministic). - * - * THE FIX (#15 + drew proviso): the tag-selection does exact-match - * first (unchanged); only when NO variant exact-matches does it - * structurally match a BARE source against a NAMED-alias variant. A - * NAMED source still binds its own NAMED variant (a bare `i64` into - * `(i64 | oserror)` picks the exact `i64`, not the oserror alias). The - * structural fallback is guarded the way #218 guards its nested-widen - * site: a bare source matching >=2 NAMED variants is ambiguous without - * nominal layout → LOUD hard-error, never a silent first-pick. - * - * row | shape | exit | byte-id - * -------------------+-----------------------------------------+------+-------- - * handle_widen | bare *vtable widened into the `stream` | 42 | cs==ww - * | (= *vtable) variant of (file|stream), | | - * | summed 10x via the stream match arm | | - * | (pre-fix took the file arm → 86) | | - * - * GATE POLARITY: must stay GREEN. Red on handle_widen means the - * 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 -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -#define STAGE_CS 1 -#define STAGE_WW 2 - -struct row { - const char *label; - const char *src; - int want_exit; - int stage_mask; - int byte_id; -}; - -static const struct row rows[] = { - /* POSITIVE byte-id + runtime: bare *vtable widened into the NAMED - * `stream` variant — the #15 fix. */ - { "handle_widen", - "package main;\n" - "type vtable = struct { x: i32 };\n" - "type stream = *vtable;\n" - "type file = i32;\n" - "type handle = (file | stream);\n" - "fn hwrite(h: handle, n: u64) u64 = {\n" - " match (h) {\n" - " case file => return 111u64;\n" - " case let s: stream => return n;\n" - " };\n" - "};\n" - "fn emit(v: *vtable, n: u64) u64 = { return hwrite(v, n); };\n" - "export fn main() i32 = {\n" - " let vt: vtable; vt.x = 0;\n" - " let sum: u64 = 0u64; let i: i32 = 0;\n" - " for (i < 10) { sum = sum + emit(&vt, 4u64); i += 1; };\n" - " if (sum == 40u64) { return 42; };\n" - " return (sum: i32);\n" - "};\n", - 42, STAGE_CS | STAGE_WW, 1 }, -}; - -/* 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]; - 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; } - if (rmdir(tmpdir) != 0) { perror(tmpdir); bad = 1; } - return bad; -} - -static int -write_source(const char *path, const char *src) -{ - FILE *f = fopen(path, "wb"); - if (!f) return -1; - if (fputs(src, f) == EOF) { - fclose(f); - return -1; - } - if (fclose(f) != 0) return -1; - return 0; -} - -static int -build_via_driver(const char *driver, const char *tmpdir, const char *src) -{ - char cmd[2048]; - /* #93 sep layout: emit asm to .sepwork/__root.s; pin - * all outputs stay under tmpdir. */ - snprintf(cmd, sizeof cmd, - "cd %s && b='%s'; timeout 180 %s build -S " - "-o \"${b%%.ww}\" \"$b\" 2>/dev/null", - tmpdir, src, driver); - return runwait(cmd); -} - -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/npav_%d_c_%d", getpid(), seq); - snprintf(tdw, sizeof tdw, "/tmp/npav_%d_w_%d", getpid(), seq); - snprintf(base, sizeof base, "main789"); - 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) 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 (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"); - if (fc && fw) { - rc = 0; - 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); -out: - 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; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - 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); - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0, seq = 0; - int wwpresent = (access(wdrv, X_OK) == 0); - - for (int i = 0; i < n; i++) { - const struct row *r = &rows[i]; - - if (wwpresent && 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 (fail) { - fprintf(stderr, "named_ptr_alias_variant_widen: %d/%d checks failed\n", - fail, total); - return 1; - } - printf("named_ptr_alias_variant_widen: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/790_single_field_struct_zeroinit.c b/test/wcc/790_single_field_struct_zeroinit.c deleted file mode 100644 index 39feb9a6..00000000 --- a/test/wcc/790_single_field_struct_zeroinit.c +++ /dev/null @@ -1,235 +0,0 @@ -/* - * 790_single_field_struct_zeroinit — project #213 (≤8B-composite slice). - * Pins that a bare `let b: T;` of an 8B single-field STRUCT zero-inits - * the slot on BOTH stages byte-identically (rule-10), and that the - * zero is observable (read-before-init sees 0, not stack garbage). - * - * THE BUG (wwstage cgen): wwstage's cglet no-rhs zero-init - * (selfhost/cmd/wcc/cgenstmt.ww) only zeroed an 8B *primitive* - * (scalar/ptr/fn/chan → MOVQ $0) or a `zsz > 8` composite (XORQ run). - * An 8B *composite* (single-field struct/tagged) was NEITHER → it fell - * through with no zero-init, while cstage emits `MOVQ $0` (cgen.c N_LET - * `else if (sz == 8)`, per ww's bare-`let`-composite contract). So a - * read-before-init saw stack garbage on wwstage but 0 on cstage — - * cs!=ww byte-id (surfaced by #5's bufio `box { src: io.stream }`) AND - * a latent silent miscompile. cstage is correct; align wwstage UP. - * - * SCOPE: 8B composites only. Sub-8 (4B/1B) composites are intentionally - * left un-zeroed — cstage doesn't zero them either, so zeroing them on - * wwstage would RE-diverge; that sub-8 read-before-init garbage is a - * SHARED (cs==ww) latent, out of this slice. - * - * row | shape | exit | byte-id - * -------------------+-----------------------------------------+------+-------- - * field8_assign | box{src:*vtable}; let b; b.src=&vt; | 42 | cs==ww - * | read b.src.x — the 778/#5 shape | | - * | (#5-independent: no io/union) | | - * read_before_init | box{x:i64}; dirty the stack, then bare | 0 | cs==ww - * | `let b; return b.x` UNassigned — must | | - * | read 0 (the load-bearing zero-init, | | - * | was stack garbage pre-fix on wwstage) | | - * - * GATE POLARITY: must stay GREEN. Red on field8_assign = the 8B-struct - * zero-init regressed (cs!=ww); red on read_before_init = the zero-init - * stopped being emitted (garbage read). - */ -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -#define STAGE_CS 1 -#define STAGE_WW 2 - -struct row { - const char *label; - const char *src; - int want_exit; - int stage_mask; - int byte_id; -}; - -static const struct row rows[] = { - /* The 778/#5 shape, #5-independent: single-field struct bare-let - * then field-assign. Pre-fix cstage zero-inits b (dead here since - * b.src is assigned), wwstage omits it → cs!=ww. */ - { "field8_assign", - "package main;\n" - "type vtable = struct { x: i32 };\n" - "type box = struct { src: *vtable };\n" - "fn use(b: *box) i32 = { return b.src.x; };\n" - "export fn main() i32 = {\n" - " let vt: vtable; vt.x = 42;\n" - " let b: box; b.src = &vt;\n" - " return use(&b);\n" - "};\n", - 42, STAGE_CS | STAGE_WW, 1 }, - - /* The correctness lock: dirty the stack, then read an UNassigned - * 8B-struct field — must be 0 (zero-init), not garbage. */ - { "read_before_init", - "package main;\n" - "type box = struct { x: i64 };\n" - "fn dirty() i64 = { let a: i64 = 0x7777777777777777i64; let b: i64 = a; return b; };\n" - "fn readit() i64 = { let b: box; return b.x; };\n" - "export fn main() i32 = {\n" - " let j: i64 = dirty();\n" - " let v: i64 = readit();\n" - " if (v == 0i64) { return 0; };\n" - " return 1;\n" - "};\n", - 0, STAGE_CS | STAGE_WW, 1 }, -}; - -/* 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]; - 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; } - if (rmdir(tmpdir) != 0) { perror(tmpdir); bad = 1; } - return bad; -} - -static int -write_source(const char *path, const char *src) -{ - FILE *f = fopen(path, "wb"); - if (!f) return -1; - if (fputs(src, f) == EOF) { - fclose(f); - return -1; - } - if (fclose(f) != 0) return -1; - return 0; -} - -static int -build_via_driver(const char *driver, const char *tmpdir, const char *src) -{ - char cmd[2048]; - /* #93 sep layout: emit asm to .sepwork/__root.s; pin - * all outputs stay under tmpdir. */ - snprintf(cmd, sizeof cmd, - "cd %s && b='%s'; timeout 180 %s build -S " - "-o \"${b%%.ww}\" \"$b\" 2>/dev/null", - tmpdir, src, driver); - return runwait(cmd); -} - -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/sfsz_%d_c_%d", getpid(), seq); - snprintf(tdw, sizeof tdw, "/tmp/sfsz_%d_w_%d", getpid(), seq); - snprintf(base, sizeof base, "main790"); - 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) 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 (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"); - if (fc && fw) { - rc = 0; - 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); -out: - 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; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - 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); - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0, seq = 0; - int wwpresent = (access(wdrv, X_OK) == 0); - - for (int i = 0; i < n; i++) { - const struct row *r = &rows[i]; - - if (wwpresent && 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) { - fprintf(stderr, "single_field_struct_zeroinit: %d/%d checks failed\n", - fail, total); - return 1; - } - printf("single_field_struct_zeroinit: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/795_xmod_valglobal_run.c b/test/wcc/795_xmod_valglobal_run.c deleted file mode 100644 index 4460a315..00000000 --- a/test/wcc/795_xmod_valglobal_run.c +++ /dev/null @@ -1,214 +0,0 @@ -/* - * 795_xmod_valglobal_run — project #1 close, the cgen residual of #55. - * 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. - * - * THE BUG (cgen-only, both stages emit IDENTICAL wrong asm → byte-id - * was BLIND to it): a bare cross-module value-global load mangled its - * symbol via a NON-preferring leaf lookup (cstage masym->mod_mangle-> - * mod_lookup first-match; wwstage emitsymname->modlookup). With - * package aa; export let v: i32 = 7; fn getv() = { return v; } - * package main; import aa; let v: i32 = 99; main = { return aa.getv(); } - * the bare `v` inside aa.getv resolved to main.v, and aa.v's DATA slot - * was ALSO labeled main.v (collision) — so aa.getv() returned 99, not 7. - * Functions were already correct (they thread a cur_mod hint via mafn/ - * emitfnname); value globals did not. - * - * THE FIX (#1): reference-site mangle uses the resolved module (curmod- - * prefer for bare idents); definition-site mangle uses the decl's OWN - * module — threaded per-site the way fns already do (cstage mahint with - * c->cur_mod / d->module; wwstage emitsymnamehint with c.curmod / - * d.nmod). The value lookup mangles ONLY on an exact (name, module) - * match and otherwise leaves the name BARE — no first-leaf fallback — - * because exported value globals are export-skipped from the module map - * (they keep their bare-name data ABI), and a first-match fallback would - * mis-mangle an exported `v` onto another module's private `v`. So the - * exported aa.v stays `v`, the private main.v stays `main.v`, no - * collision. - * - * 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- - * module reference without -I path plumbing, and the build is self- - * contained (no selfhost traversal). - * - * GATE POLARITY: must stay GREEN. A wrong exit means the value-global - * mangle regressed (collision returned); a byte-id FAIL means the stages - * diverged on the mangle (rule-10 violation). - * - * SCOPE NOTE: str/slice/array value-globals are intentionally NOT rowed - * here — `len(str-global)` and indexed-global reads trip a SEPARATE, - * still-open cs!=ww divergence in the load body (the N_DOT-base / index - * sibling tracked as #229), orthogonal to this symbol-mangle fix. - */ -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -struct row { const char *label; const char *src; int want_exit; }; - -static const struct row rows[] = { - /* canonical #1: exported aa.v (7) vs private main.v (99); aa.getv() - * must read aa's v, not collide onto main.v. Pre-fix: 99. */ - { "i32_let_export_vs_private", - "package aa;\n" - "export let v: i32 = 7;\n" - "export fn getv() i32 = { return v; };\n" - "package main;\n" - "import aa;\n" - "let v: i32 = 99;\n" - "export fn main() i32 = { return aa.getv(); };\n", 7 }, - /* def-constant flavour: exported def vs private def, same leaf. */ - { "def_export_vs_private", - "package aa;\n" - "export def K: i32 = 5;\n" - "export fn getk() i32 = { return K; };\n" - "package main;\n" - "import aa;\n" - "def K: i32 = 88;\n" - "export fn main() i32 = { return aa.getk(); };\n", 5 }, - /* float flavour: exercises the LEAQ+MOVSD load arm and the - * emit_floatlit_data DATA label. aa.getf() == 2.5 -> i32 2. */ - { "f64_let_export_vs_private", - "package aa;\n" - "export let f: f64 = 2.5;\n" - "export fn getf() f64 = { return f; };\n" - "package main;\n" - "import aa;\n" - "let f: f64 = 9.9;\n" - "export fn main() i32 = { return aa.getf(): i32; };\n", 2 }, - { NULL, NULL, 0 } -}; - -static int -slurp_eq(const char *a, const char *b) -{ - FILE *fa = fopen(a, "rb"); - FILE *fb = fopen(b, "rb"); - if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; } - int rc = 0; - for (;;) { - int ca = fgetc(fa); - int cb = fgetc(fb); - if (ca != cb) { rc = -1; break; } - if (ca == EOF) break; - } - fclose(fa); fclose(fb); - return rc; -} - -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[1100], w6c_ww[1100]; - snprintf(w6c, sizeof w6c, "%s/w6c", bin); - snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin); - if (access(w6c_ww, X_OK) != 0) { - fprintf(stderr, "xmod_valglobal: w6c_ww missing — cannot run the " - "cs==ww byte-id gate\n"); - return 1; - } - - int n = 0, fail = 0; - for (int i = 0; rows[i].src; i++, n++) { - /* src + binary + both .s land under one tmpdir so the - * compiler's .sepwork scratch stays inside it; a single - * rm -rf at the end reclaims everything. */ - char tmpdir[64]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/wwxmv_%d_d_%d", getpid(), i); - /* an unowned dir must never be rm -rf'd below */ - if (mkdir(tmpdir, 0755) != 0) { - perror(tmpdir); - fail++; - continue; - } - - 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); - - 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); - - 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++; - 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++; - if (runwait(rmcmd) != 0) - fprintf(stderr, "row[%s]: cleanup failed: %s\n", - rows[i].label, rmcmd); - continue; - } - 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++; - } - /* 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 (cs==ww byte-id)\n", n, n); - return 0; -} diff --git a/test/wcc/796_xmod_valglobal_dot_run.c b/test/wcc/796_xmod_valglobal_dot_run.c deleted file mode 100644 index a5d14870..00000000 --- a/test/wcc/796_xmod_valglobal_dot_run.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * 796_xmod_valglobal_dot_run — project #229, the N_DOT-base read + addr-of - * 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, - * but the DOTTED LOAD/addr sites still used the non-preferring leaf lookup - * (cstage masym, wwstage emitsymname). With - * package aa; export let v: i32 = 7; - * package main; import aa; let v: i32 = 99; main = { return aa.v; } - * the `aa.v` read AND `&aa.v` emitted `LEAQ main.v(SB)` — the WRONG - * module's global — returning 99, not 7. - * - * THE FIX (#229): thread the DOTTED module name (the `m` in `m.x`) — NOT - * cur_mod — into the existing value mangle at the four lockstep sites: - * cstage cgen.c N_DOT read + N_UN addr-of (mahint with n->lhs->str / - * opnd->lhs->str), wwstage cgenexpr.ww twins (emitsymnamehint with - * lhs.str / basenm). The TY_FN branches beside each site already use this - * dotted polarity via mafn/emitfnname; value globals now match. - * - * Runtime ownership moved to the matching r796_* fixtures. This carrier - * retains the w6c vs w6c_ww `.s` comparison required by rule 10. - * - * GATE POLARITY: a byte-id failure means the stages diverged. - */ -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -struct row { const char *label; const char *src; int want_exit; }; - -static const struct row rows[] = { - /* N_DOT read: aa.v (7) vs private main.v (99). Pre-fix: 99. */ - { "i32_dot_read_export_vs_private", - "package aa;\n" - "export let v: i32 = 7;\n" - "package main;\n" - "import aa;\n" - "let v: i32 = 99;\n" - "export fn main() i32 = { return aa.v; };\n", 7 }, - /* addr-of: &aa.v then deref. Pre-fix: 99. */ - { "i32_addrof_export_vs_private", - "package aa;\n" - "export let v: i32 = 7;\n" - "package main;\n" - "import aa;\n" - "let v: i32 = 99;\n" - "export fn main() i32 = { let p: *i32 = &aa.v; return *p; };\n", 7 }, - { NULL, NULL, 0 } -}; - -static int -slurp_eq(const char *a, const char *b) -{ - FILE *fa = fopen(a, "rb"); - FILE *fb = fopen(b, "rb"); - if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; } - int rc = 0; - for (;;) { - int ca = fgetc(fa); - int cb = fgetc(fb); - if (ca != cb) { rc = -1; break; } - if (ca == EOF) break; - } - fclose(fa); fclose(fb); - return rc; -} - -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[1100], w6c_ww[1100]; - snprintf(w6c, sizeof w6c, "%s/w6c", bin); - snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin); - if (access(w6c_ww, X_OK) != 0) { - fprintf(stderr, "xmod_valglobal_dot: w6c_ww missing — cannot run " - "the cs==ww byte-id gate\n"); - return 1; - } - - int n = 0, fail = 0; - for (int i = 0; rows[i].src; i++, n++) { - char tmpdir[64]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/wwxmvd_%d_d_%d", getpid(), i); - /* an unowned dir must never be rm -rf'd below */ - if (mkdir(tmpdir, 0755) != 0) { - perror(tmpdir); - fail++; - continue; - } - - char src[128], cs_s[128], ws_s[128], rmcmd[160]; - snprintf(src, sizeof src, "%s/wwxmvd_%d_%d.ww", 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++; - 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_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++; - 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++; - if (runwait(rmcmd) != 0) - fprintf(stderr, "row[%s]: cleanup failed: %s\n", - rows[i].label, rmcmd); - continue; - } - 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++; - } - /* 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 dotted value-global tests failed\n", - fail, n); - return 1; - } - printf("xmod_valglobal_dot: %d/%d ok (cs==ww byte-id)\n", - n, n); - return 0; -} diff --git a/test/wcc/798_tuple_sret_callee.c b/test/wcc/798_tuple_sret_callee.c deleted file mode 100644 index 0373b797..00000000 --- a/test/wcc/798_tuple_sret_callee.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - * 798_tuple_sret_callee — project #10 Fold A (wide tuple-return / sret, - * CALLEE side). An over-capacity tuple return (> 4 integer eightbytes or - * > 2 SSE eightbytes) used to LOUD-STOP at the N_RETURN SEND site - * ("tuple return exceeds ... register-return ABI capacity"). Fold A makes - * the CALLEE emit such a return via the existing sret skeleton: the - * prologue wires @sretarg (the caller-prealloc dest in RDI), and the - * return stores each element through *(@sretarg) at its packed layout - * offset, then returns @sretarg in RAX. - * - * THIS TEST IS COMPILE + BYTE-ID ONLY (no runtime row): - * (a) w6c (cstage) AND w6c_ww (wwstage) must now COMPILE the over-cap - * tuple-returning fn — no loud-stop. The pre-Fold-A behavior was a - * hard error; a green compile here proves the SEND emits sret. - * (b) the two .s outputs must be BYTE-IDENTICAL (rule-10). The classifier - * (cg_sret_retsize / sretretsize) and the SEND emitter share a single - * cap SSoT, so both stages classify + lay out the tuple the same way. - * - * WHY NO RUNTIME ROW: the CALL/receive side stays deliberately loud-stopped - * — an N_MLET/N_MASSIGN destructure of an over-cap tuple still aborts - * ("tuple destructure exceeds ... capacity"), so the over-cap callee is not - * yet usefully callable. The end-to-end round-trip (allocate dest, call, - * read the elements back) arrives with Fold B (#10-B), which wires the - * receive. So this fn is compiled but never called/destructured here. - * - * ROWS exercise the layout arithmetic the SEND must get right: - * - ([]u8, []u8) 6 GP eightbytes, all-wide, offsets 0 / 24. - * - (str, str) 6 GP, str IS []u8 (24B header), offsets 0 / 24. - * - (str, str, i32) 7 GP; the trailing narrow i32 must store MOVL at - * its NATURAL packed offset 48 (#169), not over-MOVQ. - * - (f64, f64, f64) 3 SSE eightbytes > the 2-wide SSE cap. - * - * GATE POLARITY: must stay GREEN. A non-zero compile means the SEND - * loud-stop regressed (Fold A reverted); a byte-id FAIL means cstage and - * wwstage diverged on the sret classification or the element layout - * (rule-10 violation / cap-SSoT drift). - */ -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -struct row { const char *label; const char *src; }; - -static const struct row rows[] = { - { "two_slices", - "package main;\n" - "export fn f(a: []u8, b: []u8) ([]u8, []u8) = { return (a, b); };\n" }, - { "two_str", - "package main;\n" - "export fn f(a: str, b: str) (str, str) = { return (a, b); };\n" }, - { "str_str_i32", - "package main;\n" - "export fn f(a: str, b: str, n: i32) (str, str, i32) = {\n" - " return (a, b, n);\n" - "};\n" }, - { "three_f64", - "package main;\n" - "export fn f(x: f64, y: f64, z: f64) (f64, f64, f64) = {\n" - " return (x, y, z);\n" - "};\n" }, - { NULL, NULL } -}; - -static int -slurp_eq(const char *a, const char *b) -{ - FILE *fa = fopen(a, "rb"); - FILE *fb = fopen(b, "rb"); - if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; } - int rc = 0; - for (;;) { - int ca = fgetc(fa); - int cb = fgetc(fb); - if (ca != cb) { rc = -1; break; } - if (ca == EOF) break; - } - fclose(fa); fclose(fb); - return rc; -} - -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[1100], w6c_ww[1100]; - snprintf(w6c, sizeof w6c, "%s/w6c", bin); - snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin); - if (access(w6c_ww, X_OK) != 0) { - fprintf(stderr, "tuple_sret_callee: w6c_ww missing — cannot run " - "the cs==ww byte-id gate\n"); - return 1; - } - - int n = 0, fail = 0; - for (int i = 0; rows[i].src; i++, n++) { - char src[64]; - snprintf(src, sizeof src, "/tmp/wwtsc_%d_%d.ww", getpid(), i); - FILE *f = fopen(src, "wb"); - if (f == NULL) { fail++; continue; } - fputs(rows[i].src, f); - fclose(f); - - 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); - 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); - 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); - rowfail = 1; - } -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) { - fprintf(stderr, "%d/%d tuple-sret-callee tests failed\n", fail, n); - return 1; - } - printf("tuple_sret_callee: %d/%d ok (compile + cs==ww byte-id)\n", n, n); - return 0; -} diff --git a/test/wcc/844_size_untyped_int.c b/test/wcc/844_size_untyped_int.c deleted file mode 100644 index 2f368596..00000000 --- a/test/wcc/844_size_untyped_int.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * 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 -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -static const char *bid_src = - "package main;\n" - "export fn main() i32 = {\n" - " let m: size = 16;\n" - " if (m >= size(u64)) { return 1; };\n" - " return 0;\n" - "};\n"; - -static int -asm_byte_identical(const char *bin) -{ - char src[64], cs[64], ws[64], cmd[1024]; - 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; - fputs(bid_src, f); - fclose(f); - - 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"); - /* 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"); - 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; - } - - 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, "bid: cstage vs wwstage asm differs\n"); - /* 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; -} - -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 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; - if (access(wdrv, X_OK) == 0) { - total++; - if (asm_byte_identical(bin) != 0) fail++; - } - if (fail) { - fprintf(stderr, "size_untyped_int: %d/%d fixtures failed\n", - fail, total); - return 1; - } - printf("size_untyped_int: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/926_tagscr_sizes_run.c b/test/wcc/926_tagscr_sizes_run.c deleted file mode 100644 index 35621bad..00000000 --- a/test/wcc/926_tagscr_sizes_run.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * 926_tagscr_sizes_run — size-keyed per-fn tagged scratch (#44): a fn - * that mixes two tagged slot sizes gets one @tagscr slot per - * distinct size instead of one shared first-use-sized slot. - * - * Pre-#44 the single shared slot was sized at first use and a later - * LARGER ask hit the rule-7 fatal ("@tagscr cached sz %d, need %d" / - * wwstage "localadd: @-prefix slot grew within fn") — so any fn that - * first widened a smaller tagged (56B append-element fill, #34) and - * then needed a larger one (64B sret return, #38) was uncompilable. - * That is exactly regex compile()'s shape (append(insts, v) then - * `return regex{...}`), the fold-2a blocker. - * - * Rows pin: smaller-then-larger (the blocker), larger-then-smaller - * (pre-#44 this silently REUSED the bigger slot; now each size pins - * its own), same-size-twice (one slot, reused), the single-size - * control (asm must stay byte-identical — also hand-checked against - * the pre-#44 compiler when the fix landed), and the N_INDEX - * tagged-element store site mixing with an sret return. - * - * 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). - */ -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -struct row { - const char *label; - const char *src; - int want; -}; - -/* 48B payload -> 56B tagged slot (the regex inst-union shape); 56B - * payload -> 64B slot (the compile()-return shape). */ -#define SIZE_TYPES \ - "type oops = !str;\n" \ - "type p48 = struct { a: i64, b: i64, c: i64, d: i64, e: i64, f: i64 };\n" \ - "type small = (p48 | bool);\n" \ - "type p56 = struct { a: i64, b: i64, c: i64, d: i64, e: i64, f: i64, g: i64 };\n" - -static const struct row rows[] = { - /* The fold-2a blocker: 56B append-element widen, then a 64B - * tagged sret return in the SAME fn. Pre-#44: loud fatal. */ - { "small_then_large", - SIZE_TYPES - "fn f() (p56 | oops | nomem) = {\n" - " let xs: []small;\n" - " let b: bool = true;\n" - " let v: small = b;\n" - " append(xs, v);\n" - " return p56 { a = 1, b = 2, c = 3, d = 4, e = 5, f = 6, g = 7 };\n" - "};\n" - "export fn main() i32 = {\n" - " match (f()) {\n" - " case let w: p56 => { if (w.g != 7) { return 1; }; };\n" - " case => return 2;\n" - " };\n" - " return 0;\n" - "};\n", - 0 }, - /* Reverse order via an early conditional return: 64B sret - * first, 56B append second. Pre-#44 this passed by silently - * REUSING the 64B slot for the 56B widen; now the 56B widen - * pins its own slot — runtime must stay correct. */ - { "large_then_small", - SIZE_TYPES - "fn f(flag: bool) (p56 | oops | nomem) = {\n" - " if (flag) {\n" - " return p56 { a = 1, b = 2, c = 3, d = 4, e = 5, f = 6, g = 9 };\n" - " };\n" - " let xs: []small;\n" - " let b: bool = true;\n" - " let v: small = b;\n" - " append(xs, v);\n" - " let e: small = xs[0];\n" - " if (!(e is bool)) {\n" - " return \"bad\": oops;\n" - " };\n" - " return p56 { a = 1, b = 2, c = 3, d = 4, e = 5, f = 6, g = 7 };\n" - "};\n" - "export fn main() i32 = {\n" - " match (f(false)) {\n" - " case let w: p56 => { if (w.g != 7) { return 1; }; };\n" - " case => return 2;\n" - " };\n" - " match (f(true)) {\n" - " case let w: p56 => { if (w.g != 9) { return 3; }; };\n" - " case => return 4;\n" - " };\n" - " return 0;\n" - "};\n", - 0 }, - /* Two 56B-slot widens in one fn share ONE slot; frame canaries - * around the appends pin against slot/neighbour overlap. */ - { "same_size_twice", - SIZE_TYPES - "type other = (p48 | i64);\n" - "export fn main() i32 = {\n" - " let canary1: i64 = 111;\n" - " let xs: []small;\n" - " let ys: []other;\n" - " let b: bool = true;\n" - " let v: small = b;\n" - " append(xs, v);\n" - " let canary2: i64 = 222;\n" - " let n: i64 = 7;\n" - " let w: other = n;\n" - " append(ys, w);\n" - " if (canary1 != 111) { return 11; };\n" - " if (canary2 != 222) { return 12; };\n" - " let e: small = xs[0];\n" - " if (!(e is bool)) { return 1; };\n" - " let e2: other = ys[0];\n" - " match (e2) {\n" - " case let q: i64 => { if (q != 7) { return 2; }; };\n" - " case => return 3;\n" - " };\n" - " return 0;\n" - "};\n", - 0 }, - /* Single-size control: one widen, one slot — the common case - * whose asm the size-keying must not perturb. */ - { "single_size_control", - SIZE_TYPES - "export fn main() i32 = {\n" - " let xs: []small;\n" - " let b: bool = true;\n" - " let v: small = b;\n" - " append(xs, v);\n" - " let e: small = xs[0];\n" - " if (!(e is bool)) { return 1; };\n" - " return 0;\n" - "};\n", - 0 }, - /* The third @tagscr site: N_INDEX tagged-element STORE (56B) - * mixed with the 64B sret return. */ - { "idx_store_then_sret", - SIZE_TYPES - "fn f() (p56 | oops | nomem) = {\n" - " let arr: [2]small;\n" - " let b: bool = true;\n" - " let v: small = b;\n" - " arr[1] = v;\n" - " let e: small = arr[1];\n" - " if (!(e is bool)) {\n" - " return \"bad\": oops;\n" - " };\n" - " return p56 { a = 1, b = 2, c = 3, d = 4, e = 5, f = 6, g = 7 };\n" - "};\n" - "export fn main() i32 = {\n" - " match (f()) {\n" - " case let w: p56 => { if (w.g != 7) { return 1; }; };\n" - " case => return 2;\n" - " };\n" - " return 0;\n" - "};\n", - 0 }, -}; - -static const char *g_bin; - -static int -compile_s(const char *tool, const char *src, const char *outpath) -{ - char cmd[1024]; - snprintf(cmd, sizeof cmd, "%s/%s %s > %s 2>&1", - g_bin, tool, src, outpath); - return runwait(cmd); -} - -static int -file_eq(const char *a, const char *b) -{ - char cmd[1024]; - snprintf(cmd, sizeof cmd, "cmp -s %s %s", a, b); - return runwait(cmd) == 0; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - static char absbin[512]; - if (bin[0] != '/') { - char cwd[256]; - if (getcwd(cwd, sizeof cwd) == NULL) return 1; - snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); - bin = absbin; - } - g_bin = bin; - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - for (int i = 0; i < n; i++) { - const struct row *r = &rows[i]; - char tmpdir[96], src[160], cs_s[160], ww_s[160], rmcmd[160]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/tagscr_%d_%d_d", - getpid(), i); - /* 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", - tmpdir, getpid(), i); - snprintf(ww_s, sizeof ww_s, "%s/tagscr_%d_%d_ww.s", - tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); - FILE *f = fopen(src, "wb"); - 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); - 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++; - } - if (fail) { - fprintf(stderr, "tagscr_sizes_run: %d/%d rows failed\n", - fail, total); - return 1; - } - printf("tagscr_sizes_run: %d assembly rows ok\n", total); - return 0; -} diff --git a/test/wcc/989_m1union_run.c b/test/wcc/989_m1union_run.c deleted file mode 100644 index ee352010..00000000 --- a/test/wcc/989_m1union_run.c +++ /dev/null @@ -1,182 +0,0 @@ -/* - * 989_m1union_run — M1 (#199b): a `match` on a tagged union returned by a - * CROSS-MODULE fn must keep each nominal variant on a DISTINCT tag. The - * canonical case is `utf8.next` (encoding.utf8), whose return - * `(rune | done | more | invalid)` has three structurally-identical - * void-alias variants (done/more = void, invalid = !void). Pre-#199b the - * wwstage collapsed done/more/invalid onto one tag (a byte-id-only - * miscompile: the doc's repro hit the rune arm and returned the same value - * either way). This test is GATE-VISIBLE: each arm maps to a distinct - * return code and the inputs deterministically hit each arm, so a collapse - * mis-routes an arm and changes the runtime exit — caught on BOTH stages. - * - * row | input | arm | want_exit - * -------------+------------------+-----------+---------- - * rune | [65] | rune | 1 - * done | [65] next twice | done | 2 - * more | [195] (lead-only)| more | 3 - * invalid | [255] | invalid | 4 - * all_arms | all four, mixed | (1,2,4,3) | 219 (1243 % 256) - * - * Runtime ownership is now r989_m1union_*; this wrapper retains all_arms - * solely for cstage.s == wwstage.s across the separate-package build. - */ -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -/* classify() is shared boilerplate: a cross-module match whose four arms - * each return a distinct code. Each row supplies only the main body. */ -#define PRELUDE \ - "package main;\n" \ - "import encoding.utf8;\n" \ - "fn classify(d: *utf8.decoder) int = {\n" \ - " match (utf8.next(d)) {\n" \ - " case let r: rune => return 1;\n" \ - " case let dn: utf8.done => return 2;\n" \ - " case let m: utf8.more => return 3;\n" \ - " case let e: utf8.invalid => return 4;\n" \ - " };\n" \ - " return 0;\n" \ - "};\n" - -struct row { - const char *label; - const char *body; /* main() body, appended to PRELUDE */ - int want_exit; -}; - -static const struct row rows[] = { - /* Runtime rows moved to r989_m1union_*; retain byte-id source only. */ - { "all_arms", - "export fn main() int = {\n" - "\tlet g: []u8 = [65u8];\n" - "\tlet dg = utf8.decode(g);\n" - "\tlet a = classify(&dg);\n" - "\tlet b = classify(&dg);\n" - "\tlet bad: []u8 = [255u8];\n" - "\tlet db = utf8.decode(bad);\n" - "\tlet c = classify(&db);\n" - "\tlet tr: []u8 = [195u8];\n" - "\tlet dt = utf8.decode(tr);\n" - "\tlet e = classify(&dt);\n" - "\treturn a * 1000 + b * 100 + c * 10 + e;\n" - "};\n", 219 }, -}; - -/* write_src — PRELUDE + row body into . */ -static int -write_src(const char *path, const struct row *r) -{ - FILE *f = fopen(path, "wb"); - if (!f) return -1; - fputs(PRELUDE, f); - fputs(r->body, f); - fclose(f); - 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 cdrv[1024], wdrv[1024]; - 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 pid = (int)getpid(); - - /* rule-10 byte-id gate on the all_arms cross-module match. */ - if (access(wdrv, X_OK) == 0) { - total++; - const struct row *r = &rows[n - 1]; /* all_arms */ - char csrc[80], cstem[80], cmd[512]; - snprintf(csrc, sizeof csrc, "/tmp/m1union_bid_%d.ww", pid); - snprintf(cstem, sizeof cstem, "/tmp/m1union_bid_c_%d", pid); - char wstem[80]; - snprintf(wstem, sizeof wstem, "/tmp/m1union_bid_w_%d", pid); - if (write_src(csrc, r) != 0) { - /* a silent skip here would print "1/1 ok" having - * asserted nothing; nothing was created, so no - * cleanup is owed either. */ - fprintf(stderr, "m1union_run: cannot write %s\n", - csrc); - fail++; - } else { - /* #93 sep layout: each stage emits per-unit asm under - * .sepwork/ (__root.s + encoding.utf8.s); the - * rule-10 byte-id gate becomes a recursive diff of the - * two sepwork trees. */ - snprintf(cmd, sizeof cmd, - "%s build -S -o '%s' '%s' " - "2>/dev/null", cdrv, cstem, csrc); - int cb = runwait(cmd); - snprintf(cmd, sizeof cmd, - "%s build -S -o '%s' '%s' " - "2>/dev/null", wdrv, wstem, csrc); - int wb = runwait(cmd); - if (cb != 0 || wb != 0) { - fprintf(stderr, "m1union_run: byte-id build " - "failed (cstage=%d wwstage=%d)\n", cb, wb); - fail++; - } else { - /* Concat each tree's per-unit *.s (sorted glob - * order is deterministic) and cmp — sidesteps a - * stray .md5tmp the wwstage drops in the - * sep dir, which a full `diff -r` would flag. */ - snprintf(cmd, sizeof cmd, - "cat '%s.sepwork/'*.s > '%s.scat' && " - "cat '%s.sepwork/'*.s > '%s.scat' && " - "cmp -s '%s.scat' '%s.scat'", - cstem, cstem, wstem, wstem, cstem, wstem); - if (runwait(cmd) != 0) { - fprintf(stderr, "m1union_run: cstage.s " - "!= wwstage.s (byte-id break)\n"); - fail++; - } - } - /* exact owned paths only (`build -S` writes solely - * under .sepwork/, plus the .scat concats) — - * no prefix glob; a failed rm must fail the carrier, - * not leak silently. */ - snprintf(cmd, sizeof cmd, - "rm -rf '%s' '%s.scat' '%s.sepwork' " - "'%s.scat' '%s.sepwork'", - csrc, cstem, cstem, wstem, wstem); - if (runwait(cmd) != 0) { - fprintf(stderr, - "m1union_run: cleanup rm failed\n"); - fail++; - } - } - } else { - fprintf(stderr, "m1union_run: skip byte-id (no ww_ww)\n"); - } - - if (fail) { - fprintf(stderr, "m1union_run: %d/%d failed\n", fail, total); - return 1; - } - printf("m1union_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/989_structlocal_frame.c b/test/wcc/989_structlocal_frame.c deleted file mode 100644 index c13c6f0d..00000000 --- a/test/wcc/989_structlocal_frame.c +++ /dev/null @@ -1,208 +0,0 @@ -/* - * 989_structlocal_frame (#75) — a struct LOCAL's stack slot must be the - * struct's NATURAL size rounded to the 8B grain, identically in both - * stages. wwstage's slotsize() TY_STRUCT arm (selfhost/cmd/wcc/ - * cgenutil.ww) returned ti.slotsize — the SUM of the slot-padded field - * widths — so a sub-8 nested-composite struct local over-reserved: a - * nested inner{x:u8,y:u8} (size 2, slotsize 8) padded its in-struct - * footprint, and the local's frame slot inherited that pad. cstage has - * no slotsize SSoT — it reserves the local at f->type->size, the - * checker's NATURAL r.size (cmd/w6c/cgen.c). So on outer{a:u8, - * p:inner{x:u8,y:u8}, z:i64} ww emitted frame $32 / struct-base -24(BP) - * while cstage emitted $16 / -16(BP): a uniform -8 BP shift on every - * field access. Both stages exit 0 (each self-consistent), so it is - * runtime-invisible — but it is a cs!=ww .s divergence (rule 10) and a - * latent byte-id gate-landmine the day such a struct enters the corpus. - * Same dual-SSoT leak as #44 (field-OFFSET) / #55, one notion over: - * struct-local-slot-SIZE. - * - * THE FIX: slotsize()'s TY_STRUCT arm returns round8(ti.size). The - * TUPLE arm (8B/elem slot, user ruling #60) and ARRAY arm (element - * stride, #48) keep ti.slotsize — deliberate, ruled divergences. - * - * This is the FRAME-ABSOLUTE proof: each fixture is compiled through - * cstage w6c AND wwstage w6c_ww and the emitted .s is byte-diffed (the - * 683 asm_byte_identical pattern). Byte-identity subsumes the frame: - * pre-fix nested3 differs at `TEXT main,$32` vs `$16`, `SUBQ $32` vs - * `$16`, and every `-24(BP)` vs `-16(BP)`; post-fix the two .s are - * byte-for-byte equal. The sibling RUNTIME exit-code proof migrated to - * test/lang/nestfield_test.ww (@test field-read rows, cs==ww via the T2 - * byte-id gate); this file is the .s/frame-absolute one #75 owes. - */ -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -struct row { const char *label; const char *src; }; - -static const struct row rows[] = { - /* nested3 — the core divergence. outer{a:u8, p:inner{x:u8,y:u8}, - * z:i64}: natural size 16 (a@0, p@1 packed, z@8), slot-padded sum 24 - * (a:8 + p:8 + z:8). Pre-fix ww frame $32 / base -24(BP); cstage - * $16 / -16(BP). The successor z makes the -8 shift visible on a - * wide field too. */ - { "nested3", - "package main;\n" - "type inner = struct { x: u8, y: u8 };\n" - "type outer = struct { a: u8, p: inner, z: i64 };\n" - "export fn main() int = {\n" - " let o: outer = outer { a = 1, p = inner { x = 2, y = 3 }," - " z = 0x44444444i64 };\n" - " if (o.a: int != 1) { return 1; };\n" - " if (o.p.x: int != 2) { return 2; };\n" - " if (o.p.y: int != 3) { return 3; };\n" - " if (o.z != 0x44444444i64) { return 4; };\n" - " return 0;\n" - "};\n" }, - - /* tail-varied — same sub-8 nested composite, a narrower (u32) tail. - * outt{a:u8, p:inner{x:u8,y:u8}, w:u32}: natural size 8 (w@4), - * slot-padded sum larger. Proves the fix on a second sub-8 shape - * whose natural size differs from nested3's. */ - { "tail_u32", - "package main;\n" - "type inner = struct { x: u8, y: u8 };\n" - "type outt = struct { a: u8, p: inner, w: u32 };\n" - "export fn main() int = {\n" - " let o: outt = outt { a = 1, p = inner { x = 2, y = 3 }," - " w = 0x1234u32 };\n" - " if (o.p.y: int != 3) { return 3; };\n" - " if (o.w != 0x1234u32) { return 5; };\n" - " return 0;\n" - "};\n" }, - - /* flat control — flat{a:u8, b:i64}, no sub-8 composite field. - * Natural and slot-padded coincide (b@8 either way), so the .s is - * byte-id BEFORE and after; proves the fix leaves the common case - * unmoved (no spurious frame change on plain structs). */ - { "flat_ctl", - "package main;\n" - "type flat = struct { a: u8, b: i64 };\n" - "export fn main() int = {\n" - " let o: flat = flat { a = 9, b = 0x33333333i64 };\n" - " if (o.a: int != 9) { return 1; };\n" - " if (o.b != 0x33333333i64) { return 2; };\n" - " return 0;\n" - "};\n" }, -}; - -/* asm_byte_identical — emit .s via cstage w6c and wwstage w6c_ww and - * diff (the 683_arr_strslice_elem harness). Pre-#75 a sub-8 nested - * struct local over-reserved its frame slot on wwstage, so the .s - * diverged (frame size, SUBQ, every field BP offset); the natural-size - * slot makes the two byte-for-byte equal. */ -static int -asm_byte_identical(const char *bin, const struct row *r, int i) -{ - char src[64], cs[64], ws[64], cmd[1024]; - int rc = -1, cleanfail = 0; - snprintf(src, sizeof src, "/tmp/slfr_%d_%d.ww", getpid(), i); - snprintf(cs, sizeof cs, "/tmp/slfr_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/slfr_%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); - if (runwait(cmd) != 0) { - fprintf(stderr, "structlocal_frame[%s]: w6c errored\n", r->label); - goto cleanup; - } - snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null", - bin, ws, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "structlocal_frame[%s]: w6c_ww errored\n", - r->label); - goto cleanup; - } - - { - FILE *fc = fopen(cs, "rb"); - FILE *fw = fopen(ws, "rb"); - 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, "structlocal_frame[%s]: cstage vs wwstage " - "asm differs (#75 struct-local frame)\n", r->label); -cleanup: - /* a failed compile may still leave a partial .s; ENOENT alone is - * benign (the leg failed before creating that output). */ - 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; -} - -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 wdrv[1024]; - snprintf(wdrv, sizeof wdrv, "%s/w6c_ww", bin); - if (access(wdrv, X_OK) != 0) { - fprintf(stderr, "structlocal_frame: skip (no %s)\n", wdrv); - printf("structlocal_frame: skipped (no wwstage)\n"); - return 77; - } - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - for (int i = 0; i < n; i++) { - total++; - if (asm_byte_identical(bin, &rows[i], i) != 0) - fail++; - } - - if (fail) { - fprintf(stderr, - "structlocal_frame: %d/%d fixtures failed\n", fail, total); - return 1; - } - printf("structlocal_frame: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/data/r756_r1_i32_struct/case.ww b/test/wcc/data/r756_r1_i32_struct/case.ww new file mode 100644 index 00000000..7404dc43 --- /dev/null +++ b/test/wcc/data/r756_r1_i32_struct/case.ww @@ -0,0 +1,16 @@ +//ww:run +package main; +type r1 = struct { + a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32, + h: i32, i: i32, j: i32, k: i32, l: i32, m: i32, p: i32, +}; +fn make_r1() r1 = { + let b: r1; + b.p = 99i32; + return b; +}; +export fn main() i32 = { + let s: r1 = make_r1(); + if (s.p != 99i32) { return 11; }; + return 0; +}; diff --git a/test/wcc/data/r756_r2_slice_single/case.ww b/test/wcc/data/r756_r2_slice_single/case.ww new file mode 100644 index 00000000..03d216fa --- /dev/null +++ b/test/wcc/data/r756_r2_slice_single/case.ww @@ -0,0 +1,14 @@ +//ww:run +package main; +type r2_struct = struct { in: []u8, delim: []u8, p: i64 }; +type r2_alias = r2_struct; +fn make_r2() r2_alias = { + let b: r2_struct; + b.p = 99i64; + return b; +}; +export fn main() i32 = { + let s: r2_alias = make_r2(); + if (s.p != 99i64) { return 12; }; + return 0; +}; diff --git a/test/wcc/data/r756_r3_slice_direct/case.ww b/test/wcc/data/r756_r3_slice_direct/case.ww new file mode 100644 index 00000000..fb81d20e --- /dev/null +++ b/test/wcc/data/r756_r3_slice_direct/case.ww @@ -0,0 +1,13 @@ +//ww:run +package main; +type r3 = struct { in: []u8, delim: []u8, p: i64 }; +fn make_r3() r3 = { + let b: r3; + b.p = 99i64; + return b; +}; +export fn main() i32 = { + let s: r3 = make_r3(); + if (s.p != 99i64) { return 13; }; + return 0; +}; diff --git a/test/wcc/data/r756_r4_i32_double/case.ww b/test/wcc/data/r756_r4_i32_double/case.ww new file mode 100644 index 00000000..72bd3cd6 --- /dev/null +++ b/test/wcc/data/r756_r4_i32_double/case.ww @@ -0,0 +1,18 @@ +//ww:run +package main; +type r4_struct = struct { + a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32, + h: i32, i: i32, j: i32, k: i32, l: i32, m: i32, p: i32, +}; +type r4_base = r4_struct; +type r4_alias = r4_base; +fn make_r4() r4_alias = { + let b: r4_alias; + b.p = 99i32; + return b; +}; +export fn main() i32 = { + let s: r4_alias = make_r4(); + if (s.p != 99i32) { return 14; }; + return 0; +}; diff --git a/test/wcc/data/r844_bid_src/case.ww b/test/wcc/data/r844_bid_src/case.ww new file mode 100644 index 00000000..324a299b --- /dev/null +++ b/test/wcc/data/r844_bid_src/case.ww @@ -0,0 +1,7 @@ +//ww:run-exit 1 +package main; +export fn main() i32 = { + let m: size = 16; + if (m >= size(u64)) { return 1; }; + return 0; +}; diff --git a/test/wcc/data/r989_structframe_flat_ctl/case.ww b/test/wcc/data/r989_structframe_flat_ctl/case.ww new file mode 100644 index 00000000..948ace56 --- /dev/null +++ b/test/wcc/data/r989_structframe_flat_ctl/case.ww @@ -0,0 +1,9 @@ +//ww:run +package main; +type flat = struct { a: u8, b: i64 }; +export fn main() int = { + let o: flat = flat { a = 9, b = 0x33333333i64 }; + if (o.a: int != 9) { return 1; }; + if (o.b != 0x33333333i64) { return 2; }; + return 0; +}; diff --git a/test/wcc/data/r989_structframe_nested3/case.ww b/test/wcc/data/r989_structframe_nested3/case.ww new file mode 100644 index 00000000..4ae27b64 --- /dev/null +++ b/test/wcc/data/r989_structframe_nested3/case.ww @@ -0,0 +1,12 @@ +//ww:run +package main; +type inner = struct { x: u8, y: u8 }; +type outer = struct { a: u8, p: inner, z: i64 }; +export fn main() int = { + let o: outer = outer { a = 1, p = inner { x = 2, y = 3 }, z = 0x44444444i64 }; + if (o.a: int != 1) { return 1; }; + if (o.p.x: int != 2) { return 2; }; + if (o.p.y: int != 3) { return 3; }; + if (o.z != 0x44444444i64) { return 4; }; + return 0; +}; diff --git a/test/wcc/data/r989_structframe_tail_u32/case.ww b/test/wcc/data/r989_structframe_tail_u32/case.ww new file mode 100644 index 00000000..fc484327 --- /dev/null +++ b/test/wcc/data/r989_structframe_tail_u32/case.ww @@ -0,0 +1,10 @@ +//ww:run +package main; +type inner = struct { x: u8, y: u8 }; +type outt = struct { a: u8, p: inner, w: u32 }; +export fn main() int = { + let o: outt = outt { a = 1, p = inner { x = 2, y = 3 }, w = 0x1234u32 }; + if (o.p.y: int != 3) { return 3; }; + if (o.w != 0x1234u32) { return 5; }; + return 0; +};