From a4839d017620a499c60d291a44bd609d6daa8f47 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Sat, 8 Aug 2026 14:30:26 +0900 Subject: [PATCH] test: port 915/949 module-global access carriers to corpus fixtures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 915_arr_module_index_run.c -> r915_arr_mod_idx0 (run), r915_arr_mod_ idx2 (run-exit 1), r915_arr_mod_idx4 (run-exit 6); the single-file rows were already owned by r915_arr_local_* + test-data-byteid. 949_xmod_fnptr_const_run.c -> r949_xmod_fnptr_const_table (run), r949_xmod_fnptr_wrong_sig, r949_xmod_fnptr_nonfn_dotted (error 'not assignable' both stages). The 915 module rows upgrade from 994-delegated to per-fixture cs==ww byte identity; 949's 2-file -I tree was not load-bearing — inline packages exercise the same N_DOT &mod.fn mangle (mafn(leaf, module)). --- internal/wwfixture/types.ww | 12 +- test/wcc/915_arr_module_index_run.c | 327 ----------------- test/wcc/949_xmod_fnptr_const_run.c | 332 ------------------ test/wcc/data/r915_arr_mod_idx0/case.ww | 10 + test/wcc/data/r915_arr_mod_idx2/case.ww | 10 + test/wcc/data/r915_arr_mod_idx4/case.ww | 10 + .../data/r949_xmod_fnptr_const_table/case.ww | 23 ++ .../data/r949_xmod_fnptr_nonfn_dotted/case.ww | 8 + .../data/r949_xmod_fnptr_wrong_sig/case.ww | 8 + 9 files changed, 75 insertions(+), 665 deletions(-) delete mode 100644 test/wcc/915_arr_module_index_run.c delete mode 100644 test/wcc/949_xmod_fnptr_const_run.c create mode 100644 test/wcc/data/r915_arr_mod_idx0/case.ww create mode 100644 test/wcc/data/r915_arr_mod_idx2/case.ww create mode 100644 test/wcc/data/r915_arr_mod_idx4/case.ww create mode 100644 test/wcc/data/r949_xmod_fnptr_const_table/case.ww create mode 100644 test/wcc/data/r949_xmod_fnptr_nonfn_dotted/case.ww create mode 100644 test/wcc/data/r949_xmod_fnptr_wrong_sig/case.ww diff --git a/internal/wwfixture/types.ww b/internal/wwfixture/types.ww index f9e857b1..dbe52578 100644 --- a/internal/wwfixture/types.ww +++ b/internal/wwfixture/types.ww @@ -1,13 +1,13 @@ package wwfixture; def protocolversion: i32 = 1; -def corpuscount: i32 = 1673; -def errorcount: i32 = 341; +def corpuscount: i32 = 1679; +def errorcount: i32 = 343; def compilecount: i32 = 18; -def runcount: i32 = 207; -def runexitcount: i32 = 1107; -def nativecount: i32 = 3346; -def corpushash: str = "a071db5169fe671d40b72d9967caaea053b505f300e64f787f3627ca0f340f5c"; +def runcount: i32 = 209; +def runexitcount: i32 = 1109; +def nativecount: i32 = 3358; +def corpushash: str = "0b3b997170a14de5d526e0d7ce7d5de3c1a33d85db7d029a26e6e01651857b5d"; type directive = enum i32 { ERROR = 0, diff --git a/test/wcc/915_arr_module_index_run.c b/test/wcc/915_arr_module_index_run.c deleted file mode 100644 index a5e3b8b5..00000000 --- a/test/wcc/915_arr_module_index_run.c +++ /dev/null @@ -1,327 +0,0 @@ -/* - * 915_arr_module_index_run — runtime + byte-id net for #128b: a - * cross-package indexed read on an imported global `let arr: [N]T` - * (the `mod.arr[i]` shape) must compute the array's ADDRESS via LEAQ - * and stride by the element type's size. Pre-fix both stages - * mis-compiled this shape, but differently: - * - cstage: emitted `MOVQ mod.arr(SB), AX` (loaded the first 8 bytes - * of the array as if it were a pointer-var value), then ADDed the - * index, then MOVZBQ at that junk address — SEGFAULT-class memory - * corruption on any non-trivial index. cstage also defaulted esz=1 - * (the `bt = n->lhs->type` was NULL for SK_USE module idents). - * - wwstage: had the same MOVQ-not-LEAQ bug; in earlier session - * state also defaulted esz=8 + MOVQ-load (wrong stride + wrong - * load-width). By the time #128b ratified, wwstage was emitting - * IMUL$2 + MOVZWQ correctly (via stamped n.type_ path) but the - * base load remained MOVQ-not-LEAQ. - * - * Fix (per the design's option B — use-site at cgindex/cgassign, NOT - * a provider-shape change at cgdot): - * - cstage: extend `cg_dotbase_addr` (the #135 helper) to recognise - * module-imported `let X: [N]T` and emit `LEAQ X(SB)` for the - * base. Add a `let_var_type(name)` lookup (LetVar gains a `type` - * field) so cgindex's N_INDEX case can fall back to the imported - * let's type when `n->lhs->type` is NULL (SK_USE-bound ident). - * - wwstage: extend `dotbaseaddr` parallel — when the inner ident - * isn't a local (localfindnode → nil) but `letvartnode(base.str)` - * resolves to N_TARRAY, emit `LEAQ symname(SB)`. - * - * Rule-10 alignment: both stages share the same cgindex N_INDEX - * fallback shape; the helper is the use-site fix per #135's pattern, - * preserving cgdot's current MOVQ semantics so whole-array-assign - * shapes (defensive case; not exercised in committed code per - * design-pass audit) aren't disturbed. - * - * Each row carries a `ww build` + run asserting the exit code (the - * cstage-segfault repro now exits cleanly). Single-file rows also carry - * a w6c vs w6c_ww `.s` cmp (rule-10 byte-id); the cross-package module - * rows gate on runtime exit here and are byte-id-covered cross-package - * by 994. The probe table is a test-local exported `[8]u16` (package - * wcmodarr — see the #93 retarget note below; it replaced strconv's - * private `let left_shift_table`, which separate compilation correctly - * hides). - */ -#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; /* main.ww content */ - const char *modname; /* exported-probe-table package, or NULL */ - const char *modsrc; /* that package's content, or NULL */ - int want_exit; -}; - -/* #93 (915 encapsulation retarget, ken-t): the module rows used to read - * strconv's PRIVATE `let left_shift_table` — which separate compilation - * CORRECTLY hides (the combined path only "worked" because the private - * symbol leaked into one unit). Retargeted to a test-LOCAL package that - * EXPORTS its own probe table: the same cross-package `mod.arr[i]` cgen - * shape (LEAQ base + element-size stride, #128b), encapsulation-faithful - * and sep-linkable. (The deeper checker tighten — loud-reject a cross-pkg - * private ref — is tracked as #45; NOT attempted here.) Table prefix is - * the same u16 sequence strconv used, so the expected exits are unchanged - * (idx0→0, idx2→0x0801&0xFF=1, idx4→0x1006&0xFF=6). */ -#define PROBE_MOD \ - "package wcmodarr;\n" \ - "export let probe_table: [8]u16 = [0u16, 0x0800u16, 0x0801u16, " \ - "0x0803u16, 0x1006u16, 0x1009u16, 0x100Du16, 0x1812u16];\n" - -static const struct row rows[] = { - /* Indexed read on an imported (exported) [N]u16 at idx=0 — load- - * width fix: MOVZWQ reads the full u16 = 0x0000 → exit 0. */ - { "mod_u16_idx0", - "package main;\n" - "import wcmodarr;\n" - "export fn main() i32 = {\n" - " let v: u32 = (wcmodarr.probe_table[0]: u32);\n" - " return v: i32;\n" - "};\n", "wcmodarr", PROBE_MOD, 0 }, - /* Nontrivial idx — pre-fix segfault repro. table[2] = 0x0801; - * 0x0801 mod 256 = 1. */ - { "mod_u16_idx2", - "package main;\n" - "import wcmodarr;\n" - "export fn main() i32 = {\n" - " let v: u32 = (wcmodarr.probe_table[2]: u32);\n" - " return v: i32;\n" - "};\n", "wcmodarr", PROBE_MOD, 1 }, - /* Further idx — pins per-element stride. table[4] = 0x1006; - * 0x1006 mod 256 = 6. */ - { "mod_u16_idx4", - "package main;\n" - "import wcmodarr;\n" - "export fn main() i32 = {\n" - " let v: u32 = (wcmodarr.probe_table[4]: u32);\n" - " return v: i32;\n" - "};\n", "wcmodarr", PROBE_MOD, 6 }, - /* Local-array control: confirms the existing IDENT-base path is - * unchanged by the #128b cgindex edit. */ - { "local_arr_control", - "package main;\n" - "export fn main() i32 = {\n" - " let a: [4]u16 = [10u16, 20u16, 30u16, 40u16];\n" - " let v: u32 = (a[2]: u32);\n" - " return v: i32;\n" - "};\n", NULL, NULL, 30 }, - /* Same-package (NOT module-qualified) access — pins the in-package - * IDENT path stays unchanged; uses a local table. */ - { "local_u16_idx_nonzero", - "package main;\n" - "export fn main() i32 = {\n" - " let t: [8]u16 = [0u16, 0x0800u16, 0x0801u16, 0x0803u16, " - "0x1006u16, 0x1009u16, 0x100Du16, 0x1812u16];\n" - " let v: u32 = (t[2]: u32);\n" - " return v: i32;\n" - "};\n", NULL, NULL, 1 }, - { NULL, NULL, 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, "modarridx: w6c_ww missing — cannot run " - "the cs==ww byte-id gate (the whole point of this test)\n"); - return 1; - } - - int n = 0, fail = 0; - for (int i = 0; rows[i].src; i++, n++) { - char tmpdir[] = "/tmp/wwmoda_XXXXXX"; - char src[160], moddir[224] = {0}, modf[320] = {0}; - char stem[200] = {0}, scratch[232] = {0}; - char cs_s[200] = {0}, ws_s[200] = {0}, cmd[2048]; - int rowfail = 0, moddir_owned = 0; - if (mkdtemp(tmpdir) == NULL) { - fprintf(stderr, "row[%s]: workspace acquisition failed\n", - rows[i].label); - fail++; - continue; - } - snprintf(src, sizeof src, "%s/main915.ww", tmpdir); - FILE *f = fopen(src, "wb"); - if (f == NULL) { - fprintf(stderr, "row[%s]: source create failed\n", - rows[i].label); - rowfail = 1; - goto row_done; - } - fputs(rows[i].src, f); - int werr = ferror(f); - if (fclose(f) != 0) werr = 1; - if (werr) { - fprintf(stderr, "row[%s]: source write failed\n", - rows[i].label); - rowfail = 1; - goto row_done; - } - - /* Exported-probe-table package lives at //.ww - * so the sep dep-scan (rooted at the entry's dir) resolves the - * `import ;`. */ - if (rows[i].modname != NULL) { - snprintf(moddir, sizeof moddir, "%s/%s", tmpdir, - rows[i].modname); - if (mkdir(moddir, 0755) != 0) { - fprintf(stderr, "row[%s]: module directory create failed\n", - rows[i].label); - rowfail = 1; - goto row_done; - } - moddir_owned = 1; - snprintf(modf, sizeof modf, "%s/%s.ww", moddir, - rows[i].modname); - FILE *mf = fopen(modf, "wb"); - if (mf == NULL) { - fprintf(stderr, "row[%s]: module source create failed\n", - rows[i].label); - rowfail = 1; - goto row_done; - } - fputs(rows[i].modsrc, mf); - werr = ferror(mf); - if (fclose(mf) != 0) werr = 1; - if (werr) { - fprintf(stderr, "row[%s]: module source write failed\n", - rows[i].label); - rowfail = 1; - goto row_done; - } - } - /* Single-file runtime behavior moved to r915_arr_* wwfixtures. - * Keep auxiliary-package rows here because the fixture grammar - * cannot materialize their directory/import topology. */ - if (rows[i].modname != NULL) { - - /* #93 sep layout: build via `-o ` (the exported - * probe table links across packages under separate compilation); - * all outputs stay under tmpdir. */ - snprintf(stem, sizeof stem, "%s/main915", tmpdir); - snprintf(scratch, sizeof scratch, "%s.sepwork", stem); - snprintf(cmd, sizeof cmd, - "cd %s && %s/ww build -o %s %s " - ">/dev/null 2>&1", tmpdir, bin, stem, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: cstage build failed\n", - rows[i].label); - rowfail = 1; - goto row_done; - } - - int got = runwait(stem); - if (got != rows[i].want_exit) { - fprintf(stderr, "row[%s]: cstage exit %d, want %d\n", - rows[i].label, got, rows[i].want_exit); - rowfail = 1; - } - } - - /* Byte-id leg — bare w6c vs w6c_ww (the compiler binaries, NOT - * the driver, so layout-invariant). Only single-file rows can be - * fed standalone; the module rows' runtime exit IS their gate - * (+ the 994 bootstrap corpus for the cross-stage byte-id). */ - if (rows[i].modname == NULL) { - snprintf(cs_s, sizeof cs_s, "%s/cs.s", tmpdir); - snprintf(ws_s, sizeof ws_s, "%s/ww.s", tmpdir); - snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", - w6c, cs_s, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: w6c failed\n", - rows[i].label); - rowfail = 1; - } else { - snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", - w6c_ww, ws_s, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: w6c_ww failed\n", - rows[i].label); - rowfail = 1; - } else if (slurp_eq(cs_s, ws_s) != 0) { - fprintf(stderr, - "row[%s]: cstage/wwstage .s DIFFER " - "(rule-10 byte-id violation)\n", - rows[i].label); - rowfail = 1; - } - } - } - - row_done: - { - int cleanfail = 0; - if (cs_s[0] && unlink(cs_s) != 0 && errno != ENOENT) - cleanfail = 1; - if (ws_s[0] && unlink(ws_s) != 0 && errno != ENOENT) - cleanfail = 1; - if (scratch[0]) { - snprintf(cmd, sizeof cmd, "rm -rf %s", scratch); - if (runwait(cmd) != 0) cleanfail = 1; - } - if (stem[0] && unlink(stem) != 0 && errno != ENOENT) - cleanfail = 1; - if (modf[0] && unlink(modf) != 0 && errno != ENOENT) - cleanfail = 1; - if (moddir_owned && rmdir(moddir) != 0) cleanfail = 1; - if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1; - if (rmdir(tmpdir) != 0) cleanfail = 1; - if (cleanfail) { - fprintf(stderr, "row[%s]: workspace cleanup failed\n", - rows[i].label); - rowfail = 1; - } - } - if (rowfail) fail++; - } - - if (fail) { - fprintf(stderr, "%d/%d mod-arr-idx tests failed\n", fail, n); - return 1; - } - printf("modarridx: %d/%d ok (cstage run + cs==ww byte-id)\n", - n, n); - return 0; -} diff --git a/test/wcc/949_xmod_fnptr_const_run.c b/test/wcc/949_xmod_fnptr_const_run.c deleted file mode 100644 index d040ac25..00000000 --- a/test/wcc/949_xmod_fnptr_const_run.c +++ /dev/null @@ -1,332 +0,0 @@ -/* - * 949_xmod_fnptr_const_run — #124: a CROSS-MODULE `&module.fn` inside a - * module-level const emits its &fn->DATAR reloc + the wwstage checker - * accepts the const table. - * - * #117/#119 wired the &fn->DATAR reloc for SAME-module `&fn` only. The - * cgen detect helper (node_fnptr_sym cgen.c / nodefnptr cgen.ww) was - * N_IDENT-only and mangled the leaf with cur_mod — so a cross-module - * `&cc.isa` (operand is N_DOT) returned NULL → no reloc → no DATA → - * w6l undefined-reference, BOTH stages. fold-6's charclass_map needs - * 12x cross-module `&ascii.isXXX` inside a const `[](str, *fn(rune)bool)`. - * - * The #124 fix is ONE both-stage, consumer-coupled commit: - * - cgen reloc (BOTH stages): node_fnptr_sym / nodefnptr grow an N_DOT - * arm forming mafn(leaf, MODULE-ident) — the same TEXT symbol the - * working runtime `&mod.fn` / direct call already emit. Shared by the - * scalar (#119) and tuple-row (#117) callers (one helper = SSoT). - * - wwstage checker (ww-only, align-UP): exprtype's TK_AMP arm now - * types a cross-module `&mod.fn` as `*fn(...)` (the N_DOT twin of the - * #206 N_IDENT synthesis, via scopelookupinmodule) — so the const - * table's nested cross-module &fn element typeeqs its `*fn` slot and - * isassignable AFFIRMATIVELY accepts (cstage types `&mod.fn` as `*fn` - * natively → ww aligns up to cstage's actual acceptance reason). The - * SK_FN gate keeps the accept narrow to genuine fn-pointers. - * - * POLARITY: align-BOTH — both stages were LOUD at base 754944a (cstage - * link-fail, wwstage checker reject). byte-id is the wwstage-correctness - * net (#263); the fn-reloc is pinned at runtime with DISTINCT fns so a - * wrong reloc (or swapped per-row offset) is caught, not masked. - * - * row | shape | exit | byte-id - * ----------------+------------------------------------+------+-------- - * xmod_table_scalar| const tbl:[](str,*fn) of (&cc.isa,| 0 | cs==ww - * | &cc.isz) DISTINCT + scalar &cc.isa| | - * | — index tbl[i].1, call each back | | - * wrong_sig_table | const t:[](str,*fn(i32)i32) = | LOUD | (n/a) - * | [(..,&cc.isa)] — sig mismatch must | | - * | reject BOTH (pins the *fn typeeq | | - * | is real, not accept-everything) | | - * nonfn_dotted_ | const t:[](str,*fn) =[(..,&cc.kval)| LOUD | (n/a) - * table | )] — & must reject | | - * | BOTH (pins the SK_FN/N_FNDECL gate | | - * | — the #117 over-accept hazard) | | - * - * Cross-module via OWN 2-module fixtures in a PRIVATE mktemp dir (784 - * model) — NOT the real lib/ascii: ordinary build retains its caller-owned - * .sepwork tree, so importing lib/ would race; a synthetic module - * `cc` exercises the IDENTICAL `&mod.fn` mangle (mafn(leaf, module)). - * NNN<950 so test-unit runs it; the private mktemp dir makes the ww_ww - * driver race-free (946 ww_ww-driver precedent). - */ -#include -#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 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; -} - -#define K_RUN 0 /* build+run BOTH drivers, exit==want, + cs==ww byte-id */ -#define K_BUILDERR 1 /* build must FAIL on BOTH drivers (rule 7 loud) */ - -struct file { const char *name; const char *src; }; - -struct scenario { - const char *label; - const struct file *files; /* name==NULL terminates */ - int kind; - int want_exit; -}; - -/* The fold-6 consumer shape: a const cross-module `[](str,*fn(rune)bool)` - * + a scalar `*fn` global, both initialised with `&cc.`. DISTINCT - * predicates per row so a wrong reloc routes the wrong fn and a call-back - * catches it. The tuple's *fn is element .1 (the consumer ordering). */ -static const struct file table_files[] = { - { "cc.ww", - "package cc;\n" - "export fn isa(c: rune) bool = { return c == 'a'; };\n" - "export fn isz(c: rune) bool = { return c == 'z'; };\n" }, - { "main.ww", - "package main;\n" - "import cc;\n" - "const tbl: [](str, *fn(c: rune) bool) =" - " [(\":a\", &cc.isa), (\":z\", &cc.isz)];\n" - "const pf: *fn(c: rune) bool = &cc.isa;\n" - "export fn main() i32 = {\n" - " if (len(tbl) != 2) { return 10; };\n" - " let e0 = tbl[0];\n" - " let e1 = tbl[1];\n" - " let f0 = e0.1;\n" - " let f1 = e1.1;\n" - " if (!(*f0)('a')) { return 1; };\n" - " if ((*f0)('z')) { return 2; };\n" - " if (!(*f1)('z')) { return 3; };\n" - " if ((*f1)('a')) { return 4; };\n" - " if (!(*pf)('a')) { return 5; };\n" - " if ((*pf)('z')) { return 6; };\n" - " return 0;\n" - "};\n" }, - { NULL, NULL } -}; - -/* The SK_FN-gated bound: a cross-module &fn whose SIGNATURE doesn't match - * the declared `*fn` slot must still reject on BOTH stages (the *fn typeeq - * is real — the lever is affirmative recognition, not accept-everything). */ -static const struct file wrong_sig_files[] = { - { "cc.ww", - "package cc;\n" - "export fn isa(c: rune) bool = { return c == 'a'; };\n" }, - { "main.ww", - "package main;\n" - "import cc;\n" - "const t: [](str, *fn(x: i32) i32) = [(\":a\", &cc.isa)];\n" - "export fn main() i32 = { return len(t): i32; };\n" }, - { NULL, NULL } -}; - -/* The SK_FN/N_FNDECL-gated bound (distinct axis from wrong_sig_files): - * a cross-module dotted address-of a NON-fn export (`&cc.kval`, kval an - * exported var) must NOT synthesize a `*fn` and must reject the table on - * BOTH stages. This locks the lever's narrowness — the over-accept hazard - * reviewer-117 caught — so a future widening of the exprtype N_DOT arm - * past genuine fn-pointers is a loud test failure, not a silent accept. */ -static const struct file nonfn_dotted_files[] = { - { "cc.ww", - "package cc;\n" - "export let kval: int = 7;\n" }, - { "main.ww", - "package main;\n" - "import cc;\n" - "const t: [](str, *fn(c: rune) bool) = [(\":k\", &cc.kval)];\n" - "export fn main() i32 = { return len(t): i32; };\n" }, - { NULL, NULL } -}; - -static const struct scenario scenarios[] = { - { "xmod_table_scalar", table_files, K_RUN, 0 }, - { "wrong_sig_table", wrong_sig_files, K_BUILDERR, 0 }, - { "nonfn_dotted_table", nonfn_dotted_files, K_BUILDERR, 0 }, -}; - -/* Build `driver build -o /` in the fixture dir; returns - * the driver's build rc and (for K_RUN) runs the produced binary, comparing - * exit to want_exit. #94: the caller-visible per-package asm is retained in - * /.sepwork/ until this carrier finishes observing it. */ -static int -build_run(const char *dir, const char *driver, const struct scenario *sc, - const char *stem) -{ - char cmd[4096], path[1024]; - snprintf(cmd, sizeof cmd, - "cd %s && %s build -I %s -o %s/%s " - "%s/main.ww >/dev/null 2>%s/err", - dir, driver, dir, dir, stem, dir, dir); - int brc = runwait(cmd); - if (sc->kind == K_BUILDERR) { - if (brc == 0) { - fprintf(stderr, "949[%s]: %s built but expected LOUD " - "reject\n", sc->label, driver); - return -1; - } - return 0; - } - if (brc != 0) { - fprintf(stderr, "949[%s]: %s build failed\n", sc->label, driver); - return -1; - } - snprintf(path, sizeof path, "%s/%s", dir, stem); - int got = runwait(path); - if (got != sc->want_exit) { - fprintf(stderr, "949[%s]: %s exit %d, want %d\n", - sc->label, driver, got, sc->want_exit); - return -1; - } - return 0; -} - -static int -run_scenario(const char *bin, const struct scenario *sc) -{ - char dir[] = "/tmp/ww949_XXXXXX"; - if (mkdtemp(dir) == NULL) { - fprintf(stderr, "949[%s]: mkdtemp failed\n", sc->label); - return -1; - } - - char path[1024], cmd[4096], csdrv[2200], wwdrv[2200]; - int rc = 0; - - for (int i = 0; sc->files[i].name; i++) { - snprintf(path, sizeof path, "%s/%s", dir, sc->files[i].name); - FILE *f = fopen(path, "wb"); - if (!f) { fprintf(stderr, "949[%s]: write %s\n", sc->label, - sc->files[i].name); rc = -1; goto done; } - int writefail = fputs(sc->files[i].src, f) == EOF; - if (fclose(f) != 0) writefail = 1; - if (writefail) { - fprintf(stderr, "949[%s]: write %s\n", sc->label, - sc->files[i].name); - rc = -1; - goto done; - } - } - - /* cstage driver build (+run for K_RUN) → /main_c.sepwork/. */ - snprintf(csdrv, sizeof csdrv, "%s/ww", bin); - if (build_run(dir, csdrv, sc, "main_c") != 0) { rc = -1; goto done; } - - /* wwstage driver build (+run for K_RUN) — race-free in the - * private mktemp dir. Direct proof the wwstage emits a runnable - * binary. Distinct stem so its sepwork doesn't clobber cstage's. */ - snprintf(wwdrv, sizeof wwdrv, "%s/ww_ww", bin); - if (build_run(dir, wwdrv, sc, "main_w") != 0) { rc = -1; goto done; } - - if (sc->kind == K_BUILDERR) goto done; - - /* Byte-id net (#263 — the wwstage-correctness proof on a gate-blind - * align-BOTH fix): the per-package w6c-emitted .s (cstage sep build) - * vs w6c_ww-emitted .s (wwstage sep build) must be byte-identical. - * Concat each stem's per-package sepwork asm (sorted glob = same set). */ - { - char cs_s[1024], ws_s[1024]; - snprintf(cs_s, sizeof cs_s, "%s/all_cs.s", dir); - snprintf(ws_s, sizeof ws_s, "%s/all_ww.s", dir); - snprintf(cmd, sizeof cmd, "cat %s/main_c.sepwork/*.s > %s 2>/dev/null", - dir, cs_s); - int cs_cat = runwait(cmd); - snprintf(cmd, sizeof cmd, "cat %s/main_w.sepwork/*.s > %s 2>/dev/null", - dir, ws_s); - int ws_cat = runwait(cmd); - if (cs_cat != 0 || ws_cat != 0) { - fprintf(stderr, "949[%s]: could not aggregate retained " - "sepwork asm\n", sc->label); - rc = -1; - } else if (slurp_eq(cs_s, ws_s) != 0) { - fprintf(stderr, "949[%s]: cs.s/ww.s DIFFER (rule-10 " - "byte-id violation)\n", sc->label); - rc = -1; - } - } - -done: - { - int cleanfail = 0; - snprintf(cmd, sizeof cmd, "rm -rf %s/main_c.sepwork", dir); - if (runwait(cmd) != 0) cleanfail = 1; - snprintf(cmd, sizeof cmd, "rm -rf %s/main_w.sepwork", dir); - if (runwait(cmd) != 0) cleanfail = 1; - for (int i = 0; sc->files[i].name; i++) { - snprintf(path, sizeof path, "%s/%s", dir, sc->files[i].name); - if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1; - } - const char *children[] = { - "main_c", "main_w", "err", "all_cs.s", "all_ww.s", NULL - }; - for (int i = 0; children[i]; i++) { - snprintf(path, sizeof path, "%s/%s", dir, children[i]); - if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1; - } - if (rmdir(dir) != 0) cleanfail = 1; - if (cleanfail) { - fprintf(stderr, "949[%s]: temporary cleanup failed\n", - sc->label); - rc = -1; - } - } - return rc; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - char absbin[2048]; - 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[2100]; - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - if (access(wdrv, X_OK) != 0) { - fprintf(stderr, "949: ww_ww missing — cannot run the cs==ww " - "byte-id gate (the whole point of this test)\n"); - return 1; - } - - int n = (int)(sizeof scenarios / sizeof scenarios[0]); - int fail = 0; - for (int i = 0; i < n; i++) { - if (run_scenario(bin, &scenarios[i]) != 0) - fail++; - } - - if (fail) { - fprintf(stderr, "949 xmod_fnptr_const: %d/%d scenarios failed\n", - fail, n); - return 1; - } - printf("xmod_fnptr_const: %d/%d ok (cs+ww run + cs==ww byte-id)\n", - n, n); - return 0; -} diff --git a/test/wcc/data/r915_arr_mod_idx0/case.ww b/test/wcc/data/r915_arr_mod_idx0/case.ww new file mode 100644 index 00000000..9b339767 --- /dev/null +++ b/test/wcc/data/r915_arr_mod_idx0/case.ww @@ -0,0 +1,10 @@ +//ww:run +// migrated from test/wcc/915_arr_module_index_run.c: cross-package mod.arr[0] — LEAQ base + u16 load width (#128b). +package wcmodarr; +export let probe_table: [8]u16 = [0u16, 0x0800u16, 0x0801u16, 0x0803u16, 0x1006u16, 0x1009u16, 0x100Du16, 0x1812u16]; +package main; +import wcmodarr; +export fn main() i32 = { + let v: u32 = (wcmodarr.probe_table[0]: u32); + return v: i32; +}; diff --git a/test/wcc/data/r915_arr_mod_idx2/case.ww b/test/wcc/data/r915_arr_mod_idx2/case.ww new file mode 100644 index 00000000..9b75104b --- /dev/null +++ b/test/wcc/data/r915_arr_mod_idx2/case.ww @@ -0,0 +1,10 @@ +//ww:run-exit 1 +// migrated from test/wcc/915_arr_module_index_run.c: nontrivial index — the pre-fix MOVQ-not-LEAQ segfault repro; 0x0801 mod 256 = 1. +package wcmodarr; +export let probe_table: [8]u16 = [0u16, 0x0800u16, 0x0801u16, 0x0803u16, 0x1006u16, 0x1009u16, 0x100Du16, 0x1812u16]; +package main; +import wcmodarr; +export fn main() i32 = { + let v: u32 = (wcmodarr.probe_table[2]: u32); + return v: i32; +}; diff --git a/test/wcc/data/r915_arr_mod_idx4/case.ww b/test/wcc/data/r915_arr_mod_idx4/case.ww new file mode 100644 index 00000000..57e68b5f --- /dev/null +++ b/test/wcc/data/r915_arr_mod_idx4/case.ww @@ -0,0 +1,10 @@ +//ww:run-exit 6 +// migrated from test/wcc/915_arr_module_index_run.c: per-element stride pin; 0x1006 mod 256 = 6. +package wcmodarr; +export let probe_table: [8]u16 = [0u16, 0x0800u16, 0x0801u16, 0x0803u16, 0x1006u16, 0x1009u16, 0x100Du16, 0x1812u16]; +package main; +import wcmodarr; +export fn main() i32 = { + let v: u32 = (wcmodarr.probe_table[4]: u32); + return v: i32; +}; diff --git a/test/wcc/data/r949_xmod_fnptr_const_table/case.ww b/test/wcc/data/r949_xmod_fnptr_const_table/case.ww new file mode 100644 index 00000000..48cb03f4 --- /dev/null +++ b/test/wcc/data/r949_xmod_fnptr_const_table/case.ww @@ -0,0 +1,23 @@ +//ww:run +// migrated from test/wcc/949_xmod_fnptr_const_run.c: cross-module &cc.fn inside const table + scalar emits the &fn->DATAR reloc (#124); DISTINCT predicates catch a swapped reloc. +package cc; +export fn isa(c: rune) bool = { return c == 'a'; }; +export fn isz(c: rune) bool = { return c == 'z'; }; +package main; +import cc; +const tbl: [](str, *fn(c: rune) bool) = [(":a", &cc.isa), (":z", &cc.isz)]; +const pf: *fn(c: rune) bool = &cc.isa; +export fn main() i32 = { + if (len(tbl) != 2) { return 10; }; + let e0 = tbl[0]; + let e1 = tbl[1]; + let f0 = e0.1; + let f1 = e1.1; + if (!(*f0)('a')) { return 1; }; + if ((*f0)('z')) { return 2; }; + if (!(*f1)('z')) { return 3; }; + if ((*f1)('a')) { return 4; }; + if (!(*pf)('a')) { return 5; }; + if ((*pf)('z')) { return 6; }; + return 0; +}; diff --git a/test/wcc/data/r949_xmod_fnptr_nonfn_dotted/case.ww b/test/wcc/data/r949_xmod_fnptr_nonfn_dotted/case.ww new file mode 100644 index 00000000..9fcdc822 --- /dev/null +++ b/test/wcc/data/r949_xmod_fnptr_nonfn_dotted/case.ww @@ -0,0 +1,8 @@ +//ww:error "not assignable" +// migrated from test/wcc/949_xmod_fnptr_const_run.c: &cc.kval (an exported VAR) must not synthesize a *fn — the SK_FN gate stays narrow (#117 over-accept hazard). +package cc; +export let kval: int = 7; +package main; +import cc; +const t: [](str, *fn(c: rune) bool) = [(":k", &cc.kval)]; +export fn main() i32 = { return len(t): i32; }; diff --git a/test/wcc/data/r949_xmod_fnptr_wrong_sig/case.ww b/test/wcc/data/r949_xmod_fnptr_wrong_sig/case.ww new file mode 100644 index 00000000..3467d24c --- /dev/null +++ b/test/wcc/data/r949_xmod_fnptr_wrong_sig/case.ww @@ -0,0 +1,8 @@ +//ww:error "not assignable" +// migrated from test/wcc/949_xmod_fnptr_const_run.c: cross-module &fn with a mismatched signature must not satisfy the declared *fn slot. +package cc; +export fn isa(c: rune) bool = { return c == 'a'; }; +package main; +import cc; +const t: [](str, *fn(x: i32) i32) = [(":a", &cc.isa)]; +export fn main() i32 = { return len(t): i32; };