From a82cfaa6ab7ec0995eb7e9a6799e03ba7cb63931 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Sat, 8 Aug 2026 15:03:06 +0900 Subject: [PATCH] test: port the cstage-only rejects to ww; retire 760_def_widen_const + 783_amp_fn_assign_run MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both carriers were already slim: the r76_def_widen_* byte-id legs are owned by the test-data-byteid blanket, 783's positive byte rows by the r78_amp_fn_assign_* fixtures plus that same blanket, and its symmetric non-tagged rejects by the corpus //ww:error fixtures. The unowned remainder is three cstage-only loud-rejects (the 760 narrowing def-ref and 783's two tagged-slot rows); the wwstage over-accepts (no def-init assignability check; #214 void-variant leniency) are documented at the site per the residual audit, never asserted as desired — the rows graduate to //ww:error fixtures when the wwstage side tightens. Bundled: one file, one concern (cstage-reject pins awaiting wwstage graduation). --- Makefile | 3 +- test/misc/reject_test.ww | 107 ++++++++++ test/wcc/760_def_widen_const.c | 202 ------------------ test/wcc/783_amp_fn_assign_run.c | 347 ------------------------------- 4 files changed, 109 insertions(+), 550 deletions(-) create mode 100644 test/misc/reject_test.ww delete mode 100644 test/wcc/760_def_widen_const.c delete mode 100644 test/wcc/783_amp_fn_assign_run.c diff --git a/Makefile b/Makefile index fd3cbd74..66839180 100644 --- a/Makefile +++ b/Makefile @@ -407,7 +407,8 @@ OBJECT_WW_TARGETS = $(OBJECT_WW_TESTS:%=wwtest/%) # run-stderr probes, .wwi round-trip trees, tool-dump AST proofs, # cstage-only rejects). Compiler/driver gates like test/sep: they run # under test-compiler. -MISC_WW_TESTS = test/misc/arrglob_test.ww test/misc/packedwwi_test.ww +MISC_WW_TESTS = test/misc/arrglob_test.ww test/misc/packedwwi_test.ww \ + test/misc/reject_test.ww MISC_WW_TARGETS = $(MISC_WW_TESTS:%=wwtest/%) BOOTSTRAP_WRAPPER_SOURCES = test/wcc/950_selfcheck.c \ test/wcc/991_w6a_ww.c \ diff --git a/test/misc/reject_test.ww b/test/misc/reject_test.ww new file mode 100644 index 00000000..1d8126da --- /dev/null +++ b/test/misc/reject_test.ww @@ -0,0 +1,107 @@ +package reject_test; + +// Cstage-only loud-reject pins, ported from the retired native +// carriers test/wcc/760_def_widen_const.c and +// test/wcc/783_amp_fn_assign_run.c. Each row asserts ONLY the cstage +// reject; the wwstage side over-accepts today and that over-accept is +// documented here (residual audit, test-infra-v2-residual-20260808), +// never asserted as desired — these rows graduate to //ww:error +// corpus fixtures the moment the wwstage checker gains the matching +// reject. +// +// 760 (#113 def_cast_fits): an out-of-range narrowing def-ref must +// fail the cstage build loud — the const-scoped widen must never +// become a silent truncation. wwstage has no def-init assignability +// check at all (pre-existing leanness) and accepts. The r76_def_widen_* +// byte-id legs the carrier also drove are owned by the +// test-data-byteid blanket over those fixtures. +// +// 783 (#206 gate, tagged rows): the two NEGATIVE tagged-slot rows are +// cstage-only pending #214 — wwstage's `(X | void)` tagged +// assignability leniently matches any *fn against the void variant +// (the accept-INVALID hole; its close is required before wwstage can +// be the authoritative checker). The positive byte-id rows are owned +// by the r78_amp_fn_assign_* fixtures plus the test-data-byteid +// blanket; the symmetric non-tagged rejects by the corpus +// r783_amp_fn_assign_neg_* //ww:error fixtures. + +import os; +import os.exec; +import strings; +import testenv; +import time; + +fn fail(label: str, why: str) void = { + let m: str = strings.concat("reject FAIL: ", label, " -- ", why, + "\n"); + os.write(2, m.ptr, m.len: u64); + assert(false); +}; + +fn tmo() time.duration = { + return (180i64 * (time.second: i64)): time.duration; +}; + +@test fn defwidennarrow() void = { + let td: str = testenv.fresh(); + testenv.writefile(strings.concat(td, "/nw.ww"), strings.concat( + "package main;\n", + "def BIG_I32: i32 = 70000;\n", + "def S_I16: i16 = BIG_I32;\n", + "export fn main() i32 = { let s: i16 = S_I16; ", + "return s: i32; };\n")); + let av: []str = [testenv.driver("w6c"), "-o", + strings.concat(td, "/nw.s"), strings.concat(td, "/nw.ww")]; + let co: testenv.commandout; + testenv.runcommand(td, td, "narrow", av, tmo(), &co); + // A deadline kill is not a reject; only a loud EXIT failure is. + if (co.termination != exec.termination.EXIT) { + fail("narrow", "w6c did not terminate normally"); + }; + if (co.code == 0) { + fail("narrow", strings.concat("w6c exited 0 (expected loud ", + "failure, no silent truncation)")); + }; + testenv.clean(td); +}; + +fn cstagerejectrow(label: str, src: str) void = { + let td: str = testenv.fresh(); + testenv.writefile(strings.concat(td, "/main783.ww"), src); + let av: []str = [testenv.driver("ww"), "build", "-o", "main783", + "main783.ww"]; + let co: testenv.commandout; + testenv.runcommand(td, td, label, av, tmo(), &co); + if (co.termination != exec.termination.EXIT) { + fail(label, "cstage build did not terminate normally"); + }; + if (co.code == 0) { + fail(label, "cstage built but expected reject"); + }; + testenv.clean(td); +}; + +@test fn ampfnassigntagged() void = { + // Two same-signature ptr-to-fn variants: a direct &fn is ambiguous + // and must never silently bind to one. + cstagerejectrow("neg_ambiguous_tagged", strings.concat( + "package main;\n", + "type reader = fn(x: i32) i32;\n", + "type writer = fn(x: i32) i32;\n", + "fn myfn(x: i32) i32 = { return x; };\n", + "fn main() i32 = {\n", + " let x: (*reader | *writer | void) = &myfn;\n", + " return 0;\n", + "};\n")); + // Laundering a materialized *fn into a (*reader|void) slot: + // cstage rejects nominally (harec types.c:1039-1066 mirror). + cstagerejectrow("neg_launder_tagged", strings.concat( + "package main;\n", + "type reader = fn(x: i32) i32;\n", + "fn rd(x: i32) i32 = { return x + 1; };\n", + "fn main() i32 = {\n", + " let p = &rd;\n", + " let s: (*reader | void) = p;\n", + " return 0;\n", + "};\n")); +}; diff --git a/test/wcc/760_def_widen_const.c b/test/wcc/760_def_widen_const.c deleted file mode 100644 index 78e02a15..00000000 --- a/test/wcc/760_def_widen_const.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * 760_def_widen_const — const-scoped cross-prim-width integer widening - * in a `def` initializer (PROJECT #113). - * - * The gap: a def-reference types as the referent's DECLARED type, so - * `def INT_MIN: int = I32_MIN;` saw the rhs as i32 and the def-init - * assignability check (cmd/wcc/check.c, type_assignable) rejected i32 → - * int. The fix: in a def initializer the rhs is a flexible constant, so - * a const integer that folds to a concrete primitive width widens to - * the declared integer type exactly as an UNTYPED_INT literal would, - * when the value fits the target (def_cast_fits). This is the - * const-scoped subset of Hare's general integer-widening; non-const - * widening stays stricter in ww (PROJECT #115). It rides the #88 - * eval_def_const fold + stamp, so cgen's literal-only emit lays the - * 8-byte DATA row with no codegen change. - * - * The fix is cstage-only: the wwstage checker (selfhost/cmd/wcc/check.ww, - * "let init / return assignability") intentionally never checks def-init - * assignability (it stays quiet, leaving full inference to the C side), - * so it never rejected the widening — the #88 stamp already laid the - * correct row. Relaxing the cstage aligns the richer side DOWN to the - * leaner side (CLAUDE.md rule 10), and both stages stamp the identical - * folded value, so the emitted asm is byte-identical. - * - * Coverage: - * 1. The r76_def_widen_* fixtures own symmetric build/runtime semantics. - * 2. BYTE-ID: cstage w6c vs wwstage w6c_ww `.s` for every fixture row - * (proves the const-fold stamp is bit-identical across stages). - * 3. FAIL-LOUD (rule 7), cstage only: an out-of-range narrowing - * def-ref (`def S: i16 = BIG;` where BIG overflows i16) must still - * fail the cstage build — def_cast_fits keeps the widening from - * becoming a silent truncation. Asserted on the cstage alone - * because the wwstage has no def-init assignability check at all - * (pre-existing leanness, unchanged by #113). - */ -#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 *path; }; - -static const struct row exec_rows[] = { - { "i32-to-int-neg", - "test/wcc/data/r76_def_widen_i32_int_neg/case.ww" }, - { "i32-to-int-small", - "test/wcc/data/r76_def_widen_i32_int_small/case.ww" }, - { "u64-to-size", - "test/wcc/data/r76_def_widen_u64_size/case.ww" }, -}; - -/* ---- shared .s emit -------------------------------------------------- */ - -static int -emit_s(const char *w6c, const char *src, char *out_s, size_t cap) -{ - char cmd[1024]; - snprintf(cmd, sizeof cmd, "timeout 180 %s -o %s %s 2>/dev/null", - w6c, out_s, src); - (void)cap; - return runwait(cmd); -} - -/* ---- 2. byte-identity of cstage vs wwstage .s ----------------------- */ - -static int -asm_byte_identical(const char *bin, const struct row *r, int i) -{ - char cs[64], ws[64]; - snprintf(cs, sizeof cs, "/tmp/dwc_bi_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/dwc_bi_%d_%d_w.s", getpid(), i); - - char w6c[640], w6c_ww[640]; - snprintf(w6c, sizeof w6c, "%s/w6c", bin); - snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin); - - if (emit_s(w6c, r->path, cs, sizeof cs) != 0) { - fprintf(stderr, "row[%s]: w6c errored\n", r->label); - return -1; - } - if (emit_s(w6c_ww, r->path, ws, sizeof ws) != 0) { - fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label); - unlink(cs); - return -1; - } - - FILE *fc = fopen(cs, "rb"); - FILE *fw = fopen(ws, "rb"); - int rc = 0; - if (!fc || !fw) { - rc = -1; - } else { - for (;;) { - int a = fgetc(fc); - int b = fgetc(fw); - if (a != b) { rc = -1; break; } - if (a == EOF) break; - } - } - if (fc) fclose(fc); - if (fw) fclose(fw); - if (rc != 0) - fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n", - r->label); - unlink(cs); unlink(ws); - return rc; -} - -/* ---- 3. fail-loud (cstage only): out-of-range narrowing def-ref ----- */ - -/* `def BIG: i32 = 70000; def S: i16 = BIG;` — 70000 overflows i16, so - * def_cast_fits says the value does not fit and the const-scoped widen - * does NOT fire; the def-init assignability check then rejects i32 → - * i16. Compile via w6c only; success means the build FAILED as required - * (nonzero w6c exit), never a silent truncation. */ -static const char narrow_src[] = - "def BIG_I32: i32 = 70000;\n" - "def S_I16: i16 = BIG_I32;\n" - "export fn main() i32 = { let s: i16 = S_I16; return s: i32; };\n"; - -static int -cstage_narrow_fails(const char *w6c) -{ - char src[64], s[64], cmd[1024]; - snprintf(src, sizeof src, "/tmp/dwc_nl_%d.ww", getpid()); - snprintf(s, sizeof s, "/tmp/dwc_nl_%d.s", getpid()); - - FILE *f = fopen(src, "wb"); - if (!f) return -1; - wwtest_fputs(narrow_src, f); - fclose(f); - - snprintf(cmd, sizeof cmd, "timeout 180 %s -o %s %s 2>/dev/null", - w6c, s, src); - int rc = runwait(cmd); - unlink(src); unlink(s); - if (rc == 0) { - fprintf(stderr, - "narrow[cstage]: w6c exited 0 (expected loud failure, " - "no silent truncation)\n"); - return -1; - } - if (rc == 124) { - fprintf(stderr, "narrow[cstage]: w6c timed out\n"); - return -1; - } - return 0; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - char absbin[1024]; - if (bin[0] != '/') { - char cwd[1024]; - if (getcwd(cwd, sizeof cwd) == NULL) return 1; - snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); - bin = absbin; - } - - char w6c[1024], w6c_ww[1024]; - snprintf(w6c, sizeof w6c, "%s/w6c", bin); - snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin); - - int have_ww = (access(w6c_ww, X_OK) == 0); - int total = 0, fail = 0; - - int nexec = (int)(sizeof exec_rows / sizeof exec_rows[0]); - /* Runtime rows moved to r76_def_widen_*; retain byte-id and cstage reject. */ - if (have_ww) { - for (int i = 0; i < nexec; i++) { - total++; - if (asm_byte_identical(bin, &exec_rows[i], i) != 0) - fail++; - } - } - - /* 3. fail-loud (cstage only) */ - total++; - if (cstage_narrow_fails(w6c) != 0) fail++; - - if (fail) { - fprintf(stderr, - "def_widen_const: %d/%d fixtures failed\n", fail, total); - return 1; - } - printf("def_widen_const: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/783_amp_fn_assign_run.c b/test/wcc/783_amp_fn_assign_run.c deleted file mode 100644 index c1ed6d50..00000000 --- a/test/wcc/783_amp_fn_assign_run.c +++ /dev/null @@ -1,347 +0,0 @@ -/* - * 783_amp_fn_assign_run — project #206 close. Pins that a bare - * `&fn_name` is assignable into a `*` slot and a - * `(* | void)` tagged slot (the io vstream vtable shape) - * WITHOUT the explicit `(&fn): *alias` cast that the lib/{io,memio, - * fmt,bufio,log} vstream surfaces currently carry. #94 fold-eFinal - * drops those casts wholesale once this is green. - * - * THE FIX (Option C, both stages, checker-only — cgen is a no-op - * fn-pointer reinterpret): type_assignable / isassignable stay fully - * NOMINAL (a materialized `*fn` value laundered into a `*alias` is - * rejected, mirror of harec ref/harec/src/types.c:1039-1066). A - * caller-site gate `assignable_addrfn` (cmd/wcc/check.c) / - * `assignableaddrfn` (selfhost/cmd/wcc/check.ww) admits ONLY a DIRECT - * `&`-of-fn-ident whose signature structurally matches the - * destination's pointed-to fn alias, or — for a tagged dst — the - * single matching ptr-to-fn variant (>=2 same-sig variants is - * ambiguous → reject). This is harec's adopt-the-alias-at-the-`&`-site - * rule (ref/harec/src/check.c:3594-3626) without threading a type - * hint through the bottom-up expression checker. - * - * ROW POLARITY: - * POSITIVE execution assertions now live in the declarative compiler - * corpus. Their residual rows here compile BOTH stages and assert - * cs.s == ww.s (rule-10). `let_call` and `structlit_build` pin the - * let-binding and struct-literal-field-init gate sites. - * `fieldstore_dispatch` pins the eFinal SHIPPING shape: a bare &fn - * field-STORE into one slot of the three-slot io.vtable - * (reader/writer/closer), then a dispatcher match on a *vtable - * POINTER-param. It remains byte-id-clean: the - * single-slot local-composite zero-init divergence (project #213, - * reproduces with the cast form and a pure `(i32|void)` field — NOT a - * #206 regression) is a single-field artifact the real three-slot - * io.vtable does not hit. - * - * The symmetric non-tagged rejection rows (`neg_launder`, - * `neg_samesig`) live in the declarative compiler corpus. Both checker - * paths report the common diagnostic fragment "not assignable". - * - * NEGATIVE tagged-slot rows (`neg_ambiguous_tagged`, - * `neg_launder_tagged`) are pinned CSTAGE-ONLY. cstage REJECTS both. - * wwstage WRONGLY ACCEPTS them: its `(X | void)` tagged-assignability - * leniently matches any `*fn` against the `void` variant (project - * #214, the void-variant OVER-acceptance). #214 is an accept-INVALID - * hole and its close is REQUIRED BEFORE wwstage can become the - * authoritative selfhost checker; these rows graduate to STAGE_WW - * when #214 closes. Mirrors the 777/780/781/782 STAGE_CS carve-out. - * - * GATE POLARITY: must stay GREEN. Red means the #206 gate over- or - * under-accepts or the stage output diverged. - */ -#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; /* local source for asymmetric controls */ - const char *fixture; /* declarative corpus source for byte rows */ - int stage_mask; - int byte_id; /* assert cs.s == ww.s */ - int expect_fail; /* 1 = build MUST fail */ -}; - -static const struct row rows[] = { - /* POSITIVE byte-id: bare &fn into a * let, then call. */ - { "let_call", - NULL, - "test/wcc/data/r78_amp_fn_assign_let_call/case.ww", - STAGE_CS | STAGE_WW, 1, 0 }, - - /* POSITIVE byte-id: struct-literal field-init of a (*alias|void) - * slot with bare &fn (the Hare `vtable{reader=&fn}` shape). Build - * only — no match (project #212 blocks struct-lit-tagged + match - * on wwstage cgen); the checker gate acceptance is what this pins. */ - { "structlit_build", - NULL, - "test/wcc/data/r78_amp_fn_assign_structlit/case.ww", - STAGE_CS | STAGE_WW, 1, 0 }, - - /* POSITIVE byte-id: bare &fn FIELD-STORE into a (*reader|void) slot - * of the THREE-slot vtable (the exact io.vtable reader/writer/closer - * eFinal shape, test 775), then dispatcher match on a *vtable - * POINTER-param. The live-code-address call-through AND the void-arm - * discrimination are exercised, and cs.s == ww.s (rule-10). This is - * the eFinal SHIPPING path, so it MUST be byte-id-clean: the single- - * slot local-composite zero-init divergence (#213) is a single-field - * artifact that the real three-slot io.vtable does not hit. */ - { "fieldstore_dispatch", - NULL, - "test/wcc/data/r78_amp_fn_assign_fieldstore_dispatch/case.ww", - STAGE_CS | STAGE_WW, 1, 0 }, - - /* NEGATIVE cstage-only (#214 wwstage void-variant over-acceptance): - * two same-signature ptr-to-fn variants in the tagged dst — a direct - * &fn is ambiguous and must be rejected, never silently bound to one. */ - { "neg_ambiguous_tagged", - "package main;\n" - "type reader = fn(x: i32) i32;\n" - "type writer = fn(x: i32) i32;\n" - "fn myfn(x: i32) i32 = { return x; };\n" - "fn main() i32 = {\n" - " let x: (*reader | *writer | void) = &myfn;\n" - " return 0;\n" - "};\n", - NULL, - STAGE_CS, 0, 1 }, - - /* NEGATIVE cstage-only (#214): laundering a materialized *fn into a - * (*reader|void) tagged slot. cstage rejects nominally; wwstage's - * void-variant leniency wrongly accepts. */ - { "neg_launder_tagged", - "package main;\n" - "type reader = fn(x: i32) i32;\n" - "fn rd(x: i32) i32 = { return x + 1; };\n" - "fn main() i32 = {\n" - " let p = &rd;\n" - " let s: (*reader | void) = p;\n" - " return 0;\n" - "};\n", - NULL, - STAGE_CS, 0, 1 }, -}; - -static int -cleanup_tmp(const char *tmpdir, const char *base) -{ - char p[1024]; - int rc = 0; - snprintf(p, sizeof p, "%s/%s", tmpdir, base); - if (unlink(p) != 0 && errno != ENOENT) rc = -1; - snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); - if (unlink(p) != 0 && errno != ENOENT) rc = -1; - /* #93: the sep scratch dir + the tmpdir-owned outputs. */ - snprintf(p, sizeof p, "rm -rf %s/%s.sepwork", tmpdir, base); - if (runwait(p) != 0) rc = -1; - if (rmdir(tmpdir) != 0) rc = -1; - return rc; -} - -static int -write_source(const char *path, const struct row *r) -{ - FILE *in = NULL; - if (r->fixture != NULL) { - in = fopen(r->fixture, "rb"); - if (in == NULL) return -1; - } - FILE *f = fopen(path, "wb"); - if (!f) { - if (in != NULL) fclose(in); - return -1; - } - if (in != NULL) { - int ioerr = 0; - for (;;) { - int ch = fgetc(in); - if (ch == EOF) break; - if (fputc(ch, f) == EOF) { ioerr = 1; break; } - } - if (ferror(in)) ioerr = 1; - if (fclose(in) != 0) ioerr = 1; - if (fclose(f) != 0) ioerr = 1; - return ioerr ? -1 : 0; - } else { - if (fputs(r->src, f) == EOF) { - fclose(f); - return -1; - } - } - return fclose(f) == 0 ? 0 : -1; -} - -static int -build_via_driver(const char *driver, const char *tmpdir, const char *src) -{ - char cmd[2048]; - /* #93 sep layout: `-o ` relocates artifacts to - * .sepwork/ under tmpdir. expect_fail rows still fail: a checker - * error makes w6c return nonzero regardless of artifact placement. */ - snprintf(cmd, sizeof cmd, - "cd %s && b='%s'; timeout 180 %s build " - "-o \"${b%%.ww}\" \"$b\" 2>/dev/null", - tmpdir, src, driver); - return runwait(cmd); -} - -/* build_ok — 1 iff the build succeeds (used by expect_fail rows). */ -static int -build_ok(const char *driver, const struct row *r, int seq) -{ - (void)seq; - char tmpdir[] = "/tmp/afa_nf_XXXXXX"; - char src[512], base[64]; - if (mkdtemp(tmpdir) == NULL) return -1; - snprintf(base, sizeof base, "main783"); - snprintf(src, sizeof src, "%s/%s.ww", tmpdir, base); - if (write_source(src, r) != 0) { - if (cleanup_tmp(tmpdir, base) != 0) - fprintf(stderr, "amp_fn_assign[%s]: setup cleanup failed\n", r->label); - return -1; - } - int br = build_via_driver(driver, tmpdir, src); - if (cleanup_tmp(tmpdir, base) != 0) { - fprintf(stderr, "amp_fn_assign[%s]: cleanup failed\n", r->label); - return -1; - } - return br == 0; -} - -/* asm_byte_identical — diff cstage vs wwstage .s. Parallel owned trees - * keep each driver's retained .sepwork/__root.s distinct. */ -static int -asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r, - int seq) -{ - (void)seq; - char src[512], base[64], cs[512], ws[512]; - char tdc[] = "/tmp/afa_c_XXXXXX"; - char tdw[] = "/tmp/afa_w_XXXXXX"; - snprintf(base, sizeof base, "main783"); - if (mkdtemp(tdc) == NULL) return -1; - if (mkdtemp(tdw) == NULL) { - if (cleanup_tmp(tdc, base) != 0) - fprintf(stderr, "amp_fn_assign[%s]: setup cleanup failed\n", r->label); - return -1; - } - snprintf(src, sizeof src, "%s/%s.ww", tdc, base); - int rc = -1; - if (write_source(src, r) != 0) goto out; - if (build_via_driver(cdrv, tdc, src) != 0) goto out; - snprintf(cs, sizeof cs, "%s/%s.sepwork/__root.s", tdc, base); - - snprintf(src, sizeof src, "%s/%s.ww", tdw, base); - if (write_source(src, r) != 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: - if (cleanup_tmp(tdc, base) != 0) { - fprintf(stderr, "amp_fn_assign[%s]: c cleanup failed\n", r->label); - rc = -1; - } - if (cleanup_tmp(tdw, base) != 0) { - fprintf(stderr, "amp_fn_assign[%s]: ww cleanup failed\n", r->label); - rc = -1; - } - return rc; -} - -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 ((r->stage_mask & STAGE_CS) && r->expect_fail) { - total++; - int got = build_ok(cdrv, r, seq++); - if (got != 0) { - if (got > 0) - fprintf(stderr, "amp_fn_assign[cs][%s]: built but expected reject\n", - r->label); - else - fprintf(stderr, "amp_fn_assign[cs][%s]: setup or cleanup failed\n", - r->label); - fail++; - } - } - - if (wwpresent && (r->stage_mask & STAGE_WW)) { - if (r->expect_fail) { - total++; - int got = build_ok(wdrv, r, seq++); - if (got != 0) { - if (got > 0) - fprintf(stderr, "amp_fn_assign[ww][%s]: built but expected reject\n", - r->label); - else - fprintf(stderr, "amp_fn_assign[ww][%s]: setup or cleanup failed\n", - r->label); - fail++; - } - } else if (r->byte_id) { - total++; - if (asm_byte_identical(cdrv, wdrv, r, seq++) != 0) { - fprintf(stderr, "amp_fn_assign[byte-id][%s]: cstage vs wwstage asm differs\n", - r->label); - fail++; - } - } - } - } - - if (fail) { - fprintf(stderr, "amp_fn_assign: %d/%d checks failed\n", fail, total); - return 1; - } - printf("amp_fn_assign: %d/%d ok\n", total, total); - return 0; -}