From a95a7a316b2e1ad9a1c07423701ff490f1c715a9 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Fri, 7 Aug 2026 23:02:47 +0900 Subject: [PATCH] test/wcc: carrier ownership repair and driver-contract adaptation Every surviving carrier now owns its artifacts: checked mkdir/mkdtemp/ fopen acquisition, one all-exit cleanup funnel per carrier, ENOENT- tolerant checked unlinks, exact-path deletion (rm -rf only for an owned pid-keyed dir or a .sepwork beneath one), and cleanup failure fails a passing carrier without overwriting its diagnostic. In the same pass the carriers adapt to the driver contract this branch lands: --sep and WW_PKGCACHE are gone, -S and the /tmp/ww_run_ scratch contract are asserted, and rows whose runtime or reject coverage moved to test/wcc/data fixtures or test/lang @test owners are trimmed to the byte/artifact/diagnostic observations only they can make. Repair and adaptation ride together because most files interleave both in the same hunks; splitting would manufacture intermediate carrier states that never existed and cannot run against either driver. --- test/wcc/110_uniesc_run.c | 78 ++- test/wcc/610_arch.c | 52 +- test/wcc/631_def_neg_global.c | 120 ++--- test/wcc/632_w6l_manyflags.c | 115 +++-- test/wcc/671_struct_packed.c | 196 +++++--- test/wcc/682_arr_enum_elem.c | 78 ++- test/wcc/683_arr_strslice_elem.c | 79 ++- test/wcc/684_arr_infer_len.c | 123 +++-- test/wcc/685_arr_tagged_elem.c | 124 +++-- test/wcc/686_slice_str_global_zero.c | 71 ++- test/wcc/687_slice_literal_global.c | 129 +++-- test/wcc/688_global_arr_elem_field.c | 69 ++- test/wcc/689_arrglob_nodup_run.c | 65 ++- test/wcc/689_globptr_field_read_run.c | 89 ++-- test/wcc/689_globptr_field_store_run.c | 89 ++-- test/wcc/689_structlit_slice_field.c | 71 ++- test/wcc/696_modtype_leaf_collision.c | 91 ++-- test/wcc/697_samemod_prefer.c | 40 +- test/wcc/698_cgreturn_struct.c | 102 +++- test/wcc/699_use_promote_alias.c | 51 +- test/wcc/700_e2e.c | 76 ++- test/wcc/706_fnlabel_mangle.c | 53 +- test/wcc/708_param_shadow_mod.c | 206 ++++++-- test/wcc/712_redecl.c | 189 +++----- test/wcc/718_struct_multi_return_scratch.c | 66 +-- test/wcc/732_def_const_fold.c | 96 +--- test/wcc/737_direnum.c | 445 ++++++++++++++--- test/wcc/744_letcopy_struct.c | 115 +---- test/wcc/749_sumtype_forward.c | 165 ++++--- test/wcc/750_mklabel_modscoped.c | 112 +++-- test/wcc/752_modparam_callee.c | 111 +++-- test/wcc/753_convwrap_audit.c | 45 +- test/wcc/754_slice_of_slice_index.c | 45 +- test/wcc/755_amp_dot_idx.c | 45 +- test/wcc/756_alias_chain_unwrap.c | 115 ++--- test/wcc/758_cgalloc_str_field.c | 208 ++------ test/wcc/760_def_widen_const.c | 140 +----- test/wcc/764_amp_fn_ident.c | 219 +++++---- test/wcc/765_star_fn_deref.c | 39 +- test/wcc/771_widen_transitive.c | 360 ++++++-------- test/wcc/779_log_vstream_run.c | 70 +-- test/wcc/783_amp_fn_assign_run.c | 310 +++++------- test/wcc/784_xmod_alias_struct_collide_run.c | 75 ++- .../wcc/785_structvariant_largeunion_return.c | 114 ++--- test/wcc/786_narrow_alias_deref_store.c | 114 ++--- test/wcc/787_xmod_variant_match.c | 84 +++- test/wcc/789_named_ptr_alias_variant_widen.c | 173 +++---- test/wcc/790_single_field_struct_zeroinit.c | 113 ++--- .../wcc/793_xmod_struct_argpush_collide_run.c | 76 ++- test/wcc/794_xmod_ident_prefer.c | 50 +- test/wcc/795_xmod_valglobal_run.c | 84 ++-- test/wcc/796_xmod_valglobal_dot_run.c | 78 +-- ...797_xmod_struct_field_layout_collide_run.c | 92 +++- test/wcc/798_tuple_sret_callee.c | 24 +- test/wcc/805_placeaddr_store.c | 88 +++- test/wcc/806_append_place.c | 90 +++- test/wcc/810_dyn.c | 55 ++- test/wcc/815_fmt_int_run.c | 451 +++--------------- test/wcc/826_alias_tuple_coerce.c | 279 ++--------- test/wcc/839_xmod_nominal_typeeqast.c | 31 +- test/wcc/844_size_untyped_int.c | 177 +------ test/wcc/848_xmod_qualstructlit_run.c | 97 ++-- test/wcc/901_asserttyped_gap.c | 102 ++-- test/wcc/911_attest_record.c | 120 +++-- test/wcc/915_arr_module_index_run.c | 119 +++-- test/wcc/926_tagged_sret_run.c | 136 +++--- test/wcc/926_tagscr_sizes_run.c | 75 ++- test/wcc/929_match_4arm_cross_module_run.c | 65 ++- test/wcc/929_tagged_memarg_run.c | 100 ++-- test/wcc/930_free_noop_run.c | 98 ++-- test/wcc/940_global_sret_run.c | 92 ++-- test/wcc/944_alias_accept_run.c | 39 +- test/wcc/944_alias_cgen_b5_run.c | 33 +- test/wcc/944_variant_chain_b95_run.c | 41 +- test/wcc/946_structparam_run.c | 87 ++-- test/wcc/946_structret_run.c | 85 ++-- test/wcc/949_driver_flagargs.c | 102 ++-- test/wcc/949_missingpkg.c | 113 +++-- test/wcc/949_xmod_fnptr_const_run.c | 73 ++- test/wcc/951_defdim_struct_run.c | 110 +++-- test/wcc/952_floats_run.c | 64 ++- test/wcc/954_tuprecv_run.c | 41 +- test/wcc/956_modqualdestr_run.c | 125 ++--- test/wcc/956_tuprecv_f64_run.c | 95 +++- test/wcc/957_assert_builtin_run.c | 229 ++++----- test/wcc/958_types_sizelim_run.c | 38 +- test/wcc/959_types_intlim_run.c | 39 +- test/wcc/961_opaque_guards.c | 289 +++-------- test/wcc/963_sort_run.c | 40 +- test/wcc/975_dirs_run.c | 51 +- test/wcc/975_dirs_toolong_run.c | 52 +- test/wcc/976_stat_run.c | 51 +- test/wcc/989_barefn_collide_run.c | 84 +++- test/wcc/989_c6soak_run.c | 119 ++--- test/wcc/989_callarg_typecheck.c | 415 +++++----------- test/wcc/989_coloimport_sep.c | 51 +- test/wcc/989_declns_sep.c | 162 ++----- test/wcc/989_depmain_sep.c | 64 +-- test/wcc/989_dynentry_run.c | 100 +++- test/wcc/989_enumcap_run.c | 120 ++++- test/wcc/989_ffivariadic_run.c | 51 +- test/wcc/989_fnptrcollide_run.c | 67 ++- test/wcc/989_lib_byteid.c | 69 ++- test/wcc/989_libprecond_abort.c | 82 ++-- test/wcc/989_m1mangle_run.c | 118 +++-- test/wcc/989_m1mangle_sym.c | 25 +- test/wcc/989_m1union_run.c | 140 ++---- test/wcc/989_m1usehint_run.c | 211 ++++++-- test/wcc/989_m2wwi_run.c | 67 +-- test/wcc/989_m3sep_run.c | 122 ++++- test/wcc/989_modresetadj_run.c | 3 +- test/wcc/989_regex_run.c | 85 ++-- test/wcc/989_separchive_run.c | 125 ++++- test/wcc/989_sepbuild_run.c | 86 ++-- test/wcc/989_sepcycle_dup.c | 124 ++++- test/wcc/989_sepdotpath_run.c | 104 +++- test/wcc/989_seproot_export_run.c | 85 +++- test/wcc/989_sepscratch_run.c | 162 +++++-- test/wcc/989_sepstructdef_run.c | 64 ++- test/wcc/989_septest_run.c | 77 +-- test/wcc/989_slttypepref_run.c | 77 ++- test/wcc/989_structlocal_frame.c | 54 ++- test/wcc/989_syntaxexport_run.c | 68 ++- test/wcc/989_tagged_subset_reject.c | 279 ++++------- test/wcc/989_wwileaf_run.c | 5 +- test/wcc/989_wwispread_sep.c | 43 +- test/wcc/990_selfhost.c | 26 +- test/wcc/991_w6a_ww.c | 23 +- test/wcc/992_w6l_ww.c | 40 +- test/wcc/993_ww_ww.c | 9 +- test/wcc/994_w6c_ww.c | 18 +- test/wcc/996_dyn_ww.c | 10 +- 132 files changed, 7573 insertions(+), 6172 deletions(-) diff --git a/test/wcc/110_uniesc_run.c b/test/wcc/110_uniesc_run.c index 6c982cea..814d803a 100644 --- a/test/wcc/110_uniesc_run.c +++ b/test/wcc/110_uniesc_run.c @@ -17,6 +17,7 @@ */ #include #include +#include #include #include #include @@ -37,31 +38,45 @@ runwait(const char *cmd) static int run_roundtrip(const char *driver, int id) { - char dir[80], wrdir[112], wrp[176], mainp[160], cmd[1024], outbin[160]; + char dir[80], wrdir[112], wrp[176], mainp[160], cmd[1024]; + char outbin[160], scratch[176]; snprintf(dir, sizeof dir, "/tmp/wwue_rt_%d_%d", getpid(), id); snprintf(wrdir, sizeof wrdir, "%s/wr", dir); - mkdir(dir, 0755); - mkdir(wrdir, 0755); snprintf(wrp, sizeof wrp, "%s/wr.ww", wrdir); snprintf(mainp, sizeof mainp, "%s/main.ww", dir); + snprintf(outbin, sizeof outbin, "%s/main", dir); + snprintf(scratch, sizeof scratch, "%s/main.sepwork", dir); + + if (mkdir(dir, 0755) != 0) { + perror(dir); + return -1; + } + int got = -1; + int wr_owned = 0; + if (mkdir(wrdir, 0755) != 0) { + perror(wrdir); + goto cleanup; + } + wr_owned = 1; FILE *f = fopen(wrp, "wb"); if (!f) { - snprintf(cmd, sizeof cmd, "rm -rf %s", dir); - (void)system(cmd); - return -1; + perror(wrp); + goto cleanup; } /* one def per serializer branch: \x (<=0xFF), \u (<=0xFFFF), \U. */ fputs("package wr;\n" "export def EACUTE: rune = '\\u00e9';\n" "export def EURO: rune = '\\u20ac';\n" "export def SMILEY: rune = '\\U0001F600';\n", f); - fclose(f); + if (fclose(f) != 0) { + perror(wrp); + goto cleanup; + } f = fopen(mainp, "wb"); if (!f) { - snprintf(cmd, sizeof cmd, "rm -rf %s", dir); - (void)system(cmd); - return -1; + perror(mainp); + goto cleanup; } fputs("package main;\n" "import wr;\n" @@ -74,19 +89,46 @@ run_roundtrip(const char *driver, int id) " if ((c: u32) != 128512u32) { return 3; };\n" " return 42;\n" "};\n", f); - fclose(f); + if (fclose(f) != 0) { + perror(mainp); + goto cleanup; + } snprintf(cmd, sizeof cmd, "cd %s && %s build main.ww", dir, driver); if (runwait(cmd) != 0) { fprintf(stderr, "roundtrip: build via %s failed\n", driver); - snprintf(cmd, sizeof cmd, "rm -rf %s", dir); - (void)system(cmd); - return -1; + goto cleanup; } - snprintf(outbin, sizeof outbin, "%s/main", dir); - int got = runwait(outbin); - snprintf(cmd, sizeof cmd, "rm -rf %s", dir); - (void)system(cmd); + got = runwait(outbin); + +cleanup: + /* `ww build` retains this exact caller-owned scratch tree. */ + snprintf(cmd, sizeof cmd, "rm -rf %s", scratch); + int cleanfail = runwait(cmd) != 0; + if (unlink(outbin) != 0 && errno != ENOENT) { + perror(outbin); + cleanfail = 1; + } + if (unlink(mainp) != 0 && errno != ENOENT) { + perror(mainp); + cleanfail = 1; + } + if (wr_owned) { + if (unlink(wrp) != 0 && errno != ENOENT) { + perror(wrp); + cleanfail = 1; + } + if (rmdir(wrdir) != 0) { + perror(wrdir); + cleanfail = 1; + } + } + if (rmdir(dir) != 0) { + perror(dir); + cleanfail = 1; + } + if (cleanfail && got == 42) + got = -1; return got; } diff --git a/test/wcc/610_arch.c b/test/wcc/610_arch.c index 336f5d33..baa379f3 100644 --- a/test/wcc/610_arch.c +++ b/test/wcc/610_arch.c @@ -32,30 +32,37 @@ main(void) char dir[64]; snprintf(dir, sizeof dir, "/tmp/wwarch_%d", getpid()); - mkdir(dir, 0755); + if (mkdir(dir, 0755) != 0) { + perror(dir); + return 1; + } + int result = 1; /* lib_good.ww — defines f1() (→ main.f1 under strict-package #24a) */ char path[256]; snprintf(path, sizeof path, "%s/lib_good.ww", dir); FILE *f = fopen(path, "wb"); + if (!f) { perror(path); goto cleanup; } wwtest_fputs("export fn f1() i32 = { return 7; };", f); - fclose(f); + if (fclose(f) != 0) { perror(path); goto cleanup; } /* lib_bad.ww — defines f2() but also references an undefined extern */ snprintf(path, sizeof path, "%s/lib_bad.ww", dir); f = fopen(path, "wb"); + if (!f) { perror(path); goto cleanup; } wwtest_fputs("@symbol(\"this_symbol_does_not_exist\") fn bogus() i32;\n" "export fn f2() i32 = { return bogus(); };", f); - fclose(f); + if (fclose(f) != 0) { perror(path); goto cleanup; } /* main.ww — calls f1, NOT f2. The plain prototype declares f1 in the * same (main) module, so the call resolves to main.f1 — matching * lib_good's moduled definition (no @symbol bare-pin needed). */ snprintf(path, sizeof path, "%s/m.ww", dir); f = fopen(path, "wb"); + if (!f) { perror(path); goto cleanup; } wwtest_fputs("fn f1() i32;\n" "fn main() i32 = { return f1(); };", f); - fclose(f); + if (fclose(f) != 0) { perror(path); goto cleanup; } char cmd[2048]; snprintf(cmd, sizeof cmd, @@ -65,7 +72,10 @@ main(void) "ar rcs libfoo.a lib_good.o lib_bad.o && " "%s/w6c -o m.s m.ww && %s/w6a -o m.o m.s", dir, bin, bin, bin, bin, bin, bin); - if (run(cmd) != 0) { fprintf(stderr, "build failed\n"); return 1; } + if (run(cmd) != 0) { + fprintf(stderr, "build failed\n"); + goto cleanup; + } /* Find start.o for the runtime */ char startobj[256]; @@ -75,8 +85,6 @@ main(void) "%s/w6l -o %s/m %s/m.o %s %s/libfoo.a 2>%s/link.err", bin, dir, dir, startobj, dir, dir); if (run(cmd) != 0) { - FILE *ef = fopen("/dev/null", "r"); - (void)ef; char errpath[300]; snprintf(errpath, sizeof errpath, "%s/link.err", dir); FILE *e = fopen(errpath, "r"); @@ -84,16 +92,36 @@ main(void) char buf[512]; size_t r = fread(buf, 1, sizeof buf - 1, e); buf[r]='\0'; fclose(e); fprintf(stderr, "link failed:\n%s\n", buf); } - return 1; + goto cleanup; } char exepath[256]; snprintf(exepath, sizeof exepath, "%s/m", dir); int rc = run(exepath); if (WIFEXITED(rc) && WEXITSTATUS(rc) == 7) { - printf("arch: ok (only needed archive member pulled)\n"); - return 0; + result = 0; + } else { + fprintf(stderr, "arch: unexpected exit\n"); } - fprintf(stderr, "arch: unexpected exit\n"); - return 1; + +cleanup: + snprintf(path, sizeof path, "%s/lib_good.ww", dir); unlink(path); + snprintf(path, sizeof path, "%s/lib_bad.ww", dir); unlink(path); + snprintf(path, sizeof path, "%s/m.ww", dir); unlink(path); + snprintf(path, sizeof path, "%s/lib_good.s", dir); unlink(path); + snprintf(path, sizeof path, "%s/lib_bad.s", dir); unlink(path); + snprintf(path, sizeof path, "%s/lib_good.o", dir); unlink(path); + snprintf(path, sizeof path, "%s/lib_bad.o", dir); unlink(path); + snprintf(path, sizeof path, "%s/libfoo.a", dir); unlink(path); + snprintf(path, sizeof path, "%s/m.s", dir); unlink(path); + snprintf(path, sizeof path, "%s/m.o", dir); unlink(path); + snprintf(path, sizeof path, "%s/m", dir); unlink(path); + snprintf(path, sizeof path, "%s/link.err", dir); unlink(path); + if (rmdir(dir) != 0) { + perror(dir); + result = 1; + } + if (result == 0) + printf("arch: ok (only needed archive member pulled)\n"); + return result; } diff --git a/test/wcc/631_def_neg_global.c b/test/wcc/631_def_neg_global.c index c2268b73..147a0ca8 100644 --- a/test/wcc/631_def_neg_global.c +++ b/test/wcc/631_def_neg_global.c @@ -33,14 +33,14 @@ * - unary-plus-i32: `def X: i32 = +5;` — TK_PLUS noop, completes * the unary whitelist coverage. * - * Per-fixture: compile+link+run via the `ww` driver (cstage) and - * via `ww_ww` (wwstage) if it exists. Asm byte-identity diff between - * cstage's w6c and wwstage's w6c_ww closes the symmetry corner. + * Runtime ownership moved to the r56_def_* runww fixtures. This wrapper + * retains only the source-to-assembly byte-identity comparison between + * cstage's w6c and wwstage's w6c_ww. */ #include #include +#include #include -#include #include #include "wwtestpkg.h" @@ -53,7 +53,7 @@ runwait(const char *cmd) return -1; } -struct row { const char *label; const char *src; int want; }; +struct row { const char *label; const char *src; }; static const struct row rows[] = { /* The headline #24 case: N_UN(TK_MINUS, N_INTLIT) rhs. @@ -65,8 +65,7 @@ static const struct row rows[] = { "\tlet x: i32 = NEG;\n" "\tif (x == -100) { return 42; };\n" "\treturn 1;\n" - "};\n", - 42 }, + "};\n" }, /* Regression check: an unwrapped N_INTLIT rhs must still * emit a DATA row. If the fold-gate rewrite accidentally @@ -77,8 +76,7 @@ static const struct row rows[] = { "\tlet x: i32 = POS;\n" "\tif (x == 100) { return 42; };\n" "\treturn 1;\n" - "};\n", - 42 }, + "};\n" }, /* Unary tilde over N_INTLIT — the other arithmetic op in * the unary whitelist. ~0 is -1 in i32 two's complement; @@ -90,8 +88,7 @@ static const struct row rows[] = { "\tlet x: i32 = NTIL;\n" "\tif (x == -1) { return 42; };\n" "\treturn 1;\n" - "};\n", - 42 }, + "};\n" }, /* 8-byte slot via the same fold path. Catches any width- * specific bug in the DATA-row emit (DATA always writes @@ -103,8 +100,7 @@ static const struct row rows[] = { "\tlet x: i64 = NEG;\n" "\tif (x == -1234567890i64) { return 42; };\n" "\treturn 1;\n" - "};\n", - 42 }, + "};\n" }, /* Unsigned (u32) slot. `~0u32` is all-ones; reading it * back through a u32 local and comparing against the @@ -116,8 +112,7 @@ static const struct row rows[] = { "\tlet x: u32 = UMAX;\n" "\tif (x == 4294967295u32) { return 42; };\n" "\treturn 1;\n" - "};\n", - 42 }, + "};\n" }, /* Unary plus — TK_PLUS noop in the fold. Completes the * unary whitelist coverage. */ @@ -127,40 +122,9 @@ static const struct row rows[] = { "\tlet x: i32 = P;\n" "\tif (x == 5) { return 42; };\n" "\treturn 1;\n" - "};\n", - 42 }, + "};\n" }, }; -static int -run_driver(const char *driver, const struct row *r, int i) -{ - char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/dng_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/dng_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/dng_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); - - FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return -1; } - wwtest_fputs(r->src, f); - fclose(f); - - snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null", - driver, outbin, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: build via %s failed\n", - r->label, driver); - runwait(rmcmd); - return -1; - } - - int got = runwait(outbin); - - runwait(rmcmd); - return got; -} - /* asm_byte_identical — generate .s via cstage's w6c and wwstage's * w6c_ww and diff. Pins the symmetric-emit contract: if either * stage's fold helper drifts (e.g. one accepts N_NIL the other @@ -178,23 +142,22 @@ asm_byte_identical(const char *bin, const struct row *r, int i) wwtest_fputs(r->src, f); fclose(f); + int rc = -1; snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c errored\n", r->label); - unlink(src); - return -1; + goto cleanup; } snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null", bin, ws, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label); - unlink(src); unlink(cs); - return -1; + goto cleanup; } FILE *fc = fopen(cs, "rb"); FILE *fw = fopen(ws, "rb"); - int rc = 0; + rc = 0; if (!fc || !fw) { rc = -1; } else { @@ -210,7 +173,25 @@ asm_byte_identical(const char *bin, const struct row *r, int i) if (rc != 0) fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n", r->label); - unlink(src); unlink(cs); unlink(ws); + +cleanup:; + /* a failing compile can still leave a partial .s — ENOENT is the + * only tolerable unlink error on the never-created legs. */ + int cleanfail = 0; + if (unlink(src) != 0 && errno != ENOENT) { + perror(src); + cleanfail = 1; + } + if (unlink(cs) != 0 && errno != ENOENT) { + perror(cs); + cleanfail = 1; + } + if (unlink(ws) != 0 && errno != ENOENT) { + perror(ws); + cleanfail = 1; + } + if (cleanfail && rc == 0) + rc = -1; return rc; } @@ -227,41 +208,14 @@ main(void) bin = absbin; } - char cdrv[1024]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); + /* gate on the binary the rows invoke (w6c_ww), not the ww_ww + * driver — probing the wrong one skips or fails all rows. */ char wdrv[1024]; - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - - struct { const char *name; const char *path; int gated_on_existence; } - drivers[] = { - { "cstage", cdrv, 0 }, - { "wwstage", wdrv, 1 }, - { NULL, NULL, 0 }, - }; + snprintf(wdrv, sizeof wdrv, "%s/w6c_ww", bin); int n = (int)(sizeof rows / sizeof rows[0]); int total = 0, fail = 0; - for (int d = 0; drivers[d].name; d++) { - if (drivers[d].gated_on_existence - && access(drivers[d].path, X_OK) != 0) { - fprintf(stderr, "def_neg_global: skip %s (no %s)\n", - drivers[d].name, drivers[d].path); - continue; - } - for (int i = 0; i < n; i++) { - int got = run_driver(drivers[d].path, &rows[i], i); - total++; - if (got != rows[i].want) { - fprintf(stderr, - "def_neg_global[%s][%s]: exit=%d want=%d\n", - drivers[d].name, rows[i].label, - got, rows[i].want); - fail++; - } - } - } - /* Asm byte-identity diff, only when wwstage is built. */ if (access(wdrv, X_OK) == 0) { for (int i = 0; i < n; i++) { diff --git a/test/wcc/632_w6l_manyflags.c b/test/wcc/632_w6l_manyflags.c index fb4438f6..17fd60a3 100644 --- a/test/wcc/632_w6l_manyflags.c +++ b/test/wcc/632_w6l_manyflags.c @@ -16,7 +16,9 @@ #include #include #include +#include #include +#include #include #include "wwtestpkg.h" @@ -49,8 +51,8 @@ runwait(const char *cmd) static int linkwith(const char *lnk, const char *obj, const char *libdir, int nflags) { - char exe[64]; - snprintf(exe, sizeof exe, "/tmp/wwfc_%d_x", getpid()); + char exe[128]; + snprintf(exe, sizeof exe, "%s/wwfc_x", libdir); char junk[8192]; size_t off = 0; @@ -63,19 +65,30 @@ linkwith(const char *lnk, const char *obj, const char *libdir, int nflags) "%s -o %s %s%s -L%s -lfoo 2>/dev/null", lnk, exe, obj, junk, libdir); int rc = runwait(cmd); - if (rc != 0) { unlink(exe); return rc; } + if (rc != 0) goto cleanup; FILE *f = fopen(exe, "rb"); - if (!f) return -1; + if (!f) { + rc = -1; + goto cleanup; + } unsigned char hdr[20]; int ok = fread(hdr, 1, sizeof hdr, f) == sizeof hdr; fclose(f); - unlink(exe); - if (!ok || memcmp(hdr, "\x7f""ELF", 4) != 0) return -1; + if (!ok || memcmp(hdr, "\x7f""ELF", 4) != 0) { + rc = -1; + goto cleanup; + } unsigned short etype = (unsigned short)hdr[16] | ((unsigned short)hdr[17] << 8); - if (etype != 2) return -1; /* ET_EXEC */ - return 0; + if (etype != 2) rc = -1; /* ET_EXEC */ + +cleanup: + if (unlink(exe) != 0 && errno != ENOENT) { + perror(exe); + if (rc == 0) rc = -1; + } + return rc; } int @@ -84,37 +97,44 @@ main(void) const char *bin = absbin(); if (!bin) return 1; - char src[64], asmf[64], obj[64]; - char fsrc[64], fasm[64], fobj[64]; + char src[128], asmf[128], obj[128]; + char fsrc[128], fasm[128], fobj[128]; char libdir[64], lib[128], cmd[1024]; int pid = getpid(); - snprintf(src, sizeof src, "/tmp/wwfc_%d_m.ww", pid); - snprintf(asmf, sizeof asmf, "/tmp/wwfc_%d_m.s", pid); - snprintf(obj, sizeof obj, "/tmp/wwfc_%d_m.o", pid); - snprintf(fsrc, sizeof fsrc, "/tmp/wwfc_%d_f.ww", pid); - snprintf(fasm, sizeof fasm, "/tmp/wwfc_%d_f.s", pid); - snprintf(fobj, sizeof fobj, "/tmp/wwfc_%d_f.o", pid); snprintf(libdir, sizeof libdir, "/tmp/wwfc_%d_lib", pid); + if (mkdir(libdir, 0755) != 0) { + perror(libdir); + return 1; + } + int result = 1; + snprintf(src, sizeof src, "%s/m.ww", libdir); + snprintf(asmf, sizeof asmf, "%s/m.s", libdir); + snprintf(obj, sizeof obj, "%s/m.o", libdir); + snprintf(fsrc, sizeof fsrc, "%s/f.ww", libdir); + snprintf(fasm, sizeof fasm, "%s/f.s", libdir); + snprintf(fobj, sizeof fobj, "%s/f.o", libdir); snprintf(lib, sizeof lib, "%s/libfoo.a", libdir); /* a standalone main.o (no undefs) + an unrelated archived foo.o. */ FILE *f = fopen(src, "wb"); + if (!f) { perror(src); goto cleanup; } wwtest_fputs("fn main() i32 = { return 42; };", f); - fclose(f); + if (fclose(f) != 0) { perror(src); goto cleanup; } f = fopen(fsrc, "wb"); + if (!f) { perror(fsrc); goto cleanup; } wwtest_fputs("export fn foo() i32 = { return 7; };", f); - fclose(f); + if (fclose(f) != 0) { perror(fsrc); goto cleanup; } snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s", bin, asmf, src); - if (runwait(cmd) != 0) { fprintf(stderr, "w6c main failed\n"); return 1; } + if (runwait(cmd) != 0) { fprintf(stderr, "w6c main failed\n"); goto cleanup; } snprintf(cmd, sizeof cmd, "%s/w6a -o %s %s", bin, obj, asmf); - if (runwait(cmd) != 0) { fprintf(stderr, "w6a main failed\n"); return 1; } + if (runwait(cmd) != 0) { fprintf(stderr, "w6a main failed\n"); goto cleanup; } snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s", bin, fasm, fsrc); - if (runwait(cmd) != 0) { fprintf(stderr, "w6c foo failed\n"); return 1; } + if (runwait(cmd) != 0) { fprintf(stderr, "w6c foo failed\n"); goto cleanup; } snprintf(cmd, sizeof cmd, "%s/w6a -o %s %s", bin, fobj, fasm); - if (runwait(cmd) != 0) { fprintf(stderr, "w6a foo failed\n"); return 1; } - snprintf(cmd, sizeof cmd, "mkdir -p %s && ar rcs %s %s", libdir, lib, fobj); - if (runwait(cmd) != 0) { fprintf(stderr, "ar failed\n"); return 1; } + if (runwait(cmd) != 0) { fprintf(stderr, "w6a foo failed\n"); goto cleanup; } + snprintf(cmd, sizeof cmd, "ar rcs %s %s", lib, fobj); + if (runwait(cmd) != 0) { fprintf(stderr, "ar failed\n"); goto cleanup; } int counts[] = { 1, 64, 65, 100, 128 }; int fail = 0; @@ -138,15 +158,50 @@ main(void) } } - snprintf(cmd, sizeof cmd, "rm -rf %s %s %s %s %s %s %s", - src, asmf, obj, fsrc, fasm, fobj, libdir); - (void)runwait(cmd); - if (fail) { fprintf(stderr, "w6l_manyflags: %d failure(s)\n", fail); - return 1; + goto cleanup; } + result = 0; + +cleanup: + { + int cleanfail = 0; + if (unlink(src) != 0 && errno != ENOENT) { + perror(src); cleanfail = 1; + } + if (unlink(asmf) != 0 && errno != ENOENT) { + perror(asmf); cleanfail = 1; + } + if (unlink(obj) != 0 && errno != ENOENT) { + perror(obj); cleanfail = 1; + } + if (unlink(fsrc) != 0 && errno != ENOENT) { + perror(fsrc); cleanfail = 1; + } + if (unlink(fasm) != 0 && errno != ENOENT) { + perror(fasm); cleanfail = 1; + } + if (unlink(fobj) != 0 && errno != ENOENT) { + perror(fobj); cleanfail = 1; + } + if (unlink(lib) != 0 && errno != ENOENT) { + perror(lib); cleanfail = 1; + } + char exe[128]; + snprintf(exe, sizeof exe, "%s/wwfc_x", libdir); + if (unlink(exe) != 0 && errno != ENOENT) { + perror(exe); + cleanfail = 1; + } + if (rmdir(libdir) != 0) { + perror(libdir); + cleanfail = 1; + } + if (cleanfail && result == 0) result = 1; + } + if (result != 0) return result; printf("w6l_manyflags: -L past slot 64 honoured by both stages " "(1/64/65/100/128 flags)\n"); - return 0; + return result; } diff --git a/test/wcc/671_struct_packed.c b/test/wcc/671_struct_packed.c index 82fdc3ff..dcb5842a 100644 --- a/test/wcc/671_struct_packed.c +++ b/test/wcc/671_struct_packed.c @@ -36,7 +36,7 @@ * type table, so cstage's single `size`/offset and wwstage's tinfo + * astsize/astoffset folds emit identical asm. * - * IDENTITY (identity_reject, CSTAGE-asserted): assigning a packed struct + * IDENTITY (identity_reject, stage-asymmetric): assigning a packed struct * value to a structurally-identical UNPACKED twin is a type error — proves * packed is part of type identity (harec types.c:621). cstage rejects it * (structural type_eq honours packed). wwstage's struct-vs-struct @@ -44,9 +44,9 @@ * (#224/#10: a bare cross-module same-leaf type name can mis-resolve, so * wwstage cannot confidently reject ANY same-kind struct mismatch — it * already accepts `struct{a:u8}` into `struct{x:u64,y:u64}`); the packed - * twin rides that same deferred arc. So this row asserts ONLY that cstage - * rejects; wwstage's accept is the documented #224/#10 divergence, not a - * #51 regression. + * twin rides that same deferred arc. The row explicitly requires cstage to + * reject and wwstage to accept; the latter is the documented #224/#10 + * divergence, not a #51 regression. * * WWI ROUND-TRIP (wwi_roundtrip): a library package exports * `type Ev = struct @packed {...}`; an importing package computes size(Ev) @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -72,7 +73,7 @@ runwait(const char *cmd) } /* want sentinels (outside any real exit code 0..255). */ -#define REJECT_CSTAGE (-2147483647 - 1) /* cstage must FAIL to build */ +#define REJECT_CSTAGE (-2147483647 - 1) /* cstage fails; wwstage runs 0 */ /* The packed/unpacked twins shared by the layout rows. Each row's `expr` * is spliced into a fn body that has `p` (packed) and `u` (unpacked) of @@ -199,9 +200,8 @@ static const struct row rows[] = { "\treturn (p.b: i32) + after - (p.a: i32);\n" "};\n", 20 }, - /* identity: packed twin not assignable to unpacked twin. cstage - * rejects (structural type_eq honours packed); wwstage rides the - * #224/#10 struct-leniency (see header). CSTAGE-asserted only. */ + /* identity: cstage rejects the packed/unpacked assignment; wwstage + * accepts it through the documented #224/#10 leniency (see header). */ { "identity_reject", "package main;\n" "type A = struct @packed { x: u8, y: u64 };\n" @@ -211,76 +211,93 @@ static const struct row rows[] = { "\tlet b: B = a;\n" "\treturn 0;\n" "};\n", REJECT_CSTAGE }, - - /* unknown struct attribute is a loud parse error (parse.c / - * parse.ww `@%s != packed` branch). cstage-asserted (the reject - * harness only pins cstage); wwstage errmsg rejects symmetrically. */ - { "unknown_attr_reject", - "package main;\n" - "type P = struct @foo { a: u8 };\n" - "export fn main() i32 = { return 0; };\n", REJECT_CSTAGE }, }; static int run_driver(const char *driver, const struct row *r, int i) { - char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024]; + char tmpdir[64], src[128], outbin[128], scratch[160], cmd[1024]; snprintf(tmpdir, sizeof tmpdir, "/tmp/pk51_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); snprintf(src, sizeof src, "%s/pk51_%d_%d.ww", tmpdir, getpid(), i); snprintf(outbin, sizeof outbin, "%s/pk51_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + snprintf(scratch, sizeof scratch, "%s.sepwork", outbin); + if (mkdir(tmpdir, 0755) != 0) { + perror(tmpdir); + return -2; + } + int result = -2; FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return -2; } + if (!f) { perror(src); goto cleanup; } fputs(r->src, f); - fclose(f); + if (fclose(f) != 0) { perror(src); goto cleanup; } snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null", driver, outbin, src); int built = runwait(cmd); if (built != 0) { /* build failed (a reject, or a real error). Caller decides. */ - runwait(rmcmd); - return -1; + result = -1; + goto cleanup; } - int got = runwait(outbin); + result = runwait(outbin); - runwait(rmcmd); - return got; +cleanup: + /* `ww build -o` retains this exact caller-owned scratch tree. */ + snprintf(cmd, sizeof cmd, "rm -rf %s", scratch); + int cleanfail = runwait(cmd) != 0; + if (unlink(outbin) != 0 && errno != ENOENT) { + perror(outbin); cleanfail = 1; + } + if (unlink(src) != 0 && errno != ENOENT) { + perror(src); cleanfail = 1; + } + if (rmdir(tmpdir) != 0) { + perror(tmpdir); cleanfail = 1; + } + if (cleanfail) { + fprintf(stderr, "row[%s]: cleanup failed after exit=%d\n", + r->label, result); + return -2; + } + return result; } /* asm_byte_identical — w6c (cstage) vs w6c_ww (wwstage) .s diff. */ static int asm_byte_identical(const char *bin, const struct row *r, int i) { - char src[64], cs[64], ws[64], cmd[1024]; - snprintf(src, sizeof src, "/tmp/pk51a_%d_%d.ww", getpid(), i); - snprintf(cs, sizeof cs, "/tmp/pk51a_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/pk51a_%d_%d_w.s", getpid(), i); + char tmpdir[64], src[128], cs[128], ws[128], cmd[1024]; + snprintf(tmpdir, sizeof tmpdir, "/tmp/pk51a_%d_%d", getpid(), i); + snprintf(src, sizeof src, "%s/input.ww", tmpdir); + snprintf(cs, sizeof cs, "%s/c.s", tmpdir); + snprintf(ws, sizeof ws, "%s/w.s", tmpdir); + if (mkdir(tmpdir, 0755) != 0) { + perror(tmpdir); + return -1; + } + int rc = -1; FILE *f = fopen(src, "wb"); - if (!f) return -1; + if (!f) { perror(src); goto cleanup; } fputs(r->src, f); - fclose(f); + if (fclose(f) != 0) { perror(src); goto cleanup; } snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c errored\n", r->label); - unlink(src); - return -1; + goto cleanup; } snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null", bin, ws, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label); - unlink(src); unlink(cs); - return -1; + goto cleanup; } FILE *fc = fopen(cs, "rb"); FILE *fw = fopen(ws, "rb"); - int rc = 0; + rc = 0; if (!fc || !fw) { rc = -1; } else { @@ -296,7 +313,20 @@ asm_byte_identical(const char *bin, const struct row *r, int i) if (rc != 0) fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n", r->label); - unlink(src); unlink(cs); unlink(ws); + +cleanup: + if (unlink(src) != 0 && errno != ENOENT) { + perror(src); rc = -1; + } + if (unlink(cs) != 0 && errno != ENOENT) { + perror(cs); rc = -1; + } + if (unlink(ws) != 0 && errno != ENOENT) { + perror(ws); rc = -1; + } + if (rmdir(tmpdir) != 0) { + perror(tmpdir); rc = -1; + } return rc; } @@ -308,20 +338,32 @@ asm_byte_identical(const char *bin, const struct row *r, int i) static int wwi_roundtrip(const char *driver, int idx) { - char root[80], libdir[128], appww[160], cmd[1024], outbin[200], rm[200]; + char root[80], libroot[112], libdir[128], appdir[112]; + char pk2[160], appww[160], outbin[200], scratch[220], cmd[1024]; snprintf(root, sizeof root, "/tmp/pk51w_%d_%d", getpid(), idx); + snprintf(libroot, sizeof libroot, "%s/lib", root); snprintf(libdir, sizeof libdir, "%s/lib/pk2", root); + snprintf(appdir, sizeof appdir, "%s/app", root); + snprintf(pk2, sizeof pk2, "%s/pk2.ww", libdir); snprintf(appww, sizeof appww, "%s/app/main.ww", root); - snprintf(rm, sizeof rm, "rm -rf %s", root); + snprintf(outbin, sizeof outbin, "%s/app/m", root); + snprintf(scratch, sizeof scratch, "%s.sepwork", outbin); + if (mkdir(root, 0755) != 0) { + perror(root); + return -1; + } + int result = -1; + int cleanfail; + int libroot_owned = 0, libdir_owned = 0, app_owned = 0; + if (mkdir(libroot, 0755) != 0) { perror(libroot); goto cleanup; } + libroot_owned = 1; + if (mkdir(libdir, 0755) != 0) { perror(libdir); goto cleanup; } + libdir_owned = 1; + if (mkdir(appdir, 0755) != 0) { perror(appdir); goto cleanup; } + app_owned = 1; - char mk[256]; - snprintf(mk, sizeof mk, "mkdir -p %s/lib/pk2 %s/app", root, root); - if (runwait(mk) != 0) { runwait(rm); return -1; } - - char pk2[256]; - snprintf(pk2, sizeof pk2, "%s/lib/pk2/pk2.ww", root); FILE *f = fopen(pk2, "wb"); - if (!f) { runwait(rm); return -1; } + if (!f) { perror(pk2); goto cleanup; } /* pk2 exports ONLY the packed type — no helper fns. The size/offset * are computed on the IMPORTER side (below), so the value depends on * the .wwi re-parse laying Ev out packed. (If evsize()/evoff() lived @@ -329,10 +371,10 @@ wwi_roundtrip(const char *driver, int idx) * @packed re-parse would never be exercised.) */ fputs("package pk2;\n" "export type Ev = struct @packed { tag: u8, data: u64 };\n", f); - fclose(f); + if (fclose(f) != 0) { perror(pk2); goto cleanup; } f = fopen(appww, "wb"); - if (!f) { runwait(rm); return -1; } + if (!f) { perror(appww); goto cleanup; } /* importer computes layout from the re-parsed .wwi: packed → 9*10+1 * = 91; a dropped @packed (unpacked Ev) would give 16*10+8 = 168. */ fputs("package main;\n" @@ -341,20 +383,45 @@ wwi_roundtrip(const char *driver, int idx) "\tlet e: pk2.Ev = pk2.Ev { tag = 1u8, data = 2u64 };\n" "\treturn size(pk2.Ev): i32 * 10 + offset(e.data): i32;\n" "};\n", f); - fclose(f); + if (fclose(f) != 0) { perror(appww); goto cleanup; } snprintf(cmd, sizeof cmd, "cd %s/app && %s build -I %s/lib -o m main.ww 2>/dev/null", root, driver, root); if (runwait(cmd) != 0) { - runwait(rm); - return -1; + goto cleanup; } - snprintf(outbin, sizeof outbin, "%s/app/m", root); - int got = runwait(outbin); + result = runwait(outbin); - runwait(rm); - return got; +cleanup: + cleanfail = 0; + if (app_owned) { + snprintf(cmd, sizeof cmd, "rm -rf %s", scratch); + if (runwait(cmd) != 0) cleanfail = 1; + if (unlink(outbin) != 0 && errno != ENOENT) { + perror(outbin); cleanfail = 1; + } + if (unlink(appww) != 0 && errno != ENOENT) { + perror(appww); cleanfail = 1; + } + } + if (libdir_owned && unlink(pk2) != 0 && errno != ENOENT) { + perror(pk2); cleanfail = 1; + } + if (app_owned && rmdir(appdir) != 0) { + perror(appdir); cleanfail = 1; + } + if (libdir_owned && rmdir(libdir) != 0) { + perror(libdir); cleanfail = 1; + } + if (libroot_owned && rmdir(libroot) != 0) { + perror(libroot); cleanfail = 1; + } + if (rmdir(root) != 0) { + perror(root); cleanfail = 1; + } + if (cleanfail && result == 91) result = -1; + return result; } int @@ -394,23 +461,14 @@ main(void) for (int i = 0; i < n; i++) { int got = run_driver(drivers[d].path, &rows[i], i); total++; - int bad; - if (rows[i].want == REJECT_CSTAGE) { - /* cstage must reject (build fail → got==-1). wwstage - * rides the #224/#10 struct-leniency (header) — its - * disposition is not asserted here. */ - if (is_cstage) - bad = (got != -1); - else - bad = 0; - } else { - bad = (got != rows[i].want); - } + int want = rows[i].want; + if (want == REJECT_CSTAGE) want = is_cstage ? -1 : 0; + int bad = (got != want); if (bad) { fprintf(stderr, "struct_packed[%s][%s]: exit=%d want=%d\n", drivers[d].name, rows[i].label, - got, rows[i].want); + got, want); fail++; } } diff --git a/test/wcc/682_arr_enum_elem.c b/test/wcc/682_arr_enum_elem.c index f3966559..70f7192e 100644 --- a/test/wcc/682_arr_enum_elem.c +++ b/test/wcc/682_arr_enum_elem.c @@ -68,6 +68,7 @@ */ #include #include +#include #include #include #include @@ -154,31 +155,47 @@ static const struct row rows[] = { static int run_driver(const char *driver, const struct row *r, int i) { - char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024]; + char tmpdir[64], src[128], outbin[128], scratch[160], cmd[1024]; snprintf(tmpdir, sizeof tmpdir, "/tmp/aee_%d_d_%d", getpid(), i); snprintf(src, sizeof src, "%s/aee_%d_%d.ww", tmpdir, getpid(), i); snprintf(outbin, sizeof outbin, "%s/aee_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + snprintf(scratch, sizeof scratch, "%s.sepwork", outbin); - mkdir(tmpdir, 0755); + if (mkdir(tmpdir, 0755) != 0) { + perror(tmpdir); + return -1; + } + int result = -1; FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return -1; } + if (!f) { perror(src); goto cleanup; } wwtest_fputs(r->src, f); - fclose(f); + if (fclose(f) != 0) { perror(src); goto cleanup; } snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null", driver, outbin, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: build via %s failed\n", r->label, driver); - runwait(rmcmd); - return -1; + goto cleanup; } - int got = runwait(outbin); + result = runwait(outbin); - runwait(rmcmd); - return got; +cleanup: + /* `ww build -o` retains this exact caller-owned scratch tree. */ + snprintf(cmd, sizeof cmd, "rm -rf %s", scratch); + int cleanfail = runwait(cmd) != 0; + if (unlink(outbin) != 0 && errno != ENOENT) { + perror(outbin); cleanfail = 1; + } + if (unlink(src) != 0 && errno != ENOENT) { + perror(src); cleanfail = 1; + } + if (rmdir(tmpdir) != 0) { + perror(tmpdir); cleanfail = 1; + } + if (cleanfail && result == r->want) result = -1; + return result; } /* asm_byte_identical — generate .s via cstage's w6c and wwstage's @@ -188,33 +205,37 @@ run_driver(const char *driver, const struct row *r, int i) static int asm_byte_identical(const char *bin, const struct row *r, int i) { - char src[64], cs[64], ws[64], cmd[1024]; - snprintf(src, sizeof src, "/tmp/aee_asm_%d_%d.ww", getpid(), i); - snprintf(cs, sizeof cs, "/tmp/aee_asm_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/aee_asm_%d_%d_w.s", getpid(), i); + char tmpdir[64], src[128], cs[128], ws[128], cmd[1024]; + snprintf(tmpdir, sizeof tmpdir, "/tmp/aee_asm_%d_%d", getpid(), i); + snprintf(src, sizeof src, "%s/input.ww", tmpdir); + snprintf(cs, sizeof cs, "%s/c.s", tmpdir); + snprintf(ws, sizeof ws, "%s/w.s", tmpdir); + if (mkdir(tmpdir, 0755) != 0) { + perror(tmpdir); + return -1; + } + int rc = -1; FILE *f = fopen(src, "wb"); - if (!f) return -1; + if (!f) { perror(src); goto cleanup; } wwtest_fputs(r->src, f); - fclose(f); + if (fclose(f) != 0) { perror(src); goto cleanup; } snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c errored\n", r->label); - unlink(src); - return -1; + goto cleanup; } snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null", bin, ws, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label); - unlink(src); unlink(cs); - return -1; + goto cleanup; } FILE *fc = fopen(cs, "rb"); FILE *fw = fopen(ws, "rb"); - int rc = 0; + rc = 0; if (!fc || !fw) { rc = -1; } else { @@ -230,7 +251,20 @@ asm_byte_identical(const char *bin, const struct row *r, int i) if (rc != 0) fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n", r->label); - unlink(src); unlink(cs); unlink(ws); + +cleanup: + if (unlink(src) != 0 && errno != ENOENT) { + perror(src); rc = -1; + } + if (unlink(cs) != 0 && errno != ENOENT) { + perror(cs); rc = -1; + } + if (unlink(ws) != 0 && errno != ENOENT) { + perror(ws); rc = -1; + } + if (rmdir(tmpdir) != 0) { + perror(tmpdir); rc = -1; + } return rc; } diff --git a/test/wcc/683_arr_strslice_elem.c b/test/wcc/683_arr_strslice_elem.c index 3795aec6..e4b40c88 100644 --- a/test/wcc/683_arr_strslice_elem.c +++ b/test/wcc/683_arr_strslice_elem.c @@ -68,6 +68,7 @@ */ #include #include +#include #include #include #include @@ -247,31 +248,48 @@ static const struct row rows[] = { static int run_driver(const char *driver, const struct row *r, int i) { - char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024]; + char tmpdir[64], src[128], outbin[128], scratch[160], cmd[1024]; snprintf(tmpdir, sizeof tmpdir, "/tmp/asse_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); snprintf(src, sizeof src, "%s/asse_%d_%d.ww", tmpdir, getpid(), i); snprintf(outbin, sizeof outbin, "%s/asse_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + snprintf(scratch, sizeof scratch, "%s.sepwork", outbin); + if (mkdir(tmpdir, 0755) != 0) { + perror(tmpdir); + return -1; + } + int result = -1; FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return -1; } + if (!f) { perror(src); goto cleanup; } fputs(r->src, f); - fclose(f); + if (fclose(f) != 0) { perror(src); goto cleanup; } snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null", driver, outbin, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: build via %s failed\n", r->label, driver); - runwait(rmcmd); - return -1; + goto cleanup; } - int got = runwait(outbin); + result = runwait(outbin); - runwait(rmcmd); - return got; +cleanup: + /* `ww build -o` retains this exact caller-owned scratch tree. */ + snprintf(cmd, sizeof cmd, "rm -rf %s", scratch); + int cleanfail = runwait(cmd) != 0; + if (unlink(outbin) != 0 && errno != ENOENT) { + perror(outbin); cleanfail = 1; + } + if (unlink(src) != 0 && errno != ENOENT) { + perror(src); cleanfail = 1; + } + if (rmdir(tmpdir) != 0) { + perror(tmpdir); cleanfail = 1; + } + if (cleanfail && (r->want == BYTEID_ONLY || result == r->want)) + result = -1; + return result; } /* asm_byte_identical — generate .s via cstage's w6c and wwstage's @@ -281,33 +299,37 @@ run_driver(const char *driver, const struct row *r, int i) static int asm_byte_identical(const char *bin, const struct row *r, int i) { - char src[64], cs[64], ws[64], cmd[1024]; - snprintf(src, sizeof src, "/tmp/asse_asm_%d_%d.ww", getpid(), i); - snprintf(cs, sizeof cs, "/tmp/asse_asm_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/asse_asm_%d_%d_w.s", getpid(), i); + char tmpdir[64], src[128], cs[128], ws[128], cmd[1024]; + snprintf(tmpdir, sizeof tmpdir, "/tmp/asse_asm_%d_%d", getpid(), i); + snprintf(src, sizeof src, "%s/input.ww", tmpdir); + snprintf(cs, sizeof cs, "%s/c.s", tmpdir); + snprintf(ws, sizeof ws, "%s/w.s", tmpdir); + if (mkdir(tmpdir, 0755) != 0) { + perror(tmpdir); + return -1; + } + int rc = -1; FILE *f = fopen(src, "wb"); - if (!f) return -1; + if (!f) { perror(src); goto cleanup; } fputs(r->src, f); - fclose(f); + if (fclose(f) != 0) { perror(src); goto cleanup; } snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c errored\n", r->label); - unlink(src); - return -1; + goto cleanup; } snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null", bin, ws, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label); - unlink(src); unlink(cs); - return -1; + goto cleanup; } FILE *fc = fopen(cs, "rb"); FILE *fw = fopen(ws, "rb"); - int rc = 0; + rc = 0; if (!fc || !fw) { rc = -1; } else { @@ -323,7 +345,20 @@ asm_byte_identical(const char *bin, const struct row *r, int i) if (rc != 0) fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n", r->label); - unlink(src); unlink(cs); unlink(ws); + +cleanup: + if (unlink(src) != 0 && errno != ENOENT) { + perror(src); rc = -1; + } + if (unlink(cs) != 0 && errno != ENOENT) { + perror(cs); rc = -1; + } + if (unlink(ws) != 0 && errno != ENOENT) { + perror(ws); rc = -1; + } + if (rmdir(tmpdir) != 0) { + perror(tmpdir); rc = -1; + } return rc; } diff --git a/test/wcc/684_arr_infer_len.c b/test/wcc/684_arr_infer_len.c index 07d1d203..dda321cc 100644 --- a/test/wcc/684_arr_infer_len.c +++ b/test/wcc/684_arr_infer_len.c @@ -47,6 +47,7 @@ */ #include #include +#include #include #include #include @@ -187,89 +188,116 @@ static const char *neg[] = { static int run_driver(const char *driver, const struct row *r, int i) { - char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024]; + char tmpdir[64], src[128], outbin[128], scratch[160], rmcmd[192], cmd[1024]; + int result = -1, setupfail = 0, cleanfail = 0; snprintf(tmpdir, sizeof tmpdir, "/tmp/ail_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); + if (mkdir(tmpdir, 0755) != 0) return -1; snprintf(src, sizeof src, "%s/ail_%d_%d.ww", tmpdir, getpid(), i); snprintf(outbin, sizeof outbin, "%s/ail_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + snprintf(scratch, sizeof scratch, "%s.sepwork", outbin); FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return -1; } - fputs(r->src, f); - fclose(f); + if (!f) { setupfail = 1; goto cleanup; } + int writefail = fputs(r->src, f) == EOF; + if (fclose(f) != 0) writefail = 1; + if (writefail) { setupfail = 1; goto cleanup; } snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null", driver, outbin, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: build via %s failed\n", r->label, driver); - runwait(rmcmd); - return -1; + goto cleanup; } - int got = runwait(outbin); - - runwait(rmcmd); - return got; + result = runwait(outbin); +cleanup: + if (setupfail) + fprintf(stderr, "row[%s]: temporary setup failed\n", r->label); + snprintf(rmcmd, sizeof rmcmd, "rm -rf -- %s", scratch); + if (runwait(rmcmd) != 0) cleanfail = 1; + if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(tmpdir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "row[%s]: temporary cleanup failed\n", r->label); + if (result == r->want) return -1; + } + return result; } /* build_should_fail — a `[_]T` that can't infer must error on `driver`; - * returns 0 when the build correctly FAILS, non-zero when it wrongly - * succeeded. */ + * returns 0 when the build correctly FAILS, positive when it wrongly + * succeeds, and negative on fixture infrastructure failure. */ static int build_should_fail(const char *driver, const char *src, int i) { - char tmpdir[64], s[128], outbin[128], rmcmd[160], cmd[1024]; + char tmpdir[64], s[128], outbin[128], scratch[160], rmcmd[192], cmd[1024]; + int result = -1, setupfail = 0, cleanfail = 0; snprintf(tmpdir, sizeof tmpdir, "/tmp/ailn_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); + if (mkdir(tmpdir, 0755) != 0) return -1; snprintf(s, sizeof s, "%s/ailn_%d_%d.ww", tmpdir, getpid(), i); snprintf(outbin, sizeof outbin, "%s/ailn_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + snprintf(scratch, sizeof scratch, "%s.sepwork", outbin); FILE *f = fopen(s, "wb"); - if (!f) { runwait(rmcmd); return -1; } - fputs(src, f); - fclose(f); + if (!f) { setupfail = 1; goto cleanup; } + int writefail = fputs(src, f) == EOF; + if (fclose(f) != 0) writefail = 1; + if (writefail) { setupfail = 1; goto cleanup; } snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null", driver, outbin, s); int rc = runwait(cmd); - runwait(rmcmd); - return rc == 0 ? -1 : 0; /* build must NOT succeed */ + result = rc == 0 ? 1 : 0; /* build must NOT succeed */ +cleanup: + if (setupfail) + fprintf(stderr, "arr_infer_len[neg%d]: temporary setup failed\n", i); + snprintf(rmcmd, sizeof rmcmd, "rm -rf -- %s", scratch); + if (runwait(rmcmd) != 0) cleanfail = 1; + if (unlink(s) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(tmpdir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "arr_infer_len[neg%d]: temporary cleanup failed\n", i); + if (result == 0) return -1; + } + return result; } /* asm_byte_identical — w6c vs w6c_ww .s for the same source must match. */ static int asm_byte_identical(const char *bin, const struct row *r, int i) { - char src[64], cs[64], ws[64], cmd[1024]; - snprintf(src, sizeof src, "/tmp/ail_asm_%d_%d.ww", getpid(), i); - snprintf(cs, sizeof cs, "/tmp/ail_asm_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/ail_asm_%d_%d_w.s", getpid(), i); + char tmpdir[64], src[96], cs[96], ws[96], cmd[1024]; + int rc = -1, cleanfail = 0; + snprintf(tmpdir, sizeof tmpdir, "/tmp/ail_asm_%d_%d", getpid(), i); + if (mkdir(tmpdir, 0755) != 0) return -1; + snprintf(src, sizeof src, "%s/t.ww", tmpdir); + snprintf(cs, sizeof cs, "%s/c.s", tmpdir); + snprintf(ws, sizeof ws, "%s/w.s", tmpdir); FILE *f = fopen(src, "wb"); - if (!f) return -1; - fputs(r->src, f); - fclose(f); + if (!f) goto cleanup; + int writefail = fputs(r->src, f) == EOF; + if (fclose(f) != 0) writefail = 1; + if (writefail) goto cleanup; snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c errored\n", r->label); - unlink(src); - return -1; + goto cleanup; } snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null", bin, ws, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label); - unlink(src); unlink(cs); - return -1; + goto cleanup; } FILE *fc = fopen(cs, "rb"); FILE *fw = fopen(ws, "rb"); - int rc = 0; + rc = 0; if (!fc || !fw) { rc = -1; } else { @@ -285,7 +313,15 @@ asm_byte_identical(const char *bin, const struct row *r, int i) if (rc != 0) fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n", r->label); - unlink(src); unlink(cs); unlink(ws); +cleanup: + if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(cs) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(ws) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(tmpdir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "row[%s]: temporary cleanup failed\n", r->label); + return -1; + } return rc; } @@ -338,12 +374,17 @@ main(void) } for (int i = 0; i < nn; i++) { total++; - if (build_should_fail(drivers[d].path, neg[i], - 100 + i) != 0) { - fprintf(stderr, - "arr_infer_len[%s][neg%d]: built ok, " - "expected a loud error\n", - drivers[d].name, i); + int nr = build_should_fail(drivers[d].path, neg[i], 100 + i); + if (nr != 0) { + if (nr > 0) + fprintf(stderr, + "arr_infer_len[%s][neg%d]: built ok, " + "expected a loud error\n", + drivers[d].name, i); + else + fprintf(stderr, + "arr_infer_len[%s][neg%d]: fixture infrastructure failed\n", + drivers[d].name, i); fail++; } } diff --git a/test/wcc/685_arr_tagged_elem.c b/test/wcc/685_arr_tagged_elem.c index d8d68c13..ebd1eebb 100644 --- a/test/wcc/685_arr_tagged_elem.c +++ b/test/wcc/685_arr_tagged_elem.c @@ -51,6 +51,7 @@ */ #include #include +#include #include #include #include @@ -188,56 +189,81 @@ static const char *neg[] = { static int run_driver(const char *driver, const struct row *r, int i) { - char src[128], tmpdir[64], outbin[128], rmcmd[160], cmd[1024]; + char src[128], tmpdir[64], outbin[128], scratch[160], rmcmd[192], cmd[1024]; + int result = -1, setupfail = 0, cleanfail = 0; snprintf(tmpdir, sizeof tmpdir, "/tmp/atag_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); + if (mkdir(tmpdir, 0755) != 0) return -1; snprintf(src, sizeof src, "%s/atag_%d_%d.ww", tmpdir, getpid(), i); snprintf(outbin, sizeof outbin, "%s/atag_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + snprintf(scratch, sizeof scratch, "%s.sepwork", outbin); FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return -1; } - fputs(r->src, f); - fclose(f); + if (!f) { setupfail = 1; goto cleanup; } + int writefail = fputs(r->src, f) == EOF; + if (fclose(f) != 0) writefail = 1; + if (writefail) { setupfail = 1; goto cleanup; } snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null", driver, outbin, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: build via %s failed\n", r->label, driver); - runwait(rmcmd); - return -1; + goto cleanup; } - - int got = runwait(outbin); - - runwait(rmcmd); - return got; + result = runwait(outbin); +cleanup: + if (setupfail) + fprintf(stderr, "row[%s]: temporary setup failed\n", r->label); + snprintf(rmcmd, sizeof rmcmd, "rm -rf -- %s", scratch); + if (runwait(rmcmd) != 0) cleanfail = 1; + if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(tmpdir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "row[%s]: temporary cleanup failed\n", r->label); + if (result == r->want) return -1; + } + return result; } /* build_should_fail — a `[N]tagged=[x,...]` repeat-fill must error on * `driver` (rule-7 loud-stop); returns 0 when the build correctly - * FAILS, non-zero when it wrongly succeeded. */ + * FAILS, positive when it wrongly succeeds, and negative on fixture + * infrastructure failure. */ static int build_should_fail(const char *driver, const char *src, int i) { - char s[128], tmpdir[64], outbin[128], rmcmd[160], cmd[1024]; + char s[128], tmpdir[64], outbin[128], scratch[160], rmcmd[192], cmd[1024]; + int result = -1, setupfail = 0, cleanfail = 0; snprintf(tmpdir, sizeof tmpdir, "/tmp/atagn_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); + if (mkdir(tmpdir, 0755) != 0) return -1; snprintf(s, sizeof s, "%s/atagn_%d_%d.ww", tmpdir, getpid(), i); snprintf(outbin, sizeof outbin, "%s/atagn_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + snprintf(scratch, sizeof scratch, "%s.sepwork", outbin); FILE *f = fopen(s, "wb"); - if (!f) { runwait(rmcmd); return -1; } - fputs(src, f); - fclose(f); + if (!f) { setupfail = 1; goto cleanup; } + int writefail = fputs(src, f) == EOF; + if (fclose(f) != 0) writefail = 1; + if (writefail) { setupfail = 1; goto cleanup; } snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null", driver, outbin, s); int rc = runwait(cmd); - runwait(rmcmd); - return rc == 0 ? -1 : 0; /* build must NOT succeed */ + result = rc == 0 ? 1 : 0; /* build must NOT succeed */ +cleanup: + if (setupfail) + fprintf(stderr, "arr_tagged_elem[neg%d]: temporary setup failed\n", i); + snprintf(rmcmd, sizeof rmcmd, "rm -rf -- %s", scratch); + if (runwait(rmcmd) != 0) cleanfail = 1; + if (unlink(s) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(tmpdir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "arr_tagged_elem[neg%d]: temporary cleanup failed\n", i); + if (result == 0) return -1; + } + return result; } /* asm_byte_identical — generate .s via cstage's w6c and wwstage's @@ -247,33 +273,35 @@ build_should_fail(const char *driver, const char *src, int i) static int asm_byte_identical(const char *bin, const struct row *r, int i) { - char src[64], cs[64], ws[64], cmd[1024]; - snprintf(src, sizeof src, "/tmp/atag_asm_%d_%d.ww", getpid(), i); - snprintf(cs, sizeof cs, "/tmp/atag_asm_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/atag_asm_%d_%d_w.s", getpid(), i); + char tmpdir[64], src[96], cs[96], ws[96], cmd[1024]; + int rc = -1, cleanfail = 0; + snprintf(tmpdir, sizeof tmpdir, "/tmp/atag_asm_%d_%d", getpid(), i); + if (mkdir(tmpdir, 0755) != 0) return -1; + snprintf(src, sizeof src, "%s/t.ww", tmpdir); + snprintf(cs, sizeof cs, "%s/c.s", tmpdir); + snprintf(ws, sizeof ws, "%s/w.s", tmpdir); FILE *f = fopen(src, "wb"); - if (!f) return -1; - fputs(r->src, f); - fclose(f); + if (!f) goto cleanup; + int writefail = fputs(r->src, f) == EOF; + if (fclose(f) != 0) writefail = 1; + if (writefail) goto cleanup; snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c errored\n", r->label); - unlink(src); - return -1; + goto cleanup; } snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null", bin, ws, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label); - unlink(src); unlink(cs); - return -1; + goto cleanup; } FILE *fc = fopen(cs, "rb"); FILE *fw = fopen(ws, "rb"); - int rc = 0; + rc = 0; if (!fc || !fw) { rc = -1; } else { @@ -289,7 +317,16 @@ asm_byte_identical(const char *bin, const struct row *r, int i) if (rc != 0) fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n", r->label); - unlink(src); unlink(cs); unlink(ws); + +cleanup: + if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(cs) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(ws) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(tmpdir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "row[%s]: temporary cleanup failed\n", r->label); + return -1; + } return rc; } @@ -342,12 +379,17 @@ main(void) } for (int i = 0; i < nn; i++) { total++; - if (build_should_fail(drivers[d].path, neg[i], - 100 + i) != 0) { - fprintf(stderr, - "arr_tagged_elem[%s][neg%d]: built ok, " - "expected a loud error (rule-7)\n", - drivers[d].name, i); + int nr = build_should_fail(drivers[d].path, neg[i], 100 + i); + if (nr != 0) { + if (nr > 0) + fprintf(stderr, + "arr_tagged_elem[%s][neg%d]: built ok, " + "expected a loud error (rule-7)\n", + drivers[d].name, i); + else + fprintf(stderr, + "arr_tagged_elem[%s][neg%d]: fixture infrastructure failed\n", + drivers[d].name, i); fail++; } } diff --git a/test/wcc/686_slice_str_global_zero.c b/test/wcc/686_slice_str_global_zero.c index e58bbf21..2964c122 100644 --- a/test/wcc/686_slice_str_global_zero.c +++ b/test/wcc/686_slice_str_global_zero.c @@ -26,6 +26,7 @@ */ #include #include +#include #include #include #include @@ -82,31 +83,39 @@ static const struct row rows[] = { static int run_driver(const char *driver, const struct row *r, int i) { - char src[128], tmpdir[64], outbin[128], rmcmd[160], cmd[1024]; + char src[128], tmpdir[64], outbin[128], scratch[160], rmcmd[192], cmd[1024]; + int result = -1, cleanfail = 0; snprintf(tmpdir, sizeof tmpdir, "/tmp/ssgz_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); + if (mkdir(tmpdir, 0755) != 0) return -1; snprintf(src, sizeof src, "%s/ssgz_%d_%d.ww", tmpdir, getpid(), i); snprintf(outbin, sizeof outbin, "%s/ssgz_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + snprintf(scratch, sizeof scratch, "%s.sepwork", outbin); FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return -1; } - fputs(r->src, f); - fclose(f); + if (!f) goto cleanup; + int writefail = fputs(r->src, f) == EOF; + if (fclose(f) != 0) writefail = 1; + if (writefail) goto cleanup; snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null", driver, outbin, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: build via %s failed\n", r->label, driver); - runwait(rmcmd); - return -1; + goto cleanup; } - - int got = runwait(outbin); - - runwait(rmcmd); - return got; + result = runwait(outbin); +cleanup: + snprintf(rmcmd, sizeof rmcmd, "rm -rf -- %s", scratch); + if (runwait(rmcmd) != 0) cleanfail = 1; + if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(tmpdir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "row[%s]: temporary cleanup failed\n", r->label); + if (result == r->want) return -1; + } + return result; } /* asm_byte_identical — w6c vs w6c_ww .s for the same source must match. @@ -115,33 +124,35 @@ run_driver(const char *driver, const struct row *r, int i) static int asm_byte_identical(const char *bin, const struct row *r, int i) { - char src[64], cs[64], ws[64], cmd[1024]; - snprintf(src, sizeof src, "/tmp/ssgz_asm_%d_%d.ww", getpid(), i); - snprintf(cs, sizeof cs, "/tmp/ssgz_asm_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/ssgz_asm_%d_%d_w.s", getpid(), i); + char tmpdir[64], src[96], cs[96], ws[96], cmd[1024]; + int rc = -1, cleanfail = 0; + snprintf(tmpdir, sizeof tmpdir, "/tmp/ssgz_asm_%d_%d", getpid(), i); + if (mkdir(tmpdir, 0755) != 0) return -1; + snprintf(src, sizeof src, "%s/t.ww", tmpdir); + snprintf(cs, sizeof cs, "%s/c.s", tmpdir); + snprintf(ws, sizeof ws, "%s/w.s", tmpdir); FILE *f = fopen(src, "wb"); - if (!f) return -1; - fputs(r->src, f); - fclose(f); + if (!f) goto cleanup; + int writefail = fputs(r->src, f) == EOF; + if (fclose(f) != 0) writefail = 1; + if (writefail) goto cleanup; snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c errored\n", r->label); - unlink(src); - return -1; + goto cleanup; } snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null", bin, ws, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label); - unlink(src); unlink(cs); - return -1; + goto cleanup; } FILE *fc = fopen(cs, "rb"); FILE *fw = fopen(ws, "rb"); - int rc = 0; + rc = 0; if (!fc || !fw) { rc = -1; } else { @@ -157,7 +168,15 @@ asm_byte_identical(const char *bin, const struct row *r, int i) if (rc != 0) fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n", r->label); - unlink(src); unlink(cs); unlink(ws); +cleanup: + if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(cs) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(ws) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(tmpdir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "row[%s]: temporary cleanup failed\n", r->label); + return -1; + } return rc; } diff --git a/test/wcc/687_slice_literal_global.c b/test/wcc/687_slice_literal_global.c index 8518b3ca..a09ad9cb 100644 --- a/test/wcc/687_slice_literal_global.c +++ b/test/wcc/687_slice_literal_global.c @@ -50,6 +50,7 @@ */ #include #include +#include #include #include #include @@ -194,91 +195,116 @@ static const char *neg[] = { static int run_driver(const char *driver, const struct row *r, int i) { - char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024]; + char tmpdir[64], src[128], outbin[128], scratch[160], rmcmd[192], cmd[1024]; + int result = -1, setupfail = 0, cleanfail = 0; snprintf(tmpdir, sizeof tmpdir, "/tmp/slg_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); + if (mkdir(tmpdir, 0755) != 0) return -1; snprintf(src, sizeof src, "%s/slg_%d_%d.ww", tmpdir, getpid(), i); snprintf(outbin, sizeof outbin, "%s/slg_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + snprintf(scratch, sizeof scratch, "%s.sepwork", outbin); FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return -1; } - fputs(r->src, f); - fclose(f); + if (!f) { setupfail = 1; goto cleanup; } + int writefail = fputs(r->src, f) == EOF; + if (fclose(f) != 0) writefail = 1; + if (writefail) { setupfail = 1; goto cleanup; } - /* #8: -o pins binary + .sepwork scratch under tmpdir; rm -rf on - * every exit path removes the now-non-empty dir. */ + /* Ordinary build retains caller-owned .sepwork scratch beside -o. */ snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null", driver, outbin, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: build via %s failed\n", r->label, driver); - runwait(rmcmd); - return -1; + goto cleanup; } - - int got = runwait(outbin); - - runwait(rmcmd); - return got; + result = runwait(outbin); +cleanup: + if (setupfail) + fprintf(stderr, "row[%s]: temporary setup failed\n", r->label); + snprintf(rmcmd, sizeof rmcmd, "rm -rf -- %s", scratch); + if (runwait(rmcmd) != 0) cleanfail = 1; + if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(tmpdir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "row[%s]: temporary cleanup failed\n", r->label); + if (result == r->want) return -1; + } + return result; } -/* build_should_fail — returns 0 when the build correctly FAILS. */ +/* build_should_fail — 0 on the expected rejection, positive on an + * unexpected successful build, negative on fixture infrastructure failure. */ static int build_should_fail(const char *driver, const char *src, int i) { - char tmpdir[64], s[128], outbin[128], rmcmd[160], cmd[1024]; + char tmpdir[64], s[128], outbin[128], scratch[160], rmcmd[192], cmd[1024]; + int result = -1, setupfail = 0, cleanfail = 0; snprintf(tmpdir, sizeof tmpdir, "/tmp/slgn_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); + if (mkdir(tmpdir, 0755) != 0) return -1; snprintf(s, sizeof s, "%s/slgn_%d_%d.ww", tmpdir, getpid(), i); snprintf(outbin, sizeof outbin, "%s/slgn_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + snprintf(scratch, sizeof scratch, "%s.sepwork", outbin); FILE *f = fopen(s, "wb"); - if (!f) { runwait(rmcmd); return -1; } - fputs(src, f); - fclose(f); + if (!f) { setupfail = 1; goto cleanup; } + int writefail = fputs(src, f) == EOF; + if (fclose(f) != 0) writefail = 1; + if (writefail) { setupfail = 1; goto cleanup; } - /* #8: -o pins binary + .sepwork scratch under tmpdir; a reject build - * still mkdir's scratch, so rm -rf must run on this path too. */ + /* A rejected ordinary build can also leave caller-owned .sepwork. */ snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null", driver, outbin, s); int rc = runwait(cmd); - runwait(rmcmd); - return rc == 0 ? -1 : 0; /* build must NOT succeed */ + result = rc == 0 ? 1 : 0; /* build must NOT succeed */ +cleanup: + if (setupfail) + fprintf(stderr, "slice_literal_global[neg%d]: temporary setup failed\n", i); + snprintf(rmcmd, sizeof rmcmd, "rm -rf -- %s", scratch); + if (runwait(rmcmd) != 0) cleanfail = 1; + if (unlink(s) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(tmpdir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "slice_literal_global[neg%d]: temporary cleanup failed\n", i); + if (result == 0) return -1; + } + return result; } /* asm_byte_identical — w6c vs w6c_ww .s for the same source must match. */ static int asm_byte_identical(const char *bin, const struct row *r, int i) { - char src[64], cs[64], ws[64], cmd[1024]; - snprintf(src, sizeof src, "/tmp/slg_asm_%d_%d.ww", getpid(), i); - snprintf(cs, sizeof cs, "/tmp/slg_asm_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/slg_asm_%d_%d_w.s", getpid(), i); + char tmpdir[64], src[96], cs[96], ws[96], cmd[1024]; + int rc = -1, cleanfail = 0; + snprintf(tmpdir, sizeof tmpdir, "/tmp/slg_asm_%d_%d", getpid(), i); + if (mkdir(tmpdir, 0755) != 0) return -1; + snprintf(src, sizeof src, "%s/t.ww", tmpdir); + snprintf(cs, sizeof cs, "%s/c.s", tmpdir); + snprintf(ws, sizeof ws, "%s/w.s", tmpdir); FILE *f = fopen(src, "wb"); - if (!f) return -1; - fputs(r->src, f); - fclose(f); + if (!f) goto cleanup; + int writefail = fputs(r->src, f) == EOF; + if (fclose(f) != 0) writefail = 1; + if (writefail) goto cleanup; snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c errored\n", r->label); - unlink(src); - return -1; + goto cleanup; } snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null", bin, ws, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label); - unlink(src); unlink(cs); - return -1; + goto cleanup; } FILE *fc = fopen(cs, "rb"); FILE *fw = fopen(ws, "rb"); - int rc = 0; + rc = 0; if (!fc || !fw) { rc = -1; } else { @@ -294,7 +320,15 @@ asm_byte_identical(const char *bin, const struct row *r, int i) if (rc != 0) fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n", r->label); - unlink(src); unlink(cs); unlink(ws); +cleanup: + if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(cs) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(ws) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(tmpdir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "row[%s]: temporary cleanup failed\n", r->label); + return -1; + } return rc; } @@ -347,12 +381,17 @@ main(void) } for (int i = 0; i < nn; i++) { total++; - if (build_should_fail(drivers[d].path, neg[i], - 100 + i) != 0) { - fprintf(stderr, - "slice_literal_global[%s][neg%d]: built ok, " - "expected a loud error\n", - drivers[d].name, i); + int nr = build_should_fail(drivers[d].path, neg[i], 100 + i); + if (nr != 0) { + if (nr > 0) + fprintf(stderr, + "slice_literal_global[%s][neg%d]: built ok, " + "expected a loud error\n", + drivers[d].name, i); + else + fprintf(stderr, + "slice_literal_global[%s][neg%d]: fixture infrastructure failed\n", + drivers[d].name, i); fail++; } } diff --git a/test/wcc/688_global_arr_elem_field.c b/test/wcc/688_global_arr_elem_field.c index 5855e49e..350d8f36 100644 --- a/test/wcc/688_global_arr_elem_field.c +++ b/test/wcc/688_global_arr_elem_field.c @@ -45,6 +45,7 @@ */ #include #include +#include #include #include #include @@ -130,31 +131,40 @@ static const struct row rows[] = { static int run_driver(const char *driver, const struct row *r, int i) { - char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024]; + char tmpdir[64], src[128], outbin[128], scratch[160], rmcmd[192], cmd[1024]; + int result = -1, cleanfail = 0; snprintf(tmpdir, sizeof tmpdir, "/tmp/gae_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); + if (mkdir(tmpdir, 0755) != 0) return -1; snprintf(src, sizeof src, "%s/gae_%d_%d.ww", tmpdir, getpid(), i); snprintf(outbin, sizeof outbin, "%s/gae_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + snprintf(scratch, sizeof scratch, "%s.sepwork", outbin); FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return -1; } + if (!f) goto cleanup; wwtest_fputs(r->src, f); - fclose(f); + int writefail = ferror(f); + if (fclose(f) != 0) writefail = 1; + if (writefail) goto cleanup; snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null", driver, outbin, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: build via %s failed\n", r->label, driver); - runwait(rmcmd); - return -1; + goto cleanup; } - - int got = runwait(outbin); - - runwait(rmcmd); - return got; + result = runwait(outbin); +cleanup: + snprintf(rmcmd, sizeof rmcmd, "rm -rf -- %s", scratch); + if (runwait(rmcmd) != 0) cleanfail = 1; + if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(tmpdir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "row[%s]: temporary cleanup failed\n", r->label); + if (result == r->want) return -1; + } + return result; } /* asm_byte_identical — generate .s via cstage's w6c and wwstage's @@ -163,33 +173,36 @@ run_driver(const char *driver, const struct row *r, int i) static int asm_byte_identical(const char *bin, const struct row *r, int i) { - char src[64], cs[64], ws[64], cmd[1024]; - snprintf(src, sizeof src, "/tmp/gae_asm_%d_%d.ww", getpid(), i); - snprintf(cs, sizeof cs, "/tmp/gae_asm_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/gae_asm_%d_%d_w.s", getpid(), i); + char tmpdir[64], src[96], cs[96], ws[96], cmd[1024]; + int rc = -1, cleanfail = 0; + snprintf(tmpdir, sizeof tmpdir, "/tmp/gae_asm_%d_%d", getpid(), i); + if (mkdir(tmpdir, 0755) != 0) return -1; + snprintf(src, sizeof src, "%s/t.ww", tmpdir); + snprintf(cs, sizeof cs, "%s/c.s", tmpdir); + snprintf(ws, sizeof ws, "%s/w.s", tmpdir); FILE *f = fopen(src, "wb"); - if (!f) return -1; + if (!f) goto cleanup; wwtest_fputs(r->src, f); - fclose(f); + int writefail = ferror(f); + if (fclose(f) != 0) writefail = 1; + if (writefail) goto cleanup; snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c errored\n", r->label); - unlink(src); - return -1; + goto cleanup; } snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null", bin, ws, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label); - unlink(src); unlink(cs); - return -1; + goto cleanup; } FILE *fc = fopen(cs, "rb"); FILE *fw = fopen(ws, "rb"); - int rc = 0; + rc = 0; if (!fc || !fw) { rc = -1; } else { @@ -205,7 +218,15 @@ asm_byte_identical(const char *bin, const struct row *r, int i) if (rc != 0) fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n", r->label); - unlink(src); unlink(cs); unlink(ws); +cleanup: + if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(cs) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(ws) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(tmpdir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "row[%s]: temporary cleanup failed\n", r->label); + return -1; + } return rc; } diff --git a/test/wcc/689_arrglob_nodup_run.c b/test/wcc/689_arrglob_nodup_run.c index b3135ce1..94a16d02 100644 --- a/test/wcc/689_arrglob_nodup_run.c +++ b/test/wcc/689_arrglob_nodup_run.c @@ -25,6 +25,7 @@ */ #include #include +#include #include #include #include @@ -50,55 +51,73 @@ static const char *SRC = static int run_driver(const char *driver) { - char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024]; + char tmpdir[64], src[128], outbin[128], scratch[160], rmcmd[192], cmd[1024]; + int result = -1, cleanfail = 0; snprintf(tmpdir, sizeof tmpdir, "/tmp/agnd_%d_d", getpid()); - mkdir(tmpdir, 0755); + if (mkdir(tmpdir, 0755) != 0) return -1; snprintf(src, sizeof src, "%s/agnd_%d.ww", tmpdir, getpid()); snprintf(outbin, sizeof outbin, "%s/agnd_%d", tmpdir, getpid()); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + snprintf(scratch, sizeof scratch, "%s.sepwork", outbin); FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return -1; } - fputs(SRC, f); - fclose(f); + if (!f) goto cleanup; + int writefail = fputs(SRC, f) == EOF; + if (fclose(f) != 0) writefail = 1; + if (writefail) goto cleanup; snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null", driver, outbin, src); - if (runwait(cmd) != 0) { runwait(rmcmd); return -1; } - - int got = runwait(outbin); - - runwait(rmcmd); - return got; + if (runwait(cmd) != 0) goto cleanup; + result = runwait(outbin); +cleanup: + snprintf(rmcmd, sizeof rmcmd, "rm -rf -- %s", scratch); + if (runwait(rmcmd) != 0) cleanfail = 1; + if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(tmpdir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "arrglob_nodup: temporary cleanup failed\n"); + if (result == 6) return -1; + } + return result; } /* emit .s via `tool`, return the count of `DATAW main.g(SB)` rows, or -1. */ static int dataw_rows(const char *bin, const char *tool) { - char src[64], asmf[80], cmd[1024]; - snprintf(src, sizeof src, "/tmp/agnd_asm_%d.ww", getpid()); - snprintf(asmf, sizeof asmf, "/tmp/agnd_asm_%d_%s.s", getpid(), tool); + char tmpdir[80], src[96], asmf[96], cmd[1024]; + int n = -1, cleanfail = 0; + snprintf(tmpdir, sizeof tmpdir, "/tmp/agnd_asm_%d_%s", getpid(), tool); + if (mkdir(tmpdir, 0755) != 0) return -1; + snprintf(src, sizeof src, "%s/t.ww", tmpdir); + snprintf(asmf, sizeof asmf, "%s/t.s", tmpdir); FILE *f = fopen(src, "wb"); - if (!f) return -1; - fputs(SRC, f); - fclose(f); + if (!f) goto cleanup; + int writefail = fputs(SRC, f) == EOF; + if (fclose(f) != 0) writefail = 1; + if (writefail) goto cleanup; snprintf(cmd, sizeof cmd, "%s/%s -o %s %s 2>/dev/null", bin, tool, asmf, src); - if (runwait(cmd) != 0) { unlink(src); return -1; } + if (runwait(cmd) != 0) goto cleanup; FILE *a = fopen(asmf, "rb"); - int n = 0; + n = 0; if (a) { char line[512]; while (fgets(line, sizeof line, a)) if (strncmp(line, "DATAW main.g(SB)", 16) == 0) n++; fclose(a); - } else { - n = -1; + } else n = -1; +cleanup: + if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(asmf) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(tmpdir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "arrglob_nodup[%s]: temporary cleanup failed\n", tool); + if (n == 1) return -1; } - unlink(src); unlink(asmf); return n; } diff --git a/test/wcc/689_globptr_field_read_run.c b/test/wcc/689_globptr_field_read_run.c index 88d2c174..9886db6a 100644 --- a/test/wcc/689_globptr_field_read_run.c +++ b/test/wcc/689_globptr_field_read_run.c @@ -46,6 +46,7 @@ */ #include #include +#include #include #include #include @@ -174,43 +175,48 @@ static const struct row ROWS[] = { /* build+run one row through `driver`; return exit code or -1. */ static int -run_driver(const char *driver, const char *src, int idx) +run_driver(const char *driver, const char *src, int idx, int want) { - char tmpdir[64], srcf[128], outbin[160], rmcmd[192], cmd[2400]; + char tmpdir[64], srcf[128], outbin[160], scratch[192], rmcmd[224], cmd[2400]; + int result = -1, cleanfail = 0; snprintf(tmpdir, sizeof tmpdir, "/tmp/gpfr_%d_%d_d", getpid(), idx); - mkdir(tmpdir, 0755); + if (mkdir(tmpdir, 0755) != 0) return -1; snprintf(srcf, sizeof srcf, "%s/t.ww", tmpdir); snprintf(outbin, sizeof outbin, "%s/t", tmpdir); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + snprintf(scratch, sizeof scratch, "%s.sepwork", outbin); FILE *f = fopen(srcf, "wb"); - if (!f) { runwait(rmcmd); return -1; } - fputs(src, f); - fclose(f); + if (!f) goto cleanup; + int writefail = fputs(src, f) == EOF; + if (fclose(f) != 0) writefail = 1; + if (writefail) goto cleanup; snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null", driver, outbin, srcf); - if (runwait(cmd) != 0) { runwait(rmcmd); return -1; } - int got = runwait(outbin); - runwait(rmcmd); - return got; + if (runwait(cmd) != 0) goto cleanup; + result = runwait(outbin); +cleanup: + snprintf(rmcmd, sizeof rmcmd, "rm -rf -- %s", scratch); + if (runwait(rmcmd) != 0) cleanfail = 1; + if (unlink(srcf) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(tmpdir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "globptr_field_read[%d]: temporary cleanup failed\n", idx); + if (result == want) return -1; + } + return result; } -/* emit .s for `tool` (w6c / w6c_ww) into asmf; 0 ok, -1 on compile fail. */ +/* Emit .s for `tool`; the caller owns srcf and asmf. */ static int -emit_asm(const char *bin, const char *tool, const char *src, int idx, +emit_asm(const char *bin, const char *tool, const char *srcf, const char *asmf) { - char srcf[80], cmd[2400]; - snprintf(srcf, sizeof srcf, "/tmp/gpfr_asm_%d_%d.ww", getpid(), idx); - FILE *f = fopen(srcf, "wb"); - if (!f) return -1; - fputs(src, f); - fclose(f); + char cmd[2400]; snprintf(cmd, sizeof cmd, "%s/%s -o %s %s 2>/dev/null", bin, tool, asmf, srcf); int rc = runwait(cmd); - unlink(srcf); return rc == 0 ? 0 : -1; } @@ -250,7 +256,7 @@ main(void) const struct row *r = &ROWS[i]; total++; - int gc = run_driver(cdrv, r->src, i); + int gc = run_driver(cdrv, r->src, i, r->want); if (gc != r->want) { fprintf(stderr, "globptr_field_read[%s,cstage]: run=%d want=%d\n", r->name, gc, r->want); @@ -258,7 +264,7 @@ main(void) } if (have_ww) { total++; - int gw = run_driver(wdrv, r->src, i); + int gw = run_driver(wdrv, r->src, i, r->want); if (gw != r->want) { fprintf(stderr, "globptr_field_read[%s,wwstage]: run=%d want=%d\n", r->name, gw, r->want); @@ -268,18 +274,43 @@ main(void) /* byte-id: w6c .s == w6c_ww .s */ if (have_ww) { - char csf[80], wwf[80]; - snprintf(csf, sizeof csf, "/tmp/gpfr_cs_%d_%d.s", getpid(), i); - snprintf(wwf, sizeof wwf, "/tmp/gpfr_ww_%d_%d.s", getpid(), i); + char td[80], srcf[96], csf[96], wwf[96]; + int ec = -1, ew = -1, same = 0, setupfail = 0, cleanfail = 0; + snprintf(td, sizeof td, "/tmp/gpfr_asm_%d_%d", getpid(), i); + snprintf(srcf, sizeof srcf, "%s/t.ww", td); + snprintf(csf, sizeof csf, "%s/c.s", td); + snprintf(wwf, sizeof wwf, "%s/w.s", td); total++; - int ec = emit_asm(bin, "w6c", r->src, i, csf); - int ew = emit_asm(bin, "w6c_ww", r->src, i, wwf); - if (ec != 0 || ew != 0 || !files_identical(csf, wwf)) { + if (mkdir(td, 0755) == 0) { + FILE *f = fopen(srcf, "wb"); + if (f) { + int writefail = fputs(r->src, f) == EOF; + if (fclose(f) != 0) writefail = 1; + if (!writefail) { + ec = emit_asm(bin, "w6c", srcf, csf); + ew = emit_asm(bin, "w6c_ww", srcf, wwf); + same = ec == 0 && ew == 0 + && files_identical(csf, wwf); + } else setupfail = 1; + } else setupfail = 1; + if (unlink(srcf) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(csf) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(wwf) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(td) != 0) cleanfail = 1; + } else setupfail = 1; + if (setupfail) + fprintf(stderr, "globptr_field_read[%s]: temporary setup failed\n", + r->name); + if (!same) { fprintf(stderr, "globptr_field_read[%s]: cs/ww .s NOT " "byte-identical (ec=%d ew=%d)\n", r->name, ec, ew); fail++; } - unlink(csf); unlink(wwf); + if (cleanfail) { + fprintf(stderr, "globptr_field_read[%s]: temporary cleanup failed\n", + r->name); + if (same) fail++; + } } } diff --git a/test/wcc/689_globptr_field_store_run.c b/test/wcc/689_globptr_field_store_run.c index c670c4bf..d6abc174 100644 --- a/test/wcc/689_globptr_field_store_run.c +++ b/test/wcc/689_globptr_field_store_run.c @@ -32,6 +32,7 @@ */ #include #include +#include #include #include #include @@ -108,43 +109,48 @@ static const struct row ROWS[] = { /* build+run one row through `driver`; return exit code or -1. */ static int -run_driver(const char *driver, const char *src, int idx) +run_driver(const char *driver, const char *src, int idx, int want) { - char tmpdir[64], srcf[128], outbin[160], rmcmd[192], cmd[2400]; + char tmpdir[64], srcf[128], outbin[160], scratch[192], rmcmd[224], cmd[2400]; + int result = -1, cleanfail = 0; snprintf(tmpdir, sizeof tmpdir, "/tmp/gpfs_%d_%d_d", getpid(), idx); - mkdir(tmpdir, 0755); + if (mkdir(tmpdir, 0755) != 0) return -1; snprintf(srcf, sizeof srcf, "%s/t.ww", tmpdir); snprintf(outbin, sizeof outbin, "%s/t", tmpdir); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + snprintf(scratch, sizeof scratch, "%s.sepwork", outbin); FILE *f = fopen(srcf, "wb"); - if (!f) { runwait(rmcmd); return -1; } - fputs(src, f); - fclose(f); + if (!f) goto cleanup; + int writefail = fputs(src, f) == EOF; + if (fclose(f) != 0) writefail = 1; + if (writefail) goto cleanup; snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null", driver, outbin, srcf); - if (runwait(cmd) != 0) { runwait(rmcmd); return -1; } - int got = runwait(outbin); - runwait(rmcmd); - return got; + if (runwait(cmd) != 0) goto cleanup; + result = runwait(outbin); +cleanup: + snprintf(rmcmd, sizeof rmcmd, "rm -rf -- %s", scratch); + if (runwait(rmcmd) != 0) cleanfail = 1; + if (unlink(srcf) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(tmpdir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "globptr_field_store[%d]: temporary cleanup failed\n", idx); + if (result == want) return -1; + } + return result; } -/* emit .s for `tool` (w6c / w6c_ww) into asmf; 0 ok, -1 on compile fail. */ +/* Emit .s for `tool`; the caller owns srcf and asmf. */ static int -emit_asm(const char *bin, const char *tool, const char *src, int idx, +emit_asm(const char *bin, const char *tool, const char *srcf, const char *asmf) { - char srcf[80], cmd[2400]; - snprintf(srcf, sizeof srcf, "/tmp/gpfs_asm_%d_%d.ww", getpid(), idx); - FILE *f = fopen(srcf, "wb"); - if (!f) return -1; - fputs(src, f); - fclose(f); + char cmd[2400]; snprintf(cmd, sizeof cmd, "%s/%s -o %s %s 2>/dev/null", bin, tool, asmf, srcf); int rc = runwait(cmd); - unlink(srcf); return rc == 0 ? 0 : -1; } @@ -184,7 +190,7 @@ main(void) const struct row *r = &ROWS[i]; total++; - int gc = run_driver(cdrv, r->src, i); + int gc = run_driver(cdrv, r->src, i, r->want); if (gc != r->want) { fprintf(stderr, "globptr_field_store[%s,cstage]: run=%d want=%d\n", r->name, gc, r->want); @@ -192,7 +198,7 @@ main(void) } if (have_ww) { total++; - int gw = run_driver(wdrv, r->src, i); + int gw = run_driver(wdrv, r->src, i, r->want); if (gw != r->want) { fprintf(stderr, "globptr_field_store[%s,wwstage]: run=%d want=%d\n", r->name, gw, r->want); @@ -202,18 +208,43 @@ main(void) /* byte-id: w6c .s == w6c_ww .s */ if (have_ww) { - char csf[80], wwf[80]; - snprintf(csf, sizeof csf, "/tmp/gpfs_cs_%d_%d.s", getpid(), i); - snprintf(wwf, sizeof wwf, "/tmp/gpfs_ww_%d_%d.s", getpid(), i); + char td[80], srcf[96], csf[96], wwf[96]; + int ec = -1, ew = -1, same = 0, setupfail = 0, cleanfail = 0; + snprintf(td, sizeof td, "/tmp/gpfs_asm_%d_%d", getpid(), i); + snprintf(srcf, sizeof srcf, "%s/t.ww", td); + snprintf(csf, sizeof csf, "%s/c.s", td); + snprintf(wwf, sizeof wwf, "%s/w.s", td); total++; - int ec = emit_asm(bin, "w6c", r->src, i, csf); - int ew = emit_asm(bin, "w6c_ww", r->src, i, wwf); - if (ec != 0 || ew != 0 || !files_identical(csf, wwf)) { + if (mkdir(td, 0755) == 0) { + FILE *f = fopen(srcf, "wb"); + if (f) { + int writefail = fputs(r->src, f) == EOF; + if (fclose(f) != 0) writefail = 1; + if (!writefail) { + ec = emit_asm(bin, "w6c", srcf, csf); + ew = emit_asm(bin, "w6c_ww", srcf, wwf); + same = ec == 0 && ew == 0 + && files_identical(csf, wwf); + } else setupfail = 1; + } else setupfail = 1; + if (unlink(srcf) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(csf) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(wwf) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(td) != 0) cleanfail = 1; + } else setupfail = 1; + if (setupfail) + fprintf(stderr, "globptr_field_store[%s]: temporary setup failed\n", + r->name); + if (!same) { fprintf(stderr, "globptr_field_store[%s]: cs/ww .s NOT " "byte-identical (ec=%d ew=%d)\n", r->name, ec, ew); fail++; } - unlink(csf); unlink(wwf); + if (cleanfail) { + fprintf(stderr, "globptr_field_store[%s]: temporary cleanup failed\n", + r->name); + if (same) fail++; + } } } diff --git a/test/wcc/689_structlit_slice_field.c b/test/wcc/689_structlit_slice_field.c index da6d08c1..7026d46b 100644 --- a/test/wcc/689_structlit_slice_field.c +++ b/test/wcc/689_structlit_slice_field.c @@ -48,6 +48,7 @@ */ #include #include +#include #include #include #include @@ -197,31 +198,39 @@ static const struct row rows[] = { static int run_driver(const char *driver, const struct row *r, int i) { - char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024]; + char tmpdir[64], src[128], outbin[128], scratch[160], rmcmd[192], cmd[1024]; + int result = -1, cleanfail = 0; snprintf(tmpdir, sizeof tmpdir, "/tmp/slf_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); + if (mkdir(tmpdir, 0755) != 0) return -1; snprintf(src, sizeof src, "%s/slf_%d_%d.ww", tmpdir, getpid(), i); snprintf(outbin, sizeof outbin, "%s/slf_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + snprintf(scratch, sizeof scratch, "%s.sepwork", outbin); FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return -1; } - fputs(r->src, f); - fclose(f); + if (!f) goto cleanup; + int writefail = fputs(r->src, f) == EOF; + if (fclose(f) != 0) writefail = 1; + if (writefail) goto cleanup; snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null", driver, outbin, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: build via %s failed\n", r->label, driver); - runwait(rmcmd); - return -1; + goto cleanup; } - - int got = runwait(outbin); - - runwait(rmcmd); - return got; + result = runwait(outbin); +cleanup: + snprintf(rmcmd, sizeof rmcmd, "rm -rf -- %s", scratch); + if (runwait(rmcmd) != 0) cleanfail = 1; + if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(tmpdir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "row[%s]: temporary cleanup failed\n", r->label); + if (result == r->want) return -1; + } + return result; } /* asm_byte_identical — w6c vs w6c_ww .s for the same source must match. @@ -230,33 +239,35 @@ run_driver(const char *driver, const struct row *r, int i) static int asm_byte_identical(const char *bin, const struct row *r, int i) { - char src[64], cs[64], ws[64], cmd[1024]; - snprintf(src, sizeof src, "/tmp/slf_asm_%d_%d.ww", getpid(), i); - snprintf(cs, sizeof cs, "/tmp/slf_asm_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/slf_asm_%d_%d_w.s", getpid(), i); + char tmpdir[64], src[96], cs[96], ws[96], cmd[1024]; + int rc = -1, cleanfail = 0; + snprintf(tmpdir, sizeof tmpdir, "/tmp/slf_asm_%d_%d", getpid(), i); + if (mkdir(tmpdir, 0755) != 0) return -1; + snprintf(src, sizeof src, "%s/t.ww", tmpdir); + snprintf(cs, sizeof cs, "%s/c.s", tmpdir); + snprintf(ws, sizeof ws, "%s/w.s", tmpdir); FILE *f = fopen(src, "wb"); - if (!f) return -1; - fputs(r->src, f); - fclose(f); + if (!f) goto cleanup; + int writefail = fputs(r->src, f) == EOF; + if (fclose(f) != 0) writefail = 1; + if (writefail) goto cleanup; snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c errored\n", r->label); - unlink(src); - return -1; + goto cleanup; } snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null", bin, ws, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label); - unlink(src); unlink(cs); - return -1; + goto cleanup; } FILE *fc = fopen(cs, "rb"); FILE *fw = fopen(ws, "rb"); - int rc = 0; + rc = 0; if (!fc || !fw) { rc = -1; } else { @@ -272,7 +283,15 @@ asm_byte_identical(const char *bin, const struct row *r, int i) if (rc != 0) fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n", r->label); - unlink(src); unlink(cs); unlink(ws); +cleanup: + if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(cs) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(ws) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(tmpdir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "row[%s]: temporary cleanup failed\n", r->label); + return -1; + } return rc; } diff --git a/test/wcc/696_modtype_leaf_collision.c b/test/wcc/696_modtype_leaf_collision.c index 035a9823..21947601 100644 --- a/test/wcc/696_modtype_leaf_collision.c +++ b/test/wcc/696_modtype_leaf_collision.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -37,65 +38,99 @@ runwait(const char *cmd) static int run_pos(const char *driver, const char *fixdir, const char *tag) { - char td[1024]; + char td[1024], bin[1024], sepwork[1100]; snprintf(td, sizeof td, "/tmp/modcol_pos_%d_%s", getpid(), tag); - mkdir(td, 0755); - char rmcmd[1100]; - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", td); + if (mkdir(td, 0755) != 0) { + fprintf(stderr, "modtype_leaf[%s]: mkdir %s failed\n", tag, td); + return 1; + } + snprintf(bin, sizeof bin, "%s/pos", td); + snprintf(sepwork, sizeof sepwork, "%s/pos.sepwork", td); char cmd[2048]; /* cd into the fixture dir so the driver's source-dir-first import - * search resolves `use mod1; use mod2;`; -o moves the binary and its - * .sepwork OUT of the tracked fixture tree into the /tmp tmpdir. */ + * search resolves `use mod1; use mod2;`; -o puts the binary and the + * caller-owned .sepwork under the directory acquired above. */ snprintf(cmd, sizeof cmd, "cd %s && %s build -o %s/pos pos.ww >/dev/null 2>&1", fixdir, driver, td); + int fail = 0; if (runwait(cmd) != 0) { fprintf(stderr, "modtype_leaf[%s]: pos.ww build failed\n", tag); - runwait(rmcmd); - return 1; + fail = 1; + } else { + int got = runwait(bin); + /* 3 + 5 (mod1.stream) + 7 + 11 (mod2.stream) = 26. */ + if (got != 26) { + fprintf(stderr, + "modtype_leaf[%s]: pos.ww exit=%d want=26\n", tag, got); + fail = 1; + } } - char bin[2048]; - snprintf(bin, sizeof bin, "%s/pos", td); - int got = runwait(bin); - runwait(rmcmd); - /* 3 + 5 (mod1.stream) + 7 + 11 (mod2.stream) = 26. */ - if (got != 26) { - fprintf(stderr, - "modtype_leaf[%s]: pos.ww exit=%d want=26\n", tag, got); - return 1; + + snprintf(cmd, sizeof cmd, "rm -rf %s", sepwork); + if (runwait(cmd) != 0) { + fprintf(stderr, "modtype_leaf[%s]: cleanup %s failed\n", + tag, sepwork); + fail = 1; } - return 0; + if (unlink(bin) != 0 && errno != ENOENT) { + fprintf(stderr, "modtype_leaf[%s]: cleanup %s failed\n", tag, bin); + fail = 1; + } + if (rmdir(td) != 0) { + fprintf(stderr, "modtype_leaf[%s]: cleanup %s failed\n", tag, td); + fail = 1; + } + return fail; } static int run_neg(const char *driver, const char *fixdir, const char *tag) { - char td[1024]; + char td[1024], bin[1024], sepwork[1100]; snprintf(td, sizeof td, "/tmp/modcol_neg_%d_%s", getpid(), tag); - mkdir(td, 0755); - char rmcmd[1100]; - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", td); + if (mkdir(td, 0755) != 0) { + fprintf(stderr, "modtype_leaf[%s]: mkdir %s failed\n", tag, td); + return 1; + } + snprintf(bin, sizeof bin, "%s/neg", td); + snprintf(sepwork, sizeof sepwork, "%s/neg.sepwork", td); char cmd[2048]; /* Build must fail with a field-resolution error: the `c` field * lives on mod2.stream, not mod1.stream. If pre-fix sym-binding * smashed mod1.stream's identity into mod2.stream's slot, this - * would silently succeed. -o moves the binary and its .sepwork (the - * scratch dir is created even on the expected build failure) OUT of - * the tracked fixture tree into the /tmp tmpdir. */ + * would silently succeed. -o puts the binary and its caller-owned + * .sepwork (created even on the expected build failure) under the + * directory acquired above. */ snprintf(cmd, sizeof cmd, "cd %s && %s build -o %s/neg neg.ww >/dev/null 2>&1", fixdir, driver, td); int rc = runwait(cmd); - runwait(rmcmd); + int fail = 0; if (rc == 0) { fprintf(stderr, "modtype_leaf[%s]: neg.ww built but should have errored\n", tag); - return 1; + fail = 1; } - return 0; + + snprintf(cmd, sizeof cmd, "rm -rf %s", sepwork); + if (runwait(cmd) != 0) { + fprintf(stderr, "modtype_leaf[%s]: cleanup %s failed\n", + tag, sepwork); + fail = 1; + } + if (unlink(bin) != 0 && errno != ENOENT) { + fprintf(stderr, "modtype_leaf[%s]: cleanup %s failed\n", tag, bin); + fail = 1; + } + if (rmdir(td) != 0) { + fprintf(stderr, "modtype_leaf[%s]: cleanup %s failed\n", tag, td); + fail = 1; + } + return fail; } int diff --git a/test/wcc/697_samemod_prefer.c b/test/wcc/697_samemod_prefer.c index 2494a268..ee8737e6 100644 --- a/test/wcc/697_samemod_prefer.c +++ b/test/wcc/697_samemod_prefer.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -40,29 +41,46 @@ runwait(const char *cmd) static int run_pos(const char *driver, const char *fixdir, const char *tag) { - /* -o into a /tmp tmpdir so the binary AND `.sepwork/` land - * outside the repo; the cd keeps source-dir-first import search + /* -o puts the binary and caller-owned `.sepwork/` under the + * acquired directory; the cd keeps source-dir-first import search * resolving `use mod1; use mod2;`. */ - char td[1024]; + char td[1024], outbin[1024], sepwork[1100]; snprintf(td, sizeof td, "/tmp/smprefer_%d_%s", getpid(), tag); - mkdir(td, 0755); - - char rmcmd[1152]; - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", td); + if (mkdir(td, 0755) != 0) { + fprintf(stderr, "samemod_prefer[%s]: mkdir %s failed\n", tag, td); + return 1; + } + snprintf(outbin, sizeof outbin, "%s/pos", td); + snprintf(sepwork, sizeof sepwork, "%s/pos.sepwork", td); char cmd[2048]; snprintf(cmd, sizeof cmd, "cd %s && %s build -o %s/pos pos.ww >/dev/null 2>&1", fixdir, driver, td); + int fail = 0; if (runwait(cmd) != 0) { fprintf(stderr, "samemod_prefer[%s]: pos.ww build failed — bare-leaf " "`read` lookup likely cross-bound\n", tag); - runwait(rmcmd); - return 1; + fail = 1; } - runwait(rmcmd); - return 0; + + snprintf(cmd, sizeof cmd, "rm -rf %s", sepwork); + if (runwait(cmd) != 0) { + fprintf(stderr, "samemod_prefer[%s]: cleanup %s failed\n", + tag, sepwork); + fail = 1; + } + if (unlink(outbin) != 0 && errno != ENOENT) { + fprintf(stderr, "samemod_prefer[%s]: cleanup %s failed\n", + tag, outbin); + fail = 1; + } + if (rmdir(td) != 0) { + fprintf(stderr, "samemod_prefer[%s]: cleanup %s failed\n", tag, td); + fail = 1; + } + return fail; } int diff --git a/test/wcc/698_cgreturn_struct.c b/test/wcc/698_cgreturn_struct.c index 3166dc22..c235ea58 100644 --- a/test/wcc/698_cgreturn_struct.c +++ b/test/wcc/698_cgreturn_struct.c @@ -37,6 +37,7 @@ */ #include #include +#include #include #include #include @@ -293,29 +294,60 @@ static const struct row rows[] = { static int run_driver(const char *driver, const struct row *r, int i) { - char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024]; + char tmpdir[64], src[128], outbin[128], sepwork[160], cmd[1024]; snprintf(tmpdir, sizeof tmpdir, "/tmp/wcrs_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); + if (mkdir(tmpdir, 0755) != 0) { + fprintf(stderr, "row[%s]: mkdir %s failed\n", r->label, tmpdir); + return -1; + } snprintf(src, sizeof src, "%s/wcrs_%d_%d.ww", tmpdir, getpid(), i); snprintf(outbin, sizeof outbin, "%s/wcrs_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + snprintf(sepwork, sizeof sepwork, "%s.sepwork", outbin); + int got = -1; FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return -1; } + if (!f) goto cleanup; wwtest_fputs(r->src, f); - fclose(f); + int writebad = ferror(f); + if (fclose(f) != 0) writebad = 1; + if (writebad) goto cleanup; snprintf(cmd, sizeof cmd, "%s build -o %s %s", driver, outbin, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: build via %s failed\n", r->label, driver); - runwait(rmcmd); - return -1; + goto cleanup; } - int got = runwait(outbin); + got = runwait(outbin); - runwait(rmcmd); + /* Ordinary build leaves `.sepwork` to its caller. This exact + * tree is beneath tmpdir, whose mkdir above established ownership. */ +cleanup: + { + int cleanup_fail = 0; + snprintf(cmd, sizeof cmd, "rm -rf %s", sepwork); + if (runwait(cmd) != 0) { + fprintf(stderr, "row[%s]: cleanup %s failed\n", + r->label, sepwork); + cleanup_fail = 1; + } + if (unlink(src) != 0 && errno != ENOENT) { + fprintf(stderr, "row[%s]: cleanup %s failed\n", r->label, src); + cleanup_fail = 1; + } + if (unlink(outbin) != 0 && errno != ENOENT) { + fprintf(stderr, "row[%s]: cleanup %s failed\n", + r->label, outbin); + cleanup_fail = 1; + } + if (rmdir(tmpdir) != 0) { + fprintf(stderr, "row[%s]: cleanup %s failed\n", + r->label, tmpdir); + cleanup_fail = 1; + } + if (cleanup_fail && got == r->want) got = -1; + } return got; } @@ -326,33 +358,39 @@ run_driver(const char *driver, const struct row *r, int i) static int asm_byte_identical(const char *bin, const struct row *r, int i) { - char src[64], cs[64], ws[64], cmd[1024]; - snprintf(src, sizeof src, "/tmp/wcrs_asm_%d_%d.ww", getpid(), i); - snprintf(cs, sizeof cs, "/tmp/wcrs_asm_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/wcrs_asm_%d_%d_w.s", getpid(), i); + char tmpdir[64], src[128], cs[128], ws[128], cmd[1024]; + snprintf(tmpdir, sizeof tmpdir, "/tmp/wcrs_asm_%d_%d", getpid(), i); + if (mkdir(tmpdir, 0755) != 0) { + fprintf(stderr, "row[%s]: mkdir %s failed\n", r->label, tmpdir); + return -1; + } + snprintf(src, sizeof src, "%s/input.ww", tmpdir); + snprintf(cs, sizeof cs, "%s/cstage.s", tmpdir); + snprintf(ws, sizeof ws, "%s/wwstage.s", tmpdir); + int rc = -1; FILE *f = fopen(src, "wb"); - if (!f) return -1; + if (!f) goto cleanup; wwtest_fputs(r->src, f); - fclose(f); + int writebad = ferror(f); + if (fclose(f) != 0) writebad = 1; + if (writebad) goto cleanup; snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c errored\n", r->label); - unlink(src); - return -1; + goto cleanup; } snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null", bin, ws, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label); - unlink(src); unlink(cs); - return -1; + goto cleanup; } FILE *fc = fopen(cs, "rb"); FILE *fw = fopen(ws, "rb"); - int rc = 0; + rc = 0; if (!fc || !fw) { rc = -1; } else { @@ -368,7 +406,29 @@ asm_byte_identical(const char *bin, const struct row *r, int i) if (rc != 0) fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n", r->label); - unlink(src); unlink(cs); unlink(ws); + +cleanup: + { + int cleanup_fail = 0; + if (unlink(src) != 0 && errno != ENOENT) { + fprintf(stderr, "row[%s]: cleanup %s failed\n", r->label, src); + cleanup_fail = 1; + } + if (unlink(cs) != 0 && errno != ENOENT) { + fprintf(stderr, "row[%s]: cleanup %s failed\n", r->label, cs); + cleanup_fail = 1; + } + if (unlink(ws) != 0 && errno != ENOENT) { + fprintf(stderr, "row[%s]: cleanup %s failed\n", r->label, ws); + cleanup_fail = 1; + } + if (rmdir(tmpdir) != 0) { + fprintf(stderr, "row[%s]: cleanup %s failed\n", + r->label, tmpdir); + cleanup_fail = 1; + } + if (cleanup_fail && rc == 0) rc = -1; + } return rc; } diff --git a/test/wcc/699_use_promote_alias.c b/test/wcc/699_use_promote_alias.c index c3a55684..9e11a3ee 100644 --- a/test/wcc/699_use_promote_alias.c +++ b/test/wcc/699_use_promote_alias.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -53,20 +54,24 @@ runwait(const char *cmd) static int run_row(const char *driver, const char *fixdir, const char *tag) { - /* Scratch dir OUT of the repo: the driver's sep `.sepwork/` - * follows the build output stem, so point -o into /tmp so both the - * binary and its .sepwork land there and die with rm -rf. */ + /* Keep the output and its caller-owned `.sepwork/` in one + * invocation-owned directory outside the repository. */ char td[64]; snprintf(td, sizeof td, "/tmp/usepr_%d_%s", getpid(), tag); - mkdir(td, 0755); - char rmcmd[128]; - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", td); + if (mkdir(td, 0755) != 0) { + fprintf(stderr, "use_promote_alias[%s]: mkdir %s: %s\n", + tag, td, strerror(errno)); + return 1; + } char src[64]; snprintf(src, sizeof src, "pos_%s.ww", tag); - char bin[2048]; + char bin[2048], sepdir[2048], rmcmd[2112]; snprintf(bin, sizeof bin, "%s/out", td); + snprintf(sepdir, sizeof sepdir, "%s.sepwork", bin); + snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepdir); char cmd[2048]; + int failed = 0, got; /* cd into the fixture dir so the driver's source-dir-first * import search resolves `use mod;`; -o points OUT of the repo. */ snprintf(cmd, sizeof cmd, @@ -75,17 +80,37 @@ run_row(const char *driver, const char *fixdir, const char *tag) fprintf(stderr, "use_promote_alias[%s]: build failed — SK_USE→SK_X " "promotion likely dropped use_alias\n", tag); - runwait(rmcmd); - return 1; + failed = 1; + goto cleanup; } - int got = runwait(bin); - runwait(rmcmd); + got = runwait(bin); if (got != 42) { fprintf(stderr, "use_promote_alias[%s]: exit=%d want=42\n", tag, got); - return 1; + failed = 1; } - return 0; + +cleanup: + { + int cleanup_failed = 0; + if (runwait(rmcmd) != 0) { + fprintf(stderr, "use_promote_alias[%s]: remove %s failed\n", + tag, sepdir); + cleanup_failed = 1; + } + if (unlink(bin) != 0 && errno != ENOENT) { + fprintf(stderr, "use_promote_alias[%s]: unlink %s: %s\n", + tag, bin, strerror(errno)); + cleanup_failed = 1; + } + if (rmdir(td) != 0) { + fprintf(stderr, "use_promote_alias[%s]: rmdir %s: %s\n", + tag, td, strerror(errno)); + cleanup_failed = 1; + } + if (!failed && cleanup_failed) failed = 1; + } + return failed; } int diff --git a/test/wcc/700_e2e.c b/test/wcc/700_e2e.c index 4386ea21..938eff91 100644 --- a/test/wcc/700_e2e.c +++ b/test/wcc/700_e2e.c @@ -5,6 +5,8 @@ */ #include #include +#include +#include #include #include #include @@ -1802,32 +1804,82 @@ main(void) for (int i = 0; rows[i].src; i++, n++) { char tmpdir[64]; snprintf(tmpdir, sizeof tmpdir, "/tmp/wwe2e_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); + if (mkdir(tmpdir, 0755) != 0) { + fprintf(stderr, "row %d: mkdir %s: %s\n", + i, tmpdir, strerror(errno)); + fail++; + continue; + } - /* #8: source + binary + .sepwork scratch all live INSIDE tmpdir - * (`-o` pins the binary, scratch lands next to the in-tmpdir - * source) so the recursive cleanup removes everything. */ - char src[128], outbin[128], rmcmd[160]; + /* Source, output, and the caller-owned output `.sepwork` all live + * inside this invocation-owned directory. */ + char src[128], outbin[128], sepdir[160], rmcmd[192]; snprintf(src, sizeof src, "%s/wwe2e_%d_%d.ww", tmpdir, getpid(), i); snprintf(outbin, sizeof outbin, "%s/wwe2e_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + snprintf(sepdir, sizeof sepdir, "%s.sepwork", outbin); + snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepdir); + int row_failed = 0; + char cmd[1024]; + int got; FILE *f = fopen(src, "wb"); + if (!f) { + fprintf(stderr, "row %d: fopen %s: %s\n", + i, src, strerror(errno)); + row_failed = 1; + goto cleanup; + } wwtest_fputs(rows[i].src, f); - fclose(f); + if (ferror(f)) { + fprintf(stderr, "row %d: write %s failed\n", i, src); + row_failed = 1; + } + if (fclose(f) != 0) { + fprintf(stderr, "row %d: close %s: %s\n", + i, src, strerror(errno)); + row_failed = 1; + } + if (row_failed) goto cleanup; - char cmd[1024]; snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s", bin, outbin, src); - if (runwait(cmd) != 0) { runwait(rmcmd); fail++; continue; } + if (runwait(cmd) != 0) { + row_failed = 1; + goto cleanup; + } - int got = runwait(outbin); + got = runwait(outbin); if (got != rows[i].want_exit) { fprintf(stderr, "row %d: exit %d, want %d\n src: %s\n", i, got, rows[i].want_exit, rows[i].src); - fail++; + row_failed = 1; } - runwait(rmcmd); + +cleanup: + { + int cleanup_failed = 0; + if (runwait(rmcmd) != 0) { + fprintf(stderr, "row %d: remove %s failed\n", i, sepdir); + cleanup_failed = 1; + } + if (unlink(src) != 0 && errno != ENOENT) { + fprintf(stderr, "row %d: unlink %s: %s\n", + i, src, strerror(errno)); + cleanup_failed = 1; + } + if (unlink(outbin) != 0 && errno != ENOENT) { + fprintf(stderr, "row %d: unlink %s: %s\n", + i, outbin, strerror(errno)); + cleanup_failed = 1; + } + if (rmdir(tmpdir) != 0) { + fprintf(stderr, "row %d: rmdir %s: %s\n", + i, tmpdir, strerror(errno)); + cleanup_failed = 1; + } + if (!row_failed && cleanup_failed) row_failed = 1; + } + if (row_failed) fail++; } if (fail) { fprintf(stderr, "%d/%d e2e tests failed\n", fail, n); return 1; } printf("e2e: %d/%d ok\n", n, n); diff --git a/test/wcc/706_fnlabel_mangle.c b/test/wcc/706_fnlabel_mangle.c index 77c37432..ae63b06f 100644 --- a/test/wcc/706_fnlabel_mangle.c +++ b/test/wcc/706_fnlabel_mangle.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -61,37 +62,61 @@ runwait(const char *cmd) static int run_pos(const char *driver, const char *fixdir, const char *tag) { - /* -o into a /tmp tmpdir so pos.sepwork follows the OUTPUT stem out - * of the tracked fixture tree; cd stays — pos.ww imports - * siblings mod1/mod2 via source-dir-first resolution. */ + /* Keep the output and caller-owned pos.sepwork outside the tracked + * fixture tree; cd stays so pos.ww imports siblings mod1/mod2. */ char td[1024]; snprintf(td, sizeof td, "/tmp/fnlbl_%d_%s", getpid(), tag); - mkdir(td, 0755); - char rmcmd[1088]; - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", td); + if (mkdir(td, 0755) != 0) { + fprintf(stderr, "fnlabel_mangle[%s]: mkdir %s: %s\n", + tag, td, strerror(errno)); + return 1; + } + char bin[2048], sepdir[2048], rmcmd[2112]; + snprintf(bin, sizeof bin, "%s/pos", td); + snprintf(sepdir, sizeof sepdir, "%s.sepwork", bin); + snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepdir); char cmd[2048]; + int failed = 0, got; snprintf(cmd, sizeof cmd, "cd %s && %s build -o %s/pos pos.ww >/dev/null 2>&1", fixdir, driver, td); if (runwait(cmd) != 0) { fprintf(stderr, "fnlabel_mangle[%s]: pos.ww build failed\n", tag); - runwait(rmcmd); - return 1; + failed = 1; + goto cleanup; } - char bin[2048]; - snprintf(bin, sizeof bin, "%s/pos", td); - int got = runwait(bin); - runwait(rmcmd); + got = runwait(bin); if (got != 112) { fprintf(stderr, "fnlabel_mangle[%s]: pos.ww exit=%d want=112 — " "fn labels likely collapsed at link, or LEAQ-of-fn " "N_DOT branch missing in cgdot\n", tag, got); - return 1; + failed = 1; } - return 0; + +cleanup: + { + int cleanup_failed = 0; + if (runwait(rmcmd) != 0) { + fprintf(stderr, "fnlabel_mangle[%s]: remove %s failed\n", + tag, sepdir); + cleanup_failed = 1; + } + if (unlink(bin) != 0 && errno != ENOENT) { + fprintf(stderr, "fnlabel_mangle[%s]: unlink %s: %s\n", + tag, bin, strerror(errno)); + cleanup_failed = 1; + } + if (rmdir(td) != 0) { + fprintf(stderr, "fnlabel_mangle[%s]: rmdir %s: %s\n", + tag, td, strerror(errno)); + cleanup_failed = 1; + } + if (!failed && cleanup_failed) failed = 1; + } + return failed; } int diff --git a/test/wcc/708_param_shadow_mod.c b/test/wcc/708_param_shadow_mod.c index 4d56025b..416bf73f 100644 --- a/test/wcc/708_param_shadow_mod.c +++ b/test/wcc/708_param_shadow_mod.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -69,25 +70,51 @@ run_neg(const char *driver, const char *fixdir, const char *tag) snprintf(src, sizeof src, "neg_%s.ww", tag); char td[128]; snprintf(td, sizeof td, "/tmp/psm_neg_%d_%s", getpid(), tag); - mkdir(td, 0755); + if (mkdir(td, 0755) != 0) { + fprintf(stderr, "param_shadow_mod[neg_%s]: mkdir %s: %s\n", + tag, td, strerror(errno)); + return 1; + } + char out[160], sepdir[192], rmcmd[224]; + snprintf(out, sizeof out, "%s/out", td); + snprintf(sepdir, sizeof sepdir, "%s.sepwork", out); + snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepdir); char cmd[2048]; /* cd into the fixture dir so the driver's source-dir-first * import search resolves `use shadowmod;`; -o moves the binary - * and its .sepwork OUT of the tracked fixture tree. */ + * and caller-owned out.sepwork out of the tracked fixture tree. */ snprintf(cmd, sizeof cmd, - "cd %s && %s build -o %s/out %s >/dev/null 2>&1", - fixdir, driver, td, src); + "cd %s && %s build -o %s %s >/dev/null 2>&1", + fixdir, driver, out, src); int rc = runwait(cmd); - char rmcmd[256]; - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", td); - runwait(rmcmd); + int failed = 0; if (rc == 0) { fprintf(stderr, "param_shadow_mod[neg_%s]: build unexpectedly succeeded " "— shadow rule did not fire\n", tag); - return 1; + failed = 1; } - return 0; + + { + int cleanup_failed = 0; + if (runwait(rmcmd) != 0) { + fprintf(stderr, "param_shadow_mod[neg_%s]: remove %s failed\n", + tag, sepdir); + cleanup_failed = 1; + } + if (unlink(out) != 0 && errno != ENOENT) { + fprintf(stderr, "param_shadow_mod[neg_%s]: unlink %s: %s\n", + tag, out, strerror(errno)); + cleanup_failed = 1; + } + if (rmdir(td) != 0) { + fprintf(stderr, "param_shadow_mod[neg_%s]: rmdir %s: %s\n", + tag, td, strerror(errno)); + cleanup_failed = 1; + } + if (!failed && cleanup_failed) failed = 1; + } + return failed; } static int @@ -95,32 +122,57 @@ run_pos(const char *driver, const char *fixdir) { char td[128]; snprintf(td, sizeof td, "/tmp/psm_pos_%d", getpid()); - mkdir(td, 0755); - char rmcmd[256]; - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", td); + if (mkdir(td, 0755) != 0) { + fprintf(stderr, "param_shadow_mod[pos_rename]: mkdir %s: %s\n", + td, strerror(errno)); + return 1; + } + char out[160], sepdir[192], rmcmd[224]; + snprintf(out, sizeof out, "%s/out", td); + snprintf(sepdir, sizeof sepdir, "%s.sepwork", out); + snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepdir); char cmd[2048]; - /* keep cd for sibling-import resolution; -o moves the binary and - * its pos_rename.sepwork OUT of the tracked fixture tree. */ + /* Keep cd for sibling-import resolution; -o moves the binary and + * caller-owned out.sepwork out of the tracked fixture tree. */ snprintf(cmd, sizeof cmd, - "cd %s && %s build -o %s/out pos_rename.ww >/dev/null 2>&1", - fixdir, driver, td); + "cd %s && %s build -o %s pos_rename.ww >/dev/null 2>&1", + fixdir, driver, out); + int failed = 0, got; if (runwait(cmd) != 0) { - runwait(rmcmd); fprintf(stderr, "param_shadow_mod[pos_rename]: build failed — rule " "over-triggered on the rename\n"); - return 1; + failed = 1; + goto cleanup; } - char bin[2048]; - snprintf(bin, sizeof bin, "%s/out", td); - int got = runwait(bin); - runwait(rmcmd); + got = runwait(out); if (got != 42) { fprintf(stderr, "param_shadow_mod[pos_rename]: exit=%d want=42\n", got); - return 1; + failed = 1; } - return 0; + +cleanup: + { + int cleanup_failed = 0; + if (runwait(rmcmd) != 0) { + fprintf(stderr, "param_shadow_mod[pos_rename]: remove %s failed\n", + sepdir); + cleanup_failed = 1; + } + if (unlink(out) != 0 && errno != ENOENT) { + fprintf(stderr, "param_shadow_mod[pos_rename]: unlink %s: %s\n", + out, strerror(errno)); + cleanup_failed = 1; + } + if (rmdir(td) != 0) { + fprintf(stderr, "param_shadow_mod[pos_rename]: rmdir %s: %s\n", + td, strerror(errno)); + cleanup_failed = 1; + } + if (!failed && cleanup_failed) failed = 1; + } + return failed; } /* @@ -134,34 +186,61 @@ run_pos(const char *driver, const char *fixdir) static int run_neg_selfimp(const char *comp, const char *fixdir, const char *tag) { - char errp[96], cmd[2048]; - snprintf(errp, sizeof errp, "/tmp/psm_selfimp_%s_%d.err", tag, getpid()); + char td[128]; + snprintf(td, sizeof td, "/tmp/psm_selfimp_%d_%s", getpid(), tag); + if (mkdir(td, 0755) != 0) { + fprintf(stderr, "param_shadow_mod[neg_selfimp-%s]: mkdir %s: %s\n", + tag, td, strerror(errno)); + return 1; + } + char errp[160], cmd[2048]; + snprintf(errp, sizeof errp, "%s/stderr", td); snprintf(cmd, sizeof cmd, "cd %s && %s selfimp/selfimptest.ww -o /dev/null 2>%s", fixdir, comp, errp); int rc = runwait(cmd); + int failed = 0, found = 0; + FILE *f; if (rc == 0) { fprintf(stderr, "param_shadow_mod[neg_selfimp-%s]: accepted " "self-import (expected reject)\n", tag); - unlink(errp); - return 1; + failed = 1; + goto cleanup; } - FILE *f = fopen(errp, "rb"); - int found = 0; + f = fopen(errp, "rb"); if (f) { char buf[4096]; size_t n = fread(buf, 1, sizeof buf - 1, f); - fclose(f); - buf[n] = '\0'; - found = strstr(buf, "self-import") != NULL; + if (!ferror(f)) { + buf[n] = '\0'; + found = strstr(buf, "self-import") != NULL; + } + if (fclose(f) != 0) found = 0; } - unlink(errp); if (!found) { fprintf(stderr, "param_shadow_mod[neg_selfimp-%s]: rejected " "but no 'self-import' on stderr\n", tag); - return 1; + failed = 1; } - return 0; + +cleanup: + { + int cleanup_failed = 0; + if (unlink(errp) != 0 && errno != ENOENT) { + fprintf(stderr, + "param_shadow_mod[neg_selfimp-%s]: unlink %s: %s\n", + tag, errp, strerror(errno)); + cleanup_failed = 1; + } + if (rmdir(td) != 0) { + fprintf(stderr, + "param_shadow_mod[neg_selfimp-%s]: rmdir %s: %s\n", + tag, td, strerror(errno)); + cleanup_failed = 1; + } + if (!failed && cleanup_failed) failed = 1; + } + return failed; } /* @@ -177,32 +256,57 @@ run_pos_crossmod(const char *driver, const char *fixdir) { char td[128]; snprintf(td, sizeof td, "/tmp/psm_crossmod_%d", getpid()); - mkdir(td, 0755); - char rmcmd[256]; - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", td); + if (mkdir(td, 0755) != 0) { + fprintf(stderr, "param_shadow_mod[pos_crossmod]: mkdir %s: %s\n", + td, strerror(errno)); + return 1; + } + char out[160], sepdir[192], rmcmd[224]; + snprintf(out, sizeof out, "%s/out", td); + snprintf(sepdir, sizeof sepdir, "%s.sepwork", out); + snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepdir); char cmd[2048]; - /* keep cd for sibling-import resolution; -o moves the binary and - * its pos_crossmod.sepwork OUT of the tracked fixture tree. */ + /* Keep cd for sibling-import resolution; -o moves the binary and + * caller-owned out.sepwork out of the tracked fixture tree. */ snprintf(cmd, sizeof cmd, - "cd %s && %s build -o %s/out pos_crossmod.ww >/dev/null 2>&1", - fixdir, driver, td); + "cd %s && %s build -o %s pos_crossmod.ww >/dev/null 2>&1", + fixdir, driver, out); + int failed = 0, got; if (runwait(cmd) != 0) { - runwait(rmcmd); fprintf(stderr, "param_shadow_mod[pos_crossmod]: build failed — shadow " "rule over-triggered on a cross-module param\n"); - return 1; + failed = 1; + goto cleanup; } - char bin[2048]; - snprintf(bin, sizeof bin, "%s/out", td); - int got = runwait(bin); - runwait(rmcmd); + got = runwait(out); if (got != 2) { fprintf(stderr, "param_shadow_mod[pos_crossmod]: exit=%d want=2\n", got); - return 1; + failed = 1; } - return 0; + +cleanup: + { + int cleanup_failed = 0; + if (runwait(rmcmd) != 0) { + fprintf(stderr, "param_shadow_mod[pos_crossmod]: remove %s failed\n", + sepdir); + cleanup_failed = 1; + } + if (unlink(out) != 0 && errno != ENOENT) { + fprintf(stderr, "param_shadow_mod[pos_crossmod]: unlink %s: %s\n", + out, strerror(errno)); + cleanup_failed = 1; + } + if (rmdir(td) != 0) { + fprintf(stderr, "param_shadow_mod[pos_crossmod]: rmdir %s: %s\n", + td, strerror(errno)); + cleanup_failed = 1; + } + if (!failed && cleanup_failed) failed = 1; + } + return failed; } int diff --git a/test/wcc/712_redecl.c b/test/wcc/712_redecl.c index f2989de0..685c1c6e 100644 --- a/test/wcc/712_redecl.c +++ b/test/wcc/712_redecl.c @@ -41,15 +41,15 @@ * neg_forrange_tuple_dup | build fails | site 1505 * neg_param_dup | build fails | site 1914 * neg_toplet_dup | build fails | site 1880 - * pos_nested_block_shadow | exit=1 | scope boundary - * pos_nested_shadow_typed | exit=2 | (name,type) — bucket - * | | keys by name only - * pos_forrange_body_shadow | exit=99 | for body N_BLOCK - * | | opens a fresh scope - * pos_mcase_per_arm | exit=7 | match arm scope + * The four legal-shadow positive rows are owned by the directive fixtures + * under test/wcc/data/r71_redecl_*. This residual wrapper intentionally owns + * only the six C-stage-only rejects below: wwstage lacks per-block scoping, + * so these cannot be represented by the symmetric //ww:error contract. */ #include #include +#include +#include #include #include #include @@ -64,167 +64,128 @@ runwait(const char *cmd) return -1; } -/* - * kind == 0: negative — build must fail (any nonzero exit). - * kind == 1: positive — build must succeed AND binary exits with `want`. - */ -struct row { const char *label; int kind; const char *src; int want; }; +struct row { const char *label; const char *src; }; static const struct row rows[] = { /* neg: site 1443 — block-body let dup. */ - { "neg_let_same_block", 0, + { "neg_let_same_block", "fn main() i32 = {\n" " let a: i32 = 1;\n" " let a: i32 = 2;\n" " return a;\n" - "};\n", - 0 }, + "};\n" }, /* neg: site 1562 — mlet shadows earlier same-block let. */ - { "neg_mlet_same_block", 0, + { "neg_mlet_same_block", "fn pair() (i32, i32) = { return (10, 20); };\n" "fn main() i32 = {\n" " let a: i32 = 1;\n" " let (a, b) = pair();\n" " return a + b;\n" - "};\n", - 0 }, + "};\n" }, /* neg: site 1562 — mlet pattern lists the same name twice. */ - { "neg_mlet_tuple_dup", 0, + { "neg_mlet_tuple_dup", "fn pair() (i32, i32) = { return (10, 20); };\n" "fn main() i32 = {\n" " let (a, a) = pair();\n" " return a;\n" - "};\n", - 0 }, + "};\n" }, /* neg: site 1505 — forrange tuple-pattern lists same name twice. */ - { "neg_forrange_tuple_dup", 0, + { "neg_forrange_tuple_dup", "fn main() i32 = {\n" " let xs: [1](i32, i32) = [(10i32, 20i32)];\n" " for (let (a, a) .. xs) {\n" " return a;\n" " };\n" " return -1;\n" - "};\n", - 0 }, + "};\n" }, /* neg: site 1914 — two params with the same name. */ - { "neg_param_dup", 0, + { "neg_param_dup", "fn f(a: i32, a: i32) i32 = { return a; };\n" - "fn main() i32 = { return f(1, 2); };\n", - 0 }, + "fn main() i32 = { return f(1, 2); };\n" }, /* neg: site 1880 — top-level let dup. */ - { "neg_toplet_dup", 0, + { "neg_toplet_dup", "let x: i32 = 1;\n" "let x: i32 = 2;\n" - "fn main() i32 = { return x; };\n", - 0 }, - - /* pos: nested-block shadow stays legal. Inner block opens a - * fresh scope, scope_define's per-scope hash bucket isolates the - * inner `a` from the outer one. Outer `a` untouched after the - * inner block exits. */ - { "pos_nested_block_shadow", 1, - "fn main() i32 = {\n" - " let a: i32 = 1;\n" - " {\n" - " let a: i32 = 99;\n" - " };\n" - " return a;\n" - "};\n", - 1 }, - - /* pos: nested-block shadow with a different type. Confirms the - * scope-bucket key is the name alone, not (name, type) — the - * inner `s: str` does NOT collide with the outer `s: i32`. */ - { "pos_nested_shadow_typed", 1, - "fn main() i32 = {\n" - " let s: i32 = 2;\n" - " {\n" - " let s: str = \"hi\";\n" - " let _ = s;\n" - " };\n" - " return s;\n" - "};\n", - 2 }, - - /* pos: for-loop body N_BLOCK opens a fresh scope, so a `let x` - * inside the body doesn't collide with the for-range's binding - * `x` (which lives in the per-loop scope, one level above). */ - { "pos_forrange_body_shadow", 1, - "fn main() i32 = {\n" - " let arr: [1]i32 = [42i32];\n" - " let r: i32 = 0;\n" - " for (let x .. arr) {\n" - " let x: i32 = 99;\n" - " r = x;\n" - " };\n" - " return r;\n" - "};\n", - 99 }, - - /* pos: each match arm is its own scope, so two arms each - * binding `v` don't collide. Pre-#32 this was already legal - * (newscope wrapper at check.c:1186); pinned here so a future - * scope-tightening can't quietly remove that wrapper. */ - { "pos_mcase_per_arm", 1, - "type T = (i32 | f64);\n" - "fn main() i32 = {\n" - " let t: T = 7i32;\n" - " match (t) {\n" - " case let v: i32 => { return v; };\n" - " case let v: f64 => { return -1; };\n" - " };\n" - "};\n", - 7 }, + "fn main() i32 = { return x; };\n" }, }; static int run_row(const char *driver, const struct row *r, int i) { - char src[128], tmpdir[128], outbin[128], rmcmd[160], cmd[2048]; + char src[128], tmpdir[128], outbin[128], sepdir[160], rmcmd[192]; + char cmd[2048]; snprintf(tmpdir, sizeof tmpdir, "/tmp/wcredecl_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); + if (mkdir(tmpdir, 0755) != 0) { + fprintf(stderr, "redecl[%s]: mkdir %s: %s\n", + r->label, tmpdir, strerror(errno)); + return -1; + } snprintf(src, sizeof src, "%s/wcredecl_%d_%d.ww", tmpdir, getpid(), i); snprintf(outbin, sizeof outbin, "%s/wcredecl_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + snprintf(sepdir, sizeof sepdir, "%s.sepwork", outbin); + snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepdir); + int failed = 0, rc; FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return -1; } + if (!f) { + fprintf(stderr, "redecl[%s]: fopen %s: %s\n", + r->label, src, strerror(errno)); + failed = 1; + goto cleanup; + } wwtest_fputs(r->src, f); - fclose(f); + if (ferror(f)) { + fprintf(stderr, "redecl[%s]: write %s failed\n", r->label, src); + failed = 1; + } + if (fclose(f) != 0) { + fprintf(stderr, "redecl[%s]: close %s: %s\n", + r->label, src, strerror(errno)); + failed = 1; + } + if (failed) goto cleanup; snprintf(cmd, sizeof cmd, "%s build -o %s %s >/dev/null 2>&1", driver, outbin, src); - int rc = runwait(cmd); + rc = runwait(cmd); - if (r->kind == 0) { - /* Negative — build must fail. */ - if (rc == 0) - fprintf(stderr, - "redecl[%s]: build unexpectedly succeeded\n", - r->label); - runwait(rmcmd); - return rc == 0 ? -1 : 0; + if (rc == 0) { + fprintf(stderr, "redecl[%s]: build unexpectedly succeeded\n", + r->label); + failed = 1; } - /* Positive — build then run. */ - if (rc != 0) { - fprintf(stderr, "redecl[%s]: build failed\n", r->label); - runwait(rmcmd); - return -1; +cleanup: + { + int cleanup_failed = 0; + if (runwait(rmcmd) != 0) { + fprintf(stderr, "redecl[%s]: remove %s failed\n", + r->label, sepdir); + cleanup_failed = 1; + } + if (unlink(src) != 0 && errno != ENOENT) { + fprintf(stderr, "redecl[%s]: unlink %s: %s\n", + r->label, src, strerror(errno)); + cleanup_failed = 1; + } + if (unlink(outbin) != 0 && errno != ENOENT) { + fprintf(stderr, "redecl[%s]: unlink %s: %s\n", + r->label, outbin, strerror(errno)); + cleanup_failed = 1; + } + if (rmdir(tmpdir) != 0) { + fprintf(stderr, "redecl[%s]: rmdir %s: %s\n", + r->label, tmpdir, strerror(errno)); + cleanup_failed = 1; + } + if (!failed && cleanup_failed) failed = 1; } - int got = runwait(outbin); - runwait(rmcmd); - if (got != r->want) { - fprintf(stderr, "redecl[%s]: exit=%d want=%d\n", - r->label, got, r->want); - return -1; - } - return 0; + return failed ? -1 : 0; } int diff --git a/test/wcc/718_struct_multi_return_scratch.c b/test/wcc/718_struct_multi_return_scratch.c index 2d58cce8..c0142915 100644 --- a/test/wcc/718_struct_multi_return_scratch.c +++ b/test/wcc/718_struct_multi_return_scratch.c @@ -63,7 +63,6 @@ #include #include #include -#include #include #include "wwtestpkg.h" @@ -77,9 +76,9 @@ runwait(const char *cmd) } struct row { + /* Runtime ownership: test/wcc/data/r718_struct_return_*. */ const char *label; const char *src; - int want; int frame; /* expected `TEXT f,$N` — 0 = don't check */ int max_off; /* assert no `-K(BP)` with K > max_off — 0 = don't check */ int check_byte_id; /* 1 = assert cstage-vs-wwstage asm byte-identical */ @@ -112,7 +111,6 @@ static const struct row rows[] = { " if (q.nsec != 23i64) { return 4; };\n" " return 0;\n" "};\n", - 0, /* frame: i(16) + x(8) + r(16) + retscr(24) = 64 */ 64, 64, @@ -152,7 +150,6 @@ static const struct row rows[] = { " if (c.nsec != 20i64) { return 6; };\n" " return 0;\n" "};\n", - 0, /* frame: i(16) + k(8) + r(16) + retscr(24) = 64 */ 64, 64, @@ -181,7 +178,6 @@ static const struct row rows[] = { " if (r.nsec != 201i64) { return 2; };\n" " return 0;\n" "};\n", - 0, /* frame: i(16) + r(16) + retscr(24) = 56 -> aligned to 64 */ 64, 64, @@ -209,7 +205,6 @@ static const struct row rows[] = { " };\n" " return 0;\n" "};\n", - 0, /* Frame/max_off depend on the tagged ABI shape; pin byte-id * only, not specific values. The negative-offset bound here * still catches a stomp regression because byte-identity @@ -221,36 +216,6 @@ static const struct row rows[] = { 1 }, }; -static int -run_driver(const char *driver, const struct row *r, int i) -{ - char src[128], tmpdir[64], outbin[128], rmcmd[160], cmd[1024]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/wcsmr_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/wcsmr_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/wcsmr_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); - - FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return -1; } - wwtest_fputs(r->src, f); - fclose(f); - - snprintf(cmd, sizeof cmd, "%s build -o %s %s", - driver, outbin, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: build via %s failed\n", - r->label, driver); - runwait(rmcmd); - return -1; - } - - int got = runwait(outbin); - - runwait(rmcmd); - return got; -} - /* Scan asm output for the `TEXT f,$N` line; return N (or -1 if not * found). Tolerates an optional MODULE-mangled prefix. */ static int @@ -391,41 +356,12 @@ main(void) bin = absbin; } - char cdrv[1024]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); char wdrv[1024]; snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - struct { const char *name; const char *path; int gated_on_existence; } - drivers[] = { - { "cstage", cdrv, 0 }, - { "wwstage", wdrv, 1 }, - { NULL, NULL, 0 }, - }; - int n = (int)(sizeof rows / sizeof rows[0]); int total = 0, fail = 0; - for (int d = 0; drivers[d].name; d++) { - if (drivers[d].gated_on_existence - && access(drivers[d].path, X_OK) != 0) { - fprintf(stderr, "struct_multi_return_scratch: skip %s (no %s)\n", - drivers[d].name, drivers[d].path); - continue; - } - for (int i = 0; i < n; i++) { - int got = run_driver(drivers[d].path, &rows[i], i); - total++; - if (got != rows[i].want) { - fprintf(stderr, - "struct_multi_return_scratch[%s][%s]: exit=%d want=%d\n", - drivers[d].name, rows[i].label, - got, rows[i].want); - fail++; - } - } - } - if (access(wdrv, X_OK) == 0) { for (int i = 0; i < n; i++) { total++; diff --git a/test/wcc/732_def_const_fold.c b/test/wcc/732_def_const_fold.c index d2459ccd..a8b9cf35 100644 --- a/test/wcc/732_def_const_fold.c +++ b/test/wcc/732_def_const_fold.c @@ -42,7 +42,6 @@ #include #include #include -#include #include #include "wwtestpkg.h" @@ -57,7 +56,8 @@ runwait(const char *cmd) /* ---- 1. same-module EXEC rows (compile+link+run) -------------------- */ -struct row { const char *label; const char *src; int want; }; +/* Runtime ownership: test/wcc/data/r732_def_{sibling,chain,shift,widening}_*. */ +struct row { const char *label; const char *src; }; static const struct row exec_rows[] = { /* Bare sibling reference: `def B = A;`. Pre-#88 the rhs (N_IDENT) @@ -69,8 +69,7 @@ static const struct row exec_rows[] = { "\tlet x: i32 = B;\n" "\tif (x == 7) { return 42; };\n" "\treturn 1;\n" - "};\n", - 42 }, + "};\n" }, /* Sibling + arithmetic: the headline `def B = A + 1;`. */ { "sibling-add", @@ -80,8 +79,7 @@ static const struct row exec_rows[] = { "\tlet x: i32 = B;\n" "\tif (x == 6) { return 42; };\n" "\treturn 1;\n" - "};\n", - 42 }, + "};\n" }, /* def -> def -> def chain: C resolves through B through A. Pins * the recursive resolve (and that the depth guard doesn't trip on @@ -94,8 +92,7 @@ static const struct row exec_rows[] = { "\tlet x: i32 = C;\n" "\tif (x == 30) { return 42; };\n" "\treturn 1;\n" - "};\n", - 42 }, + "};\n" }, /* Pure arithmetic with a shift: `(1 << 7) - 1` == 127. Exercises * the fold_binop shift + subtract path off any sibling ref. */ @@ -105,8 +102,7 @@ static const struct row exec_rows[] = { "\tlet x: i32 = MASK;\n" "\tif (x == 127) { return 42; };\n" "\treturn 1;\n" - "};\n", - 42 }, + "};\n" }, /* Widening cast over a sibling ref: `A: i64` where A: i32 = 5. * The N_CAST is stripped (i32->i64 widens, value fits), so W folds @@ -118,42 +114,9 @@ static const struct row exec_rows[] = { "\tlet x: i64 = W;\n" "\tif (x == 5i64) { return 42; };\n" "\treturn 1;\n" - "};\n", - 42 }, + "};\n" }, }; -static int -run_driver(const char *driver, const struct row *r, int i) -{ - char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/dcf_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/dcf_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/dcf_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); - - FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return -1; } - wwtest_fputs(r->src, f); - fclose(f); - - /* timeout 180 per repo convention (990-997); test/run does not bound - * individual binaries, so an unguarded hang would stall make test. */ - snprintf(cmd, sizeof cmd, "timeout 180 %s build -o %s %s 2>/dev/null", - driver, outbin, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: build via %s failed\n", - r->label, driver); - runwait(rmcmd); - return -1; - } - - int got = runwait(outbin); - - runwait(rmcmd); - return got; -} - /* ---- shared .s emit + slurp ----------------------------------------- */ static int @@ -282,13 +245,8 @@ xmod_fold_present(const char *w6c, const char *stage) struct failrow { const char *label; const char *src; }; static const struct failrow fail_rows[] = { - /* Same-module cycle: the depth guard must trip and fail loud, not - * hang and not silently drop the row. */ - { "same-module-cycle", - "def A: i32 = B;\n" - "def B: i32 = A;\n" - "export fn main() i32 = { return A; };\n" }, - + /* Residual combined-source artifact: cannot use the ordinary + * single-package //ww:error carrier. */ /* Cross-module cycle (combined source): a.J -> b.K -> a.J. */ { "cross-module-cycle", "package a;\n" @@ -298,12 +256,6 @@ static const struct failrow fail_rows[] = { "import a;\n" "export def K: i32 = a.J;\n" "export fn main() i32 = { return 0; };\n" }, - - /* Narrowing cast that loses the value: 0x1FF (511) does not fit a - * u8, so the cast must fail loud rather than silently truncate. */ - { "narrowing-cast", - "def Z: u8 = 0x1FF: u8;\n" - "export fn main() i32 = { let z: u8 = Z; return z: i32; };\n" }, }; /* Compile via w6c only (no link/run); success means the build FAILED as @@ -355,42 +307,14 @@ main(void) bin = absbin; } - char cdrv[1024], wdrv[1024], w6c[1024], w6c_ww[1024]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); + char w6c[1024], w6c_ww[1024]; snprintf(w6c, sizeof w6c, "%s/w6c", bin); snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin); int have_ww = (access(w6c_ww, X_OK) == 0); int total = 0, fail = 0; - /* 1. same-module exec via cstage `ww` (+ wwstage `ww_ww` if built) */ - struct { const char *name; const char *path; int gated; } - drivers[] = { - { "cstage", cdrv, 0 }, - { "wwstage", wdrv, 1 }, - { NULL, NULL, 0 }, - }; int nexec = (int)(sizeof exec_rows / sizeof exec_rows[0]); - for (int d = 0; drivers[d].name; d++) { - if (drivers[d].gated && access(drivers[d].path, X_OK) != 0) { - fprintf(stderr, "def_const_fold: skip %s (no %s)\n", - drivers[d].name, drivers[d].path); - continue; - } - for (int i = 0; i < nexec; i++) { - int got = run_driver(drivers[d].path, &exec_rows[i], i); - total++; - if (got != exec_rows[i].want) { - fprintf(stderr, - "def_const_fold[%s][%s]: exit=%d want=%d\n", - drivers[d].name, exec_rows[i].label, - got, exec_rows[i].want); - fail++; - } - } - } - /* 2. byte-id on every exec row (only when wwstage is built) */ if (have_ww) { for (int i = 0; i < nexec; i++) { diff --git a/test/wcc/737_direnum.c b/test/wcc/737_direnum.c index e40fb6c3..cd5ddee0 100644 --- a/test/wcc/737_direnum.c +++ b/test/wcc/737_direnum.c @@ -1,14 +1,14 @@ /* - * 737_direnum — driver-level sentinel for task #22 directory-as- - * module enumeration. Two row families pinning the contract: + * 737_direnum — driver-level sentinel for directory-package loading. + * Existing imported-directory fixtures plus a private dynamic table pin: * * ok — multi-file dir is concatenated by both stages; the entry * reads cross-pkg bare-leaf fns from sibling files. Build * must succeed for both C-built `ww` and ww-built `ww_ww`. - * bad — multi-file dir with mismatched `package ;` decls - * triggers the strict-same-package error (task #25 subset - * bundled with #22 because the failure mode is dir-enum's - * own; cross-stage symmetric). + * bad — multi-file dirs with conflicting `package ;` clauses; + * root — direct multi-file directory builds, deterministic source order, + * missing/invalid clauses, and root conflicts; + * leaf — an imported directory with one consistent but wrong package name. * * Asm-presence isn't checked separately — 968_utf8_run, 966_strings_ * run, 995_self_rebuild already exercise dir-enum end-to-end at @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -43,6 +44,49 @@ stderr_contains(const char *path, const char *needle) return strstr(buf, needle) != NULL; } +static int +write_file(const char *path, const char *body) +{ + FILE *f = fopen(path, "wb"); + if (!f) return -1; + int failed = fputs(body, f) == EOF; + if (fclose(f) != 0) failed = 1; + return failed ? -1 : 0; +} + +static int +files_equal(const char *a, const char *b) +{ + FILE *fa = fopen(a, "rb"), *fb = fopen(b, "rb"); + if (!fa || !fb) { + if (fa) fclose(fa); + if (fb) fclose(fb); + return 0; + } + int ca, cb; + do { + ca = fgetc(fa); + cb = fgetc(fb); + } while (ca == cb && ca != EOF); + fclose(fa); + fclose(fb); + return ca == cb; +} + +static int +file_orders(const char *path, const char *first, const char *second) +{ + FILE *f = fopen(path, "rb"); + if (!f) return 0; + char buf[16384]; + size_t n = fread(buf, 1, sizeof buf - 1, f); + fclose(f); + buf[n] = '\0'; + char *a = strstr(buf, first); + char *b = strstr(buf, second); + return a != NULL && b != NULL && a < b; +} + int main(void) { @@ -61,11 +105,21 @@ main(void) snprintf(ww_ww, sizeof ww_ww, "%s/ww_ww", bin); int total = 0, fail = 0; - char errp[64]; + int have_owned = 0, have_rootok = 0, have_rootbad = 0; + int have_importbad = 0, have_wanted = 0; + int have_missing = 0, have_invalid = 0; + char owner[128]; + snprintf(owner, sizeof owner, "/tmp/direnum_owner_%d", getpid()); + if (mkdir(owner, 0755) != 0) { + fprintf(stderr, "737[setup]: cannot acquire temp directory: %s\n", + strerror(errno)); + return 1; + } + char errp[512]; - /* ok: cross-pkg bare-leaf via dir-enum, both stages. `ww run` routes - * its scratch through /tmp/ww_run_.sepwork and removes it itself - * (do_run keepscratch 0), so no per-invocation tmpdir is needed here. */ + /* ok: cross-pkg bare-leaf via dir-enum, both stages. `ww run` owns + * /tmp/ww_run_/{main,main.sepwork} and removes both itself, so no + * per-invocation build directory is needed here. */ { const char *src = "test/wcc/data/direnum/entry.ww"; char cmd[2048]; @@ -88,114 +142,365 @@ main(void) } } - /* bad: mismatched package decls in same dir → import-path mismatch - * error. E3-C1: the amalgamator's strict-same-package "differs from" - * check is deleted; under sep w6c rejects the composed unit with - * "package

does not match import path " (wwstage is terser per - * #68, so match the shared "does not match import path" substring). - * Both stages must surface it in stderr. */ + /* bad: the package loader validates the complete stored source list + * conflict-first, before checking the import-path leaf. Both stages must + * surface the same directory-level conflict. */ { const char *src = "test/wcc/data/direnum/bad_entry.ww"; - snprintf(errp, sizeof errp, "/tmp/direnum_%d_bad.err", getpid()); - /* CLASS B: build INPUT stays the in-repo fixture, but -o points the - * output stem (and thus its .sepwork scratch) at a unique /tmp - * tmpdir so nothing leaks into the tracked tree — even on the - * expected build failure, since scratch is mkdir'd before the reject. - * rm -rf the tmpdir on every exit path. */ - char td[64], rmcmd[128]; + /* Build INPUT stays in-repo. Output, diagnostic, and the retained + * exact .sepwork tree stay beneath this invocation's root. */ + char out[512]; char cmd[2048]; - snprintf(td, sizeof td, "/tmp/direnum_%d_bad_c", getpid()); - mkdir(td, 0755); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", td); - snprintf(cmd, sizeof cmd, "%s build -o %s/out %s 2>%s >/dev/null", - ww, td, src, errp); + snprintf(out, sizeof out, "%s/bad-c", owner); + snprintf(errp, sizeof errp, "%s/bad-c.err", owner); + snprintf(cmd, sizeof cmd, "%s build -o '%s' %s 2>'%s' >/dev/null", + ww, out, src, errp); int rc = runwait(cmd); - runwait(rmcmd); total++; if (rc == 0) { fprintf(stderr, "737[bad-cstage]: expected build failure, succeeded\n"); fail++; - } else if (!stderr_contains(errp, "does not match import path")) { - fprintf(stderr, "737[bad-cstage]: stderr missing 'does not match import path'\n"); + } else if (!stderr_contains(errp, "conflicting package names")) { + fprintf(stderr, "737[bad-cstage]: stderr missing 'conflicting package names'\n"); fail++; } - unlink(errp); if (access(ww_ww, X_OK) == 0) { - snprintf(td, sizeof td, "/tmp/direnum_%d_bad_w", getpid()); - mkdir(td, 0755); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", td); - snprintf(cmd, sizeof cmd, "%s build -o %s/out %s 2>%s >/dev/null", - ww_ww, td, src, errp); + snprintf(out, sizeof out, "%s/bad-w", owner); + snprintf(errp, sizeof errp, "%s/bad-w.err", owner); + snprintf(cmd, sizeof cmd, + "%s build -o '%s' %s 2>'%s' >/dev/null", + ww_ww, out, src, errp); rc = runwait(cmd); - runwait(rmcmd); total++; if (rc == 0) { fprintf(stderr, "737[bad-wwstage]: expected build failure, succeeded\n"); fail++; - } else if (!stderr_contains(errp, "does not match import path")) { - fprintf(stderr, "737[bad-wwstage]: stderr missing 'does not match import path'\n"); + } else if (!stderr_contains(errp, "conflicting package names")) { + fprintf(stderr, "737[bad-wwstage]: stderr missing 'conflicting package names'\n"); fail++; } - unlink(errp); } } - /* bad-deep: same mismatch, but one file's `package` decl sits behind - * a >2048-byte comment header. Pins the PREP-peek whole-file read - * (#16): the old 2048-capped peek missed that decl, so the strict- - * same-package check skipped the file and the mismatch went - * undetected (build wrongly succeeded); the whole-file peek finds it - * → "does not match import path". Both stages. (reviewer-batcha pin.) */ + /* bad-deep: same conflict, but one clause sits behind a >2048-byte + * comment header. The loader's one whole-source header/import read must + * still validate it in both stages. */ { const char *src = "test/wcc/data/direnum/bad_deep_entry.ww"; - snprintf(errp, sizeof errp, "/tmp/direnum_%d_baddeep.err", getpid()); - /* CLASS B (see bad block): -o into a unique /tmp tmpdir keeps the - * .sepwork scratch out of the tracked tree on the expected - * reject; rm -rf on every exit path. */ - char td[64], rmcmd[128]; + /* Same owned-root layout as the shallow conflict above. */ + char out[512]; char cmd[2048]; - snprintf(td, sizeof td, "/tmp/direnum_%d_baddeep_c", getpid()); - mkdir(td, 0755); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", td); - snprintf(cmd, sizeof cmd, "%s build -o %s/out %s 2>%s >/dev/null", - ww, td, src, errp); + snprintf(out, sizeof out, "%s/baddeep-c", owner); + snprintf(errp, sizeof errp, "%s/baddeep-c.err", owner); + snprintf(cmd, sizeof cmd, "%s build -o '%s' %s 2>'%s' >/dev/null", + ww, out, src, errp); int rc = runwait(cmd); - runwait(rmcmd); total++; if (rc == 0) { fprintf(stderr, "737[bad-deep-cstage]: expected build failure, succeeded\n"); fail++; - } else if (!stderr_contains(errp, "does not match import path")) { - fprintf(stderr, "737[bad-deep-cstage]: stderr missing 'does not match import path'\n"); + } else if (!stderr_contains(errp, "conflicting package names")) { + fprintf(stderr, "737[bad-deep-cstage]: stderr missing 'conflicting package names'\n"); fail++; } - unlink(errp); if (access(ww_ww, X_OK) == 0) { - snprintf(td, sizeof td, "/tmp/direnum_%d_baddeep_w", getpid()); - mkdir(td, 0755); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", td); - snprintf(cmd, sizeof cmd, "%s build -o %s/out %s 2>%s >/dev/null", - ww_ww, td, src, errp); + snprintf(out, sizeof out, "%s/baddeep-w", owner); + snprintf(errp, sizeof errp, "%s/baddeep-w.err", owner); + snprintf(cmd, sizeof cmd, + "%s build -o '%s' %s 2>'%s' >/dev/null", + ww_ww, out, src, errp); rc = runwait(cmd); - runwait(rmcmd); total++; if (rc == 0) { fprintf(stderr, "737[bad-deep-wwstage]: expected build failure, succeeded\n"); fail++; - } else if (!stderr_contains(errp, "does not match import path")) { - fprintf(stderr, "737[bad-deep-wwstage]: stderr missing 'does not match import path'\n"); + } else if (!stderr_contains(errp, "conflicting package names")) { + fprintf(stderr, "737[bad-deep-wwstage]: stderr missing 'conflicting package names'\n"); fail++; } - unlink(errp); } } + /* Authoritative Package rows. All inputs, outputs, and retained build + * scratch live beneath the invocation-owned root and are removed below. */ + { + char td[512], rootok[512], rootbad[512], importbad[512]; + char wanted[512], missing[512], invalid[512]; + char path[1024], cmd[4096]; + snprintf(td, sizeof td, "%s/owned", owner); + snprintf(rootok, sizeof rootok, "%s/rootok", td); + snprintf(rootbad, sizeof rootbad, "%s/rootbad", td); + snprintf(importbad, sizeof importbad, "%s/importbad", td); + snprintf(wanted, sizeof wanted, "%s/wanted", importbad); + snprintf(missing, sizeof missing, "%s/missing", td); + snprintf(invalid, sizeof invalid, "%s/invalid", td); + + int setup = 0; + if (mkdir(td, 0755) != 0) setup = 1; + else have_owned = 1; + if (!setup && mkdir(rootok, 0755) != 0) setup = 1; + else if (!setup) have_rootok = 1; + if (!setup && mkdir(rootbad, 0755) != 0) setup = 1; + else if (!setup) have_rootbad = 1; + if (!setup && mkdir(importbad, 0755) != 0) setup = 1; + else if (!setup) have_importbad = 1; + if (!setup && mkdir(wanted, 0755) != 0) setup = 1; + else if (!setup) have_wanted = 1; + if (!setup && mkdir(missing, 0755) != 0) setup = 1; + else if (!setup) have_missing = 1; + if (!setup && mkdir(invalid, 0755) != 0) setup = 1; + else if (!setup) have_invalid = 1; + if (!setup) snprintf(path, sizeof path, "%s/z.ww", rootok); + if (!setup) setup = write_file(path, + "package rootok;\n// ORDER-Z\n" + "fn value() i32 = { return 17; };\n"); + if (!setup) snprintf(path, sizeof path, "%s/a.ww", rootok); + if (!setup) setup = write_file(path, + "// leading comment is part of the loader grammar\n" + "package rootok;\n// ORDER-A\n" + "fn main() i32 = { return value(); };\n"); + if (!setup) snprintf(path, sizeof path, "%s/a.ww", rootbad); + if (!setup) setup = write_file(path, + "package rootbad;\nfn main() i32 = { return 0; };\n"); + if (!setup) snprintf(path, sizeof path, "%s/b.ww", rootbad); + if (!setup) setup = write_file(path, + "package other;\nfn spare() i32 = { return 0; };\n"); + if (!setup) snprintf(path, sizeof path, "%s/entry.ww", importbad); + if (!setup) setup = write_file(path, + "package main;\nimport wanted;\n" + "fn main() i32 = { return wanted.value(); };\n"); + if (!setup) snprintf(path, sizeof path, "%s/a.ww", wanted); + if (!setup) setup = write_file(path, + "package other;\nexport fn value() i32 = { return 0; };\n"); + if (!setup) snprintf(path, sizeof path, "%s/a.ww", missing); + if (!setup) setup = write_file(path, + "fn main() i32 = { return 0; };\n"); + if (!setup) snprintf(path, sizeof path, "%s/a.ww", invalid); + if (!setup) setup = write_file(path, + "package 7bad;\nfn main() i32 = { return 0; };\n"); + + if (setup) { + fprintf(stderr, "737[owned-setup]: cannot create private fixtures\n"); + fail++; + total++; + } else { + const char *drivers[] = { ww, ww_ww }; + const char *stages[] = { "cstage", "wwstage" }; + char units[2][512]; + int built[2] = {0, 0}; + for (int s = 0; s < 2; s++) { + if (access(drivers[s], X_OK) != 0) continue; + char out[512], err[512]; + snprintf(out, sizeof out, "%s/root-%d", td, s); + snprintf(err, sizeof err, "%s/root-%d.err", td, s); + snprintf(cmd, sizeof cmd, + "%s build -o '%s' '%s' " + ">/dev/null 2>'%s'", drivers[s], out, + rootok, err); + int rc = runwait(cmd); + total++; + if (rc != 0 || runwait(out) != 17) { + fprintf(stderr, + "737[root-%s]: build/run failed (build=%d)\n", + stages[s], rc); + fail++; + continue; + } + snprintf(units[s], sizeof units[s], + "%s.sepwork/__root.unit.ww", out); + if (!file_orders(units[s], "ORDER-A", "ORDER-Z")) { + fprintf(stderr, + "737[root-%s]: stored sources are not byte-sorted\n", + stages[s]); + fail++; + } else { + built[s] = 1; + } + } + if (built[0] && built[1]) { + total++; + if (!files_equal(units[0], units[1])) { + fprintf(stderr, + "737[root-parity]: C/WW units differ\n"); + fail++; + } + } + + struct rejectrow { + const char *tag; + const char *target; + const char *needle; + } rows[] = { + { "root-conflict", rootbad, "conflicting package names" }, + { "import-leaf", NULL, "does not match import path" }, + { "root-missing", missing, "invalid or missing package clause" }, + { "root-invalid", invalid, "invalid or missing package clause" }, + }; + char entry[512]; + snprintf(entry, sizeof entry, "%s/entry.ww", importbad); + rows[1].target = entry; + for (int r = 0; r < 4; r++) { + char errors[2][512]; + int ran[2] = {0, 0}; + for (int s = 0; s < 2; s++) { + if (access(drivers[s], X_OK) != 0) continue; + char out[512]; + snprintf(out, sizeof out, "%s/%s-%d", td, + rows[r].tag, s); + snprintf(errors[s], sizeof errors[s], "%s/%s-%d.err", + td, rows[r].tag, s); + snprintf(cmd, sizeof cmd, + "%s build -o '%s' '%s' " + ">/dev/null 2>'%s'", drivers[s], out, + rows[r].target, errors[s]); + int rc = runwait(cmd); + total++; + ran[s] = 1; + if (rc == 0 + || !stderr_contains(errors[s], rows[r].needle)) { + fprintf(stderr, + "737[%s-%s]: reject/diagnostic mismatch\n", + rows[r].tag, stages[s]); + fail++; + } + } + if (ran[0] && ran[1]) { + total++; + if (!files_equal(errors[0], errors[1])) { + fprintf(stderr, + "737[%s-parity]: C/WW diagnostics differ\n", + rows[r].tag); + fail++; + } + } + } + } + } + + int cleanup_fail = 0; + char path[1024], cmd[2048]; + const char *top_stems[] = { "bad-c", "bad-w", "baddeep-c", "baddeep-w" }; + const char *suffixes[] = { "", ".err" }; + for (size_t i = 0; i < sizeof top_stems / sizeof top_stems[0]; i++) { + snprintf(path, sizeof path, "%s/%s.sepwork", owner, top_stems[i]); + snprintf(cmd, sizeof cmd, "rm -rf -- '%s'", path); + if (runwait(cmd) != 0) { + fprintf(stderr, "737[cleanup]: remove %s failed\n", path); + cleanup_fail = 1; + } + } + const char *reject_tags[] = { + "root-conflict", "import-leaf", "root-missing", "root-invalid" + }; + if (have_owned) { + for (int s = 0; s < 2; s++) { + snprintf(path, sizeof path, "%s/owned/root-%d.sepwork", owner, s); + snprintf(cmd, sizeof cmd, "rm -rf -- '%s'", path); + if (runwait(cmd) != 0) { + fprintf(stderr, "737[cleanup]: remove %s failed\n", path); + cleanup_fail = 1; + } + for (size_t r = 0; r < sizeof reject_tags / sizeof reject_tags[0]; r++) { + snprintf(path, sizeof path, "%s/owned/%s-%d.sepwork", + owner, reject_tags[r], s); + snprintf(cmd, sizeof cmd, "rm -rf -- '%s'", path); + if (runwait(cmd) != 0) { + fprintf(stderr, "737[cleanup]: remove %s failed\n", path); + cleanup_fail = 1; + } + } + } + } + + for (size_t i = 0; i < sizeof top_stems / sizeof top_stems[0]; i++) { + for (size_t k = 0; k < sizeof suffixes / sizeof suffixes[0]; k++) { + snprintf(path, sizeof path, "%s/%s%s", owner, + top_stems[i], suffixes[k]); + if (unlink(path) != 0 && errno != ENOENT) { + fprintf(stderr, "737[cleanup]: unlink %s: %s\n", + path, strerror(errno)); + cleanup_fail = 1; + } + } + } + if (have_owned) { + for (int s = 0; s < 2; s++) { + for (size_t k = 0; k < sizeof suffixes / sizeof suffixes[0]; k++) { + snprintf(path, sizeof path, "%s/owned/root-%d%s", + owner, s, suffixes[k]); + if (unlink(path) != 0 && errno != ENOENT) { + fprintf(stderr, "737[cleanup]: unlink %s: %s\n", + path, strerror(errno)); + cleanup_fail = 1; + } + } + for (size_t r = 0; r < sizeof reject_tags / sizeof reject_tags[0]; r++) { + for (size_t k = 0; k < sizeof suffixes / sizeof suffixes[0]; k++) { + snprintf(path, sizeof path, "%s/owned/%s-%d%s", + owner, reject_tags[r], s, suffixes[k]); + if (unlink(path) != 0 && errno != ENOENT) { + fprintf(stderr, "737[cleanup]: unlink %s: %s\n", + path, strerror(errno)); + cleanup_fail = 1; + } + } + } + } + } + + struct owned_path { + const char *name; + int owned; + } fixture_files[] = { + { "owned/rootok/z.ww", have_rootok }, + { "owned/rootok/a.ww", have_rootok }, + { "owned/rootbad/a.ww", have_rootbad }, + { "owned/rootbad/b.ww", have_rootbad }, + { "owned/importbad/entry.ww", have_importbad }, + { "owned/importbad/wanted/a.ww", have_wanted }, + { "owned/missing/a.ww", have_missing }, + { "owned/invalid/a.ww", have_invalid }, + }; + for (size_t i = 0; i < sizeof fixture_files / sizeof fixture_files[0]; i++) { + if (!fixture_files[i].owned) continue; + snprintf(path, sizeof path, "%s/%s", owner, fixture_files[i].name); + if (unlink(path) != 0 && errno != ENOENT) { + fprintf(stderr, "737[cleanup]: unlink %s: %s\n", + path, strerror(errno)); + cleanup_fail = 1; + } + } + struct owned_path fixture_dirs[] = { + { "owned/importbad/wanted", have_wanted }, + { "owned/rootok", have_rootok }, + { "owned/rootbad", have_rootbad }, + { "owned/importbad", have_importbad }, + { "owned/missing", have_missing }, + { "owned/invalid", have_invalid }, + { "owned", have_owned }, + }; + for (size_t i = 0; i < sizeof fixture_dirs / sizeof fixture_dirs[0]; i++) { + if (!fixture_dirs[i].owned) continue; + snprintf(path, sizeof path, "%s/%s", owner, fixture_dirs[i].name); + if (rmdir(path) != 0 && errno != ENOENT) { + fprintf(stderr, "737[cleanup]: rmdir %s: %s\n", + path, strerror(errno)); + cleanup_fail = 1; + } + } + if (rmdir(owner) != 0) { + fprintf(stderr, "737[cleanup]: rmdir %s: %s\n", + owner, strerror(errno)); + cleanup_fail = 1; + } + if (cleanup_fail) fail++; + if (fail) { fprintf(stderr, "737_direnum: %d/%d fixtures failed\n", fail, total); return 1; diff --git a/test/wcc/744_letcopy_struct.c b/test/wcc/744_letcopy_struct.c index 1f35990c..b6a1bc4e 100644 --- a/test/wcc/744_letcopy_struct.c +++ b/test/wcc/744_letcopy_struct.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include "wwtestpkg.h" @@ -70,8 +69,7 @@ struct row { const char *label; /* Direct-w6c source: bare fn produce, no package/imports. */ const char *asm_src; - /* Driver source: `package main; import os; ...` runtime wrapper. */ - const char *run_src; + /* Runtime copies are owned by test/wcc/data/r744_letcopy_*. */ /* Source-slot loads expected in produce: at least N for the copy. * Pre-fix cstage emitted 0; pre-fix wwstage emitted 1 with stale BX. * `MOVQ -K(BP), AX` is also emitted by p2.a / p2.s.len reads in the @@ -96,18 +94,6 @@ static const struct row rows[] = { " if (p2.c != 33) { return 13; };\n" " return 0;\n" "};\n", - "package main;\n" - "import os;\n" - "type tri = struct { a: i32, b: i32, c: i32 };\n" - "fn produce() i32 = {\n" - " let p1: tri = tri{a=11, b=22, c=33};\n" - " let p2: tri = p1;\n" - " if (p2.a != 11) { return 11; };\n" - " if (p2.b != 22) { return 12; };\n" - " if (p2.c != 33) { return 13; };\n" - " return 0;\n" - "};\n" - "export fn main() i32 = { os.exit(produce()); };\n", 1 }, /* (b) i32 + str field, sz=24. Copy = 3 × MOVQ. */ { "field_str", @@ -120,18 +106,6 @@ static const struct row rows[] = { " if (p2.s.len != 2) { return 12; };\n" " return 0;\n" "};\n", - "package main;\n" - "import os;\n" - "type ws = struct { a: i32, s: str };\n" - "fn produce() i32 = {\n" - " let p1: ws;\n" - " p1.a = 7; p1.s = \"hi\";\n" - " let p2: ws = p1;\n" - " if (p2.a != 7) { return 11; };\n" - " if (p2.s.len != 2) { return 12; };\n" - " return 0;\n" - "};\n" - "export fn main() i32 = { os.exit(produce()); };\n", 3 }, /* (c) i32 + []u8 slice field, sz=32. Copy = 4 × MOVQ. */ { "field_slice", @@ -145,19 +119,6 @@ static const struct row rows[] = { " if (p2.b.len != 3) { return 12; };\n" " return 0;\n" "};\n", - "package main;\n" - "import os;\n" - "type wsl = struct { a: i32, b: []u8 };\n" - "fn produce() i32 = {\n" - " let raw: [3]u8 = [1: u8, 2: u8, 3: u8];\n" - " let p1: wsl;\n" - " p1.a = 9; p1.b = raw[0:3];\n" - " let p2: wsl = p1;\n" - " if (p2.a != 9) { return 11; };\n" - " if (p2.b.len != 3) { return 12; };\n" - " return 0;\n" - "};\n" - "export fn main() i32 = { os.exit(produce()); };\n", 4 }, /* (d) i32 + tagged (i32|str) field, sz=32. Copy = 4 × MOVQ. * Runtime checks only p2.a; the asm load-count (>=4) and the @@ -174,21 +135,6 @@ static const struct row rows[] = { " if (p2.a != 5) { return 11; };\n" " return 0;\n" "};\n", - "package main;\n" - "import os;\n" - "type tag = (i32 | str);\n" - "type wtg = struct { a: i32, t: tag };\n" - "fn produce() i32 = {\n" - " let p1: wtg = wtg{a=5, t=42: tag};\n" - " let p2: wtg = p1;\n" - " if (p2.a != 5) { return 11; };\n" - " match (p2.t) {\n" - " case let x: i32 => if (x != 42) { return 12; };\n" - " case let s: str => return 13;\n" - " };\n" - " return 0;\n" - "};\n" - "export fn main() i32 = { os.exit(produce()); };\n", 4 }, }; @@ -243,39 +189,6 @@ emit_s(const char *w6c, const struct row *r, int i, int stage, return rc; } -static int -run_driver(const char *driver, const struct row *r, int i, int stage) -{ - char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/lcs_run_%d_d_%d_%d", - getpid(), i, stage); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/lcs_run_%d_%d_%d.ww", - tmpdir, getpid(), i, stage); - snprintf(outbin, sizeof outbin, "%s/lcs_run_%d_%d_%d", - tmpdir, getpid(), i, stage); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); - - FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return -1; } - wwtest_fputs(r->run_src, f); - fclose(f); - - snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null", - driver, outbin, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s] build via %s failed\n", - r->label, driver); - runwait(rmcmd); - return -1; - } - - int got = runwait(outbin); - - runwait(rmcmd); - return got; -} - int main(void) { @@ -289,14 +202,11 @@ main(void) bin = absbin; } - char w6c[640], w6c_ww[640], cdrv[640], wdrv[640]; + char w6c[640], w6c_ww[640]; snprintf(w6c, sizeof w6c, "%s/w6c", bin); snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin); - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); int have_ww = (access(w6c_ww, X_OK) == 0); - int have_wdrv = (access(wdrv, X_OK) == 0); int n = (int)(sizeof rows / sizeof rows[0]); int total = 0, fail = 0; @@ -343,27 +253,6 @@ main(void) } unlink(cs_path); - /* Runtime correctness via cstage driver. */ - total++; - int got = run_driver(cdrv, r, i, 2); - if (got != 0) { - fprintf(stderr, - "letcopy_struct[ww/%s]: exit=%d want=0\n", - r->label, got); - fail++; - } - - /* Runtime correctness via wwstage driver. */ - if (have_wdrv) { - total++; - got = run_driver(wdrv, r, i, 3); - if (got != 0) { - fprintf(stderr, - "letcopy_struct[ww_ww/%s]: exit=%d want=0\n", - r->label, got); - fail++; - } - } } if (fail) { diff --git a/test/wcc/749_sumtype_forward.c b/test/wcc/749_sumtype_forward.c index bc591a8d..35112b3a 100644 --- a/test/wcc/749_sumtype_forward.c +++ b/test/wcc/749_sumtype_forward.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -53,10 +54,9 @@ runwait(const char *cmd) } struct row { + /* Runtime ownership: test/wcc/data/r749_sumtype_forward_*. */ const char *label; const char *src; - int want_cs; - int want_ws; }; static const struct row rows[] = { @@ -85,8 +85,7 @@ static const struct row rows[] = { "export fn main() i32 = {\n" " if (!pick(\"foo\", \"world\", 'x')) { return 11; };\n" " return 0;\n" - "};\n", - 0, 0 }, + "};\n" }, /* 2. Ident-source sum-arg. needle is a (str|rune) local; helper * call routes through the existing N_IDENT-aistagged path. The @@ -105,8 +104,7 @@ static const struct row rows[] = { " let needle: (str | rune) = \"foo\";\n" " if (!helper(needle)) { return 11; };\n" " return 0;\n" - "};\n", - 0, 0 }, + "};\n" }, /* 3. Literal-source sum-arg. helper(\"foo\") widens the str at * the call site. Stays on the widening path (param tagged, arg @@ -123,25 +121,9 @@ static const struct row rows[] = { "export fn main() i32 = {\n" " if (!helper(\"foo\")) { return 11; };\n" " return 0;\n" - "};\n", - 0, 0 }, + "};\n" }, }; -static int -build_with(const char *driver, const char *src_path, const char *outbin) -{ - char cmd[1024]; - snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null", - driver, outbin, src_path); - return runwait(cmd); -} - -static int -exec_bin(const char *bin) -{ - return runwait(bin); -} - /* read_file slurps a file whole, returning a malloc'd buffer * (NUL-terminated) or NULL on error. */ static char * @@ -195,12 +177,9 @@ main(void) bin = absbin; } - char cdrv[640], wdrv[640], cw6[640], ww6[640]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); + char cw6[640], ww6[640]; snprintf(cw6, sizeof cw6, "%s/w6c", bin); snprintf(ww6, sizeof ww6, "%s/w6c_ww", bin); - int have_ww = (access(wdrv, X_OK) == 0); int have_w6cww = (access(ww6, X_OK) == 0); int n = (int)(sizeof rows / sizeof rows[0]); @@ -209,56 +188,37 @@ main(void) for (int i = 0; i < n; i++) { const struct row *r = &rows[i]; - char src[128], tmpdir[64], outbin[128], rmcmd[160]; + char src[160], tmpdir[96], css[160], wss[160]; snprintf(tmpdir, sizeof tmpdir, "/tmp/sumtype_fwd_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + if (mkdir(tmpdir, 0755) != 0) { + fprintf(stderr, + "sumtype_forward[%s]: cannot acquire temp directory: %s\n", + r->label, strerror(errno)); + fail++; total++; + continue; + } snprintf(src, sizeof src, "%s/sumtype_fwd_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/sumtype_fwd_%d_%d", - tmpdir, getpid(), i); + snprintf(css, sizeof css, "%s/cs.s", tmpdir); + snprintf(wss, sizeof wss, "%s/ws.s", tmpdir); + char *cs_asm = NULL, *ws_asm = NULL; + size_t cs_n = 0, ws_n = 0; + FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); fail++; total++; continue; } - fputs(r->src, f); - fclose(f); - - /* Runtime parity (cstage). */ - total++; - if (build_with(cdrv, src, outbin) != 0) { - fprintf(stderr, - "sumtype_forward[cs][%s]: build failed\n", - r->label); - fail++; - } else { - int got = exec_bin(outbin); - if (got != r->want_cs) { - fprintf(stderr, - "sumtype_forward[cs][%s]: rc=%d want=%d\n", - r->label, got, r->want_cs); - fail++; - } + if (!f) { + fprintf(stderr, "sumtype_forward[%s]: cannot create source: %s\n", + r->label, strerror(errno)); + fail++; total++; + goto cleanup; } - unlink(outbin); - - /* Runtime parity (wwstage). */ - if (have_ww) { - total++; - if (build_with(wdrv, src, outbin) != 0) { - fprintf(stderr, - "sumtype_forward[ws][%s]: build failed\n", - r->label); - fail++; - } else { - int got = exec_bin(outbin); - if (got != r->want_ws) { - fprintf(stderr, - "sumtype_forward[ws][%s]: rc=%d want=%d\n", - r->label, got, r->want_ws); - fail++; - } - } - unlink(outbin); + int write_failed = fputs(r->src, f) == EOF; + if (fclose(f) != 0) write_failed = 1; + if (write_failed) { + fprintf(stderr, "sumtype_forward[%s]: cannot write source\n", + r->label); + fail++; total++; + goto cleanup; } /* Byte-id of the helper(needles[i]) callsite window. Emit @@ -266,23 +226,43 @@ main(void) * (w6c emits no .sepwork, so no leak) and compare. Only the * first row has a main.helper call; rows 2/3 also have one * but a simpler shape — match them all for symmetry. */ - char *cs_asm = NULL, *ws_asm = NULL; - size_t cs_n = 0, ws_n = 0; if (have_w6cww) { - char css[160], wss[160], emit[1100]; - snprintf(css, sizeof css, "%s/cs.s", tmpdir); - snprintf(wss, sizeof wss, "%s/ws.s", tmpdir); + char emit[1100]; + total++; snprintf(emit, sizeof emit, "%s -o %s %s 2>/dev/null", cw6, css, src); - if (runwait(emit) == 0) - cs_asm = read_file(css, &cs_n); + if (runwait(emit) != 0) { + fprintf(stderr, + "sumtype_forward[cstage][%s]: w6c failed\n", + r->label); + fail++; + goto cleanup; + } + cs_asm = read_file(css, &cs_n); + if (cs_asm == NULL) { + fprintf(stderr, + "sumtype_forward[cstage][%s]: cannot read %s\n", + r->label, css); + fail++; + goto cleanup; + } snprintf(emit, sizeof emit, "%s -o %s %s 2>/dev/null", ww6, wss, src); - if (runwait(emit) == 0) - ws_asm = read_file(wss, &ws_n); - } - if (cs_asm && ws_asm) { - total++; + if (runwait(emit) != 0) { + fprintf(stderr, + "sumtype_forward[wwstage][%s]: w6c_ww failed\n", + r->label); + fail++; + goto cleanup; + } + ws_asm = read_file(wss, &ws_n); + if (ws_asm == NULL) { + fprintf(stderr, + "sumtype_forward[wwstage][%s]: cannot read %s\n", + r->label, wss); + fail++; + goto cleanup; + } const char *cs_site = helper_callsite(cs_asm); const char *ws_site = helper_callsite(ws_asm); if (!cs_site || !ws_site) { @@ -299,10 +279,25 @@ main(void) fail++; } } + + cleanup: ; free(cs_asm); free(ws_asm); - - runwait(rmcmd); + int cleanup_fail = 0; + const char *files[] = { src, css, wss }; + for (size_t k = 0; k < sizeof files / sizeof files[0]; k++) { + if (unlink(files[k]) != 0 && errno != ENOENT) { + fprintf(stderr, "sumtype_forward[%s]: unlink %s: %s\n", + r->label, files[k], strerror(errno)); + cleanup_fail = 1; + } + } + if (rmdir(tmpdir) != 0) { + fprintf(stderr, "sumtype_forward[%s]: rmdir %s: %s\n", + r->label, tmpdir, strerror(errno)); + cleanup_fail = 1; + } + if (cleanup_fail) fail++; } if (fail) { diff --git a/test/wcc/750_mklabel_modscoped.c b/test/wcc/750_mklabel_modscoped.c index d442995f..fe211b9a 100644 --- a/test/wcc/750_mklabel_modscoped.c +++ b/test/wcc/750_mklabel_modscoped.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -176,7 +177,8 @@ static const struct row rows[] = { /* Write `src` to `

/`, mkdir'ing intermediate dirs. */ static int -write_one(const char *dir, const char *rel, const char *src) +write_one(const char *dir, const char *rel, const char *src, + int *parent_owned) { char path[512]; snprintf(path, sizeof path, "%s/%s", dir, rel); @@ -184,14 +186,15 @@ write_one(const char *dir, const char *rel, const char *src) char *slash = strchr(path + strlen(dir) + 1, '/'); if (slash) { *slash = '\0'; - mkdir(path, 0755); + if (mkdir(path, 0755) != 0) return -1; + *parent_owned = 1; *slash = '/'; } FILE *f = fopen(path, "wb"); if (!f) return -1; - fputs(src, f); - fclose(f); - return 0; + int failed = fputs(src, f) == EOF; + if (fclose(f) != 0) failed = 1; + return failed ? -1 : 0; } static char * @@ -215,29 +218,43 @@ slurp(const char *path, size_t *outsz) static int run_row(const char *driver, const struct row *r, int idx, const char *tag) { - char tmpdir[64]; + char tmpdir[96]; snprintf(tmpdir, sizeof tmpdir, "/tmp/mklabel_%s_%d_%d", tag, getpid(), idx); - mkdir(tmpdir, 0755); + if (mkdir(tmpdir, 0755) != 0) { + fprintf(stderr, + "mklabel_modscoped[%s][%s]: cannot acquire temp directory: %s\n", + tag, r->label, strerror(errno)); + return 1; + } int fail = 0; + int parent_owned[4] = { 0 }; + char entry_bin[512], stem[512], alls[600], cmd[2048]; + snprintf(entry_bin, sizeof entry_bin, "%s/%s", tmpdir, r->entry); + char *dot = strrchr(entry_bin, '.'); + if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; + snprintf(stem, sizeof stem, "%s/%s", tmpdir, r->entry); + char *d2 = strrchr(stem, '.'); + if (d2 && strcmp(d2, ".ww") == 0) *d2 = '\0'; + snprintf(alls, sizeof alls, "%s/all.s", tmpdir); for (int k = 0; k < r->n_files; k++) { if (write_one(tmpdir, r->files[2 * k], - r->files[2 * k + 1]) != 0) { + r->files[2 * k + 1], &parent_owned[k]) != 0) { fprintf(stderr, "mklabel_modscoped[%s][%s]: write %s failed\n", tag, r->label, r->files[2 * k]); - return 1; + fail++; + goto cleanup; } } - char cmd[2048]; /* #93 sep layout: emit asm to .sepwork/.s; pin - * WW_PKGCACHE under tmpdir so out/.pkgcache is untouched. */ + * all outputs stay under tmpdir. */ snprintf(cmd, sizeof cmd, - "cd %s && b='%s'; WW_PKGCACHE=%s/pkgc %s build --sep " + "cd %s && b='%s'; %s build " "-o \"${b%%.ww}\" \"$b\" >/dev/null 2>&1", - tmpdir, r->entry, tmpdir, driver); + tmpdir, r->entry, driver); if (runwait(cmd) != 0) { fprintf(stderr, "mklabel_modscoped[%s][%s]: build failed\n", @@ -247,10 +264,6 @@ run_row(const char *driver, const struct row *r, int idx, const char *tag) } /* Runtime check. */ - char entry_bin[512]; - snprintf(entry_bin, sizeof entry_bin, "%s/%s", tmpdir, r->entry); - char *dot = strrchr(entry_bin, '.'); - if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; int got = runwait(entry_bin); if (got != r->want_exit) { fprintf(stderr, @@ -264,12 +277,7 @@ run_row(const char *driver, const struct row *r, int idx, const char *tag) * cross-module label substrings (mod1.x / strings.x) live in the * per-package files, so concat ALL of them (sorted glob) — a single * __root.s read would FALSE-FAIL the multi-package rows. */ - char stem[512]; - snprintf(stem, sizeof stem, "%s/%s", tmpdir, r->entry); - char *d2 = strrchr(stem, '.'); - if (d2 && strcmp(d2, ".ww") == 0) *d2 = '\0'; - char alls[600], catcmd[1200]; - snprintf(alls, sizeof alls, "%s/all.s", tmpdir); + char catcmd[1200]; snprintf(catcmd, sizeof catcmd, "cat %s.sepwork/*.s > %s 2>/dev/null", stem, alls); (void)runwait(catcmd); @@ -293,10 +301,60 @@ run_row(const char *driver, const struct row *r, int idx, const char *tag) free(asm_buf); } -cleanup: - /* Best-effort cleanup. Leaving artifacts is fine on failure. */ - snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir); - (void)runwait(cmd); +cleanup: ; + /* The retained build scratch is the only recursively removed path. */ + int cleanup_fail = 0; + char sepwork[600]; + snprintf(sepwork, sizeof sepwork, "%s.sepwork", stem); + snprintf(cmd, sizeof cmd, "rm -rf -- '%s'", sepwork); + if (runwait(cmd) != 0) { + fprintf(stderr, + "mklabel_modscoped[%s][%s]: remove %s failed\n", + tag, r->label, sepwork); + cleanup_fail = 1; + } + const char *outputs[] = { alls, entry_bin }; + for (size_t k = 0; k < sizeof outputs / sizeof outputs[0]; k++) { + if (unlink(outputs[k]) != 0 && errno != ENOENT) { + fprintf(stderr, + "mklabel_modscoped[%s][%s]: unlink %s: %s\n", + tag, r->label, outputs[k], strerror(errno)); + cleanup_fail = 1; + } + } + for (int k = 0; k < r->n_files; k++) { + char path[512]; + snprintf(path, sizeof path, "%s/%s", tmpdir, r->files[2 * k]); + char *slash = strchr(path + strlen(tmpdir) + 1, '/'); + if (slash && !parent_owned[k]) continue; + if (unlink(path) != 0 && errno != ENOENT) { + fprintf(stderr, + "mklabel_modscoped[%s][%s]: unlink %s: %s\n", + tag, r->label, path, strerror(errno)); + cleanup_fail = 1; + } + } + for (int k = r->n_files - 1; k >= 0; k--) { + char path[512]; + snprintf(path, sizeof path, "%s/%s", tmpdir, r->files[2 * k]); + char *slash = strrchr(path, '/'); + if (parent_owned[k] && slash && slash > path + strlen(tmpdir)) { + *slash = '\0'; + if (rmdir(path) != 0 && errno != ENOENT) { + fprintf(stderr, + "mklabel_modscoped[%s][%s]: rmdir %s: %s\n", + tag, r->label, path, strerror(errno)); + cleanup_fail = 1; + } + } + } + if (rmdir(tmpdir) != 0) { + fprintf(stderr, + "mklabel_modscoped[%s][%s]: rmdir %s: %s\n", + tag, r->label, tmpdir, strerror(errno)); + cleanup_fail = 1; + } + if (cleanup_fail) fail++; return fail; } diff --git a/test/wcc/752_modparam_callee.c b/test/wcc/752_modparam_callee.c index 6ca91391..3e947255 100644 --- a/test/wcc/752_modparam_callee.c +++ b/test/wcc/752_modparam_callee.c @@ -42,14 +42,6 @@ * | mis-marshals — rc=11. * | Post-fix both stages * | rc=0. - * same_module_same_leaf | non-regression: two - * | fns in the same module - * | sharing a leaf (one - * | variadic, one not). - * | mklabel seq stays - * | distinct + each call - * | dispatches to its own - * | shape. * bare_leaf_no_collision | control: variadic fn * | declared in only one * | module, called via @@ -60,10 +52,16 @@ * | through bare * | fnparamslookup * | (post-#4d). + * + * The standalone same_module_same_leaf runtime control now lives in + * test/wcc/data/r75_modparam_same_module_same_leaf/case.ww. This wrapper + * retains only the two package-layout rows that require separately named + * imported source files and therefore are not expressible by case.ww. */ #include #include #include +#include #include #include #include @@ -126,34 +124,6 @@ static const struct row rows[] = { "};\n" }, "main.ww" }, - /* Non-regression. Same module declares two fns sharing a leaf - * (one variadic, one not). The N_IDENT bare-leaf path still - * graduates via fnparamslookup's same-module-first walk (#4d); - * the N_DOT arm doesn't fire because there's no `mod.fn` use - * site. */ - { "same_module_same_leaf", 0, 1, - { "main.ww", - "package main;\n" - "fn variadic_foo(a: i32, xs: i32...) i32 = {\n" - "\treturn a + xs.len;\n" - "};\n" - "fn scalar_foo(a: i32, n: (u8 | []u8)) i32 = {\n" - "\tmatch (n) {\n" - "\tcase let c: u8 => return a + (c: i32);\n" - "\tcase let s: []u8 => return a + s.len;\n" - "\t};\n" - "\treturn 0;\n" - "};\n" - "export fn main() i32 = {\n" - "\tlet n: (u8 | []u8) = 5u8;\n" - "\tlet a: i32 = variadic_foo(1, 2, 3, 4);\n" - "\tlet b: i32 = scalar_foo(10, n);\n" - "\tif (a != 4) { return 30; };\n" - "\tif (b != 15) { return 31; };\n" - "\treturn 0;\n" - "};\n" }, - "main.ww" }, - /* Control. A variadic fn exists in only one imported module; * the caller calls it via N_DOT. No same-leaf collision, so * fnparamslookupmod's same-module hint and the head-walk @@ -179,39 +149,79 @@ static const struct row rows[] = { }; static int -write_one(const char *dir, const char *rel, const char *src) +write_one(const char *dir, const char *rel, const char *src, + int *parent_owned) { char path[512]; snprintf(path, sizeof path, "%s/%s", dir, rel); char *slash = strchr(path + strlen(dir) + 1, '/'); if (slash) { *slash = '\0'; - mkdir(path, 0755); + if (mkdir(path, 0755) != 0) return -1; + *parent_owned = 1; *slash = '/'; } FILE *f = fopen(path, "wb"); if (!f) return -1; - fputs(src, f); - fclose(f); - return 0; + int rc = fputs(src, f) == EOF ? -1 : 0; + if (fclose(f) != 0) rc = -1; + return rc; +} + +static int +cleanup_row(const char *tmpdir, const struct row *r, + const int *parent_owned) +{ + char path[512], cmd[640], entry[512]; + int rc = 0; + + for (int k = 0; k < r->n_files; k++) { + snprintf(path, sizeof path, "%s/%s", tmpdir, r->files[2 * k]); + char *slash = strchr(path + strlen(tmpdir) + 1, '/'); + if (slash && !parent_owned[k]) continue; + if (unlink(path) != 0 && errno != ENOENT) rc = -1; + } + + snprintf(entry, sizeof entry, "%s/%s", tmpdir, r->entry); + char *dot = strrchr(entry, '.'); + if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; + if (unlink(entry) != 0 && errno != ENOENT) rc = -1; + snprintf(cmd, sizeof cmd, "rm -rf %s.sepwork", entry); + if (runwait(cmd) != 0) rc = -1; + + for (int k = 0; k < r->n_files; k++) { + snprintf(path, sizeof path, "%s/%s", tmpdir, r->files[2 * k]); + char *slash = strchr(path + strlen(tmpdir) + 1, '/'); + if (slash && parent_owned[k]) { + *slash = '\0'; + if (rmdir(path) != 0 && errno != ENOENT) rc = -1; + } + } + if (rmdir(tmpdir) != 0) rc = -1; + return rc; } static int run_row(const char *driver, const struct row *r, int idx, const char *tag) { - char tmpdir[64]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/modparam_%s_%d_%d", - tag, getpid(), idx); - mkdir(tmpdir, 0755); + (void)idx; + char tmpdir[] = "/tmp/modparam_XXXXXX"; + if (mkdtemp(tmpdir) == NULL) { + fprintf(stderr, "modparam_callee[%s][%s]: mkdtemp failed\n", + tag, r->label); + return 1; + } int fail = 0; + int parent_owned[4] = { 0 }; for (int k = 0; k < r->n_files; k++) { if (write_one(tmpdir, r->files[2 * k], - r->files[2 * k + 1]) != 0) { + r->files[2 * k + 1], &parent_owned[k]) != 0) { fprintf(stderr, "modparam_callee[%s][%s]: write %s failed\n", tag, r->label, r->files[2 * k]); - return 1; + fail++; + goto cleanup; } } @@ -239,8 +249,11 @@ run_row(const char *driver, const struct row *r, int idx, const char *tag) } cleanup: - snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir); - (void)runwait(cmd); + if (cleanup_row(tmpdir, r, parent_owned) != 0) { + fprintf(stderr, "modparam_callee[%s][%s]: cleanup failed\n", + tag, r->label); + if (fail == 0) fail++; + } return fail; } diff --git a/test/wcc/753_convwrap_audit.c b/test/wcc/753_convwrap_audit.c index f2e8e340..dc760e51 100644 --- a/test/wcc/753_convwrap_audit.c +++ b/test/wcc/753_convwrap_audit.c @@ -53,6 +53,7 @@ #include #include #include +#include #include #include @@ -125,7 +126,18 @@ emit_s(const char *w6c, const struct row *r, int i, char *out_s, size_t cap) snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, out_s, src); int rc = runwait(cmd); - unlink(src); + int cleanfail = 0; + if (unlink(src) != 0 && errno != ENOENT) { + perror(src); + cleanfail = 1; + } + /* a failing compile can still leave a partial .s behind */ + if (rc != 0 && unlink(out_s) != 0 && errno != ENOENT) { + perror(out_s); + cleanfail = 1; + } + if (cleanfail && rc == 0) + rc = -1; return rc; } @@ -187,6 +199,7 @@ main(void) int have_ww = (access(w6c_ww, X_OK) == 0); int n = (int)(sizeof rows / sizeof rows[0]); int total = 0, fail = 0; + int cleanfail = 0; for (int i = 0; i < n; i++) { char cs_path[128], ws_path[128]; @@ -200,19 +213,43 @@ main(void) total++; if (check_imm(cs_path, &rows[i], "cstage") != 0) fail++; - if (!have_ww) { unlink(cs_path); continue; } + if (!have_ww) { + if (unlink(cs_path) != 0 && errno != ENOENT) { + perror(cs_path); + cleanfail = 1; + } + continue; + } if (emit_s(w6c_ww, &rows[i], i, ws_path, sizeof ws_path) != 0) { fprintf(stderr, "convwrap_audit[wwstage][%s]: w6c_ww failed\n", rows[i].label); fail++; total++; - unlink(cs_path); continue; + if (unlink(cs_path) != 0 && errno != ENOENT) { + perror(cs_path); + cleanfail = 1; + } + continue; } total++; if (check_imm(ws_path, &rows[i], "wwstage") != 0) fail++; - unlink(cs_path); unlink(ws_path); + if (unlink(cs_path) != 0 && errno != ENOENT) { + perror(cs_path); + cleanfail = 1; + } + if (unlink(ws_path) != 0 && errno != ENOENT) { + perror(ws_path); + cleanfail = 1; + } + } + + /* a cleanup failure must not pass silently, but must not mask a + * real assertion failure's own report either. */ + if (cleanfail && fail == 0) { + fprintf(stderr, "convwrap_audit: cleanup failed\n"); + return 1; } if (fail) { diff --git a/test/wcc/754_slice_of_slice_index.c b/test/wcc/754_slice_of_slice_index.c index 1253f0fa..49aae830 100644 --- a/test/wcc/754_slice_of_slice_index.c +++ b/test/wcc/754_slice_of_slice_index.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include "wwtestpkg.h" @@ -117,7 +118,18 @@ emit_s(const char *w6c, const struct row *r, int i, char *out_s, size_t cap) snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, out_s, src); int rc = runwait(cmd); - unlink(src); + int cleanfail = 0; + if (unlink(src) != 0 && errno != ENOENT) { + perror(src); + cleanfail = 1; + } + /* a failing compile can still leave a partial .s behind */ + if (rc != 0 && unlink(out_s) != 0 && errno != ENOENT) { + perror(out_s); + cleanfail = 1; + } + if (cleanfail && rc == 0) + rc = -1; return rc; } @@ -186,6 +198,7 @@ main(void) int have_ww = (access(w6c_ww, X_OK) == 0); int n = (int)(sizeof rows / sizeof rows[0]); int total = 0, fail = 0; + int cleanfail = 0; for (int i = 0; i < n; i++) { char cs_path[128], ws_path[128]; @@ -199,14 +212,24 @@ main(void) total++; if (check_stride(cs_path, &rows[i], "cstage") != 0) fail++; - if (!have_ww) { unlink(cs_path); continue; } + if (!have_ww) { + if (unlink(cs_path) != 0 && errno != ENOENT) { + perror(cs_path); + cleanfail = 1; + } + continue; + } if (emit_s(w6c_ww, &rows[i], i, ws_path, sizeof ws_path) != 0) { fprintf(stderr, "slice_of_slice_index[wwstage][%s]: w6c_ww failed\n", rows[i].label); fail++; total++; - unlink(cs_path); continue; + if (unlink(cs_path) != 0 && errno != ENOENT) { + perror(cs_path); + cleanfail = 1; + } + continue; } total++; if (check_stride(ws_path, &rows[i], "wwstage") != 0) fail++; @@ -222,7 +245,21 @@ main(void) fail++; } - unlink(cs_path); unlink(ws_path); + if (unlink(cs_path) != 0 && errno != ENOENT) { + perror(cs_path); + cleanfail = 1; + } + if (unlink(ws_path) != 0 && errno != ENOENT) { + perror(ws_path); + cleanfail = 1; + } + } + + /* a cleanup failure must not pass silently, but must not mask a + * real assertion failure's own report either. */ + if (cleanfail && fail == 0) { + fprintf(stderr, "slice_of_slice_index: cleanup failed\n"); + return 1; } if (fail) { diff --git a/test/wcc/755_amp_dot_idx.c b/test/wcc/755_amp_dot_idx.c index f6563ded..c8811db2 100644 --- a/test/wcc/755_amp_dot_idx.c +++ b/test/wcc/755_amp_dot_idx.c @@ -59,6 +59,7 @@ #include #include #include +#include #include #include #include "wwtestpkg.h" @@ -149,7 +150,18 @@ emit_s(const char *w6c, const struct row *r, int i, char *out_s, size_t cap) snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, out_s, src); int rc = runwait(cmd); - unlink(src); + int cleanfail = 0; + if (unlink(src) != 0 && errno != ENOENT) { + perror(src); + cleanfail = 1; + } + /* a failing compile can still leave a partial .s behind */ + if (rc != 0 && unlink(out_s) != 0 && errno != ENOENT) { + perror(out_s); + cleanfail = 1; + } + if (cleanfail && rc == 0) + rc = -1; return rc; } @@ -218,6 +230,7 @@ main(void) int have_ww = (access(w6c_ww, X_OK) == 0); int n = (int)(sizeof rows / sizeof rows[0]); int total = 0, fail = 0; + int cleanfail = 0; for (int i = 0; i < n; i++) { char cs_path[128], ws_path[128]; @@ -231,14 +244,24 @@ main(void) total++; if (check_stride(cs_path, &rows[i], "cstage") != 0) fail++; - if (!have_ww) { unlink(cs_path); continue; } + if (!have_ww) { + if (unlink(cs_path) != 0 && errno != ENOENT) { + perror(cs_path); + cleanfail = 1; + } + continue; + } if (emit_s(w6c_ww, &rows[i], i, ws_path, sizeof ws_path) != 0) { fprintf(stderr, "amp_dot_idx[wwstage][%s]: w6c_ww failed\n", rows[i].label); fail++; total++; - unlink(cs_path); continue; + if (unlink(cs_path) != 0 && errno != ENOENT) { + perror(cs_path); + cleanfail = 1; + } + continue; } total++; if (check_stride(ws_path, &rows[i], "wwstage") != 0) fail++; @@ -253,7 +276,21 @@ main(void) fail++; } - unlink(cs_path); unlink(ws_path); + if (unlink(cs_path) != 0 && errno != ENOENT) { + perror(cs_path); + cleanfail = 1; + } + if (unlink(ws_path) != 0 && errno != ENOENT) { + perror(ws_path); + cleanfail = 1; + } + } + + /* a cleanup failure must not pass silently, but must not mask a + * real assertion failure's own report either. */ + if (cleanfail && fail == 0) { + fprintf(stderr, "amp_dot_idx: cleanup failed\n"); + return 1; } if (fail) { diff --git a/test/wcc/756_alias_chain_unwrap.c b/test/wcc/756_alias_chain_unwrap.c index 1f42939a..1bace48f 100644 --- a/test/wcc/756_alias_chain_unwrap.c +++ b/test/wcc/756_alias_chain_unwrap.c @@ -26,19 +26,13 @@ * r4_i32_double | `type b = a; type a = struct{i32...}` | wedge pre-fix (RC=14) * | aliased twice → `type c = b;` | * - * Each row asserts: - * - cstage RC == 0 - * - wwstage RC == 0 (when w6c_ww present) - * - cstage and wwstage emit byte-identical asm - * - * Pre-fix: rows 2 and 4 fail the RC check on both stages and the - * byte-id is also broken on rows 2/4 (one stage emits sret discipline, - * the other emits the scalar-AX fall-through). Post-fix all 12 - * fixtures pass. + * Runtime ownership moved to the matching r756_* fixtures. This carrier + * retains the cstage/wwstage assembly byte-identity discriminator. */ #include #include #include +#include #include #include #include @@ -135,36 +129,6 @@ static const struct row rows[] = { }, }; -static int -build_and_run(const char *driver, const struct row *r, int i) -{ - char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/acu_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/acu_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/acu_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); - - FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return -1; } - fputs(r->src, f); - fclose(f); - - snprintf(cmd, sizeof cmd, "%s build -o %s %s", - driver, outbin, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: build via %s failed\n", - r->label, driver); - runwait(rmcmd); - return -1; - } - - int got = runwait(outbin); - - runwait(rmcmd); - return got; -} - static int emit_s(const char *w6c, const struct row *r, int i, char *out_s, size_t cap) { @@ -180,7 +144,18 @@ emit_s(const char *w6c, const struct row *r, int i, char *out_s, size_t cap) snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, out_s, src); int rc = runwait(cmd); - unlink(src); + int cleanfail = 0; + if (unlink(src) != 0 && errno != ENOENT) { + perror(src); + cleanfail = 1; + } + /* a failing compile can still leave a partial .s behind */ + if (rc != 0 && unlink(out_s) != 0 && errno != ENOENT) { + perror(out_s); + cleanfail = 1; + } + if (cleanfail && rc == 0) + rc = -1; return rc; } @@ -197,52 +172,40 @@ main(void) bin = absbin; } - char cdrv[640], wdrv[640]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); char cw6c[640], ww6c[640]; snprintf(cw6c, sizeof cw6c, "%s/w6c", bin); snprintf(ww6c, sizeof ww6c, "%s/w6c_ww", bin); - int have_ww = (access(wdrv, X_OK) == 0) - && (access(ww6c, X_OK) == 0); + int have_ww = access(ww6c, X_OK) == 0; int n = (int)(sizeof rows / sizeof rows[0]); int total = 0, fail = 0; + int cleanfail = 0; for (int i = 0; i < n; i++) { - /* cstage RC */ - total++; - int got_c = build_and_run(cdrv, &rows[i], i); - if (got_c != 0) { - fprintf(stderr, - "alias_chain[cstage][%s]: exit=%d want=0\n", - rows[i].label, got_c); - fail++; - } - if (!have_ww) continue; - /* wwstage RC */ - total++; - int got_w = build_and_run(wdrv, &rows[i], i); - if (got_w != 0) { - fprintf(stderr, - "alias_chain[wwstage][%s]: exit=%d want=0\n", - rows[i].label, got_w); - fail++; - } - /* byte-id cstage vs wwstage */ total++; char cs_path[160], ws_path[160]; - if (emit_s(cw6c, &rows[i], i, cs_path, sizeof cs_path) != 0 - || emit_s(ww6c, &rows[i], i, ws_path, sizeof ws_path) != 0) { + /* legs split: on a first-leg failure ws_path is never + * filled in, so it must never reach unlink. */ + if (emit_s(cw6c, &rows[i], i, cs_path, sizeof cs_path) != 0) { fprintf(stderr, "alias_chain[byte-id][%s]: emit failed\n", rows[i].label); fail++; - unlink(cs_path); unlink(ws_path); + continue; + } + if (emit_s(ww6c, &rows[i], i, ws_path, sizeof ws_path) != 0) { + fprintf(stderr, + "alias_chain[byte-id][%s]: emit failed\n", + rows[i].label); + fail++; + if (unlink(cs_path) != 0 && errno != ENOENT) { + perror(cs_path); + cleanfail = 1; + } continue; } char cmpcmd[512]; @@ -254,7 +217,21 @@ main(void) rows[i].label); fail++; } - unlink(cs_path); unlink(ws_path); + if (unlink(cs_path) != 0 && errno != ENOENT) { + perror(cs_path); + cleanfail = 1; + } + if (unlink(ws_path) != 0 && errno != ENOENT) { + perror(ws_path); + cleanfail = 1; + } + } + + /* a cleanup failure must not pass silently, but must not mask a + * real assertion failure's own report either. */ + if (cleanfail && fail == 0) { + fprintf(stderr, "alias_chain: cleanup failed\n"); + return 1; } if (fail) { diff --git a/test/wcc/758_cgalloc_str_field.c b/test/wcc/758_cgalloc_str_field.c index c50c8ae2..28a232ed 100644 --- a/test/wcc/758_cgalloc_str_field.c +++ b/test/wcc/758_cgalloc_str_field.c @@ -27,9 +27,9 @@ * combines lib/rt/malloc.ww into the fixture and would always supply * the @symbol decl, masking the regression. * - * The runtime `rows` below run the generated binary under cstage and - * (when present) wwstage; the exit code is the regression-catching - * gate. The cstage row is the cross-check oracle. + * The runtime rows moved to the test/wcc/data/r75_alloc_{str_singleton, + * i32_then_str,str_then_i32,two_str,f64_str_i32,str_readback} fixtures; + * this carrier retains only the asm-line checks below. * * Full asm byte-identity is intentionally NOT checked here — the * `(BX)` (cstage txt.c omits zero displacement) vs `0(BX)` (wwstage @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -54,119 +55,6 @@ runwait(const char *cmd) return -1; } -struct row { const char *label; const char *src; int want; }; - -static const struct row rows[] = { - /* Singleton str field. Pre-fix p.s.len stayed 0; post-fix 5. */ - { "alloc_str_singleton", - "package main;\n" - "import rt;\n" - "type holder = struct { s: str };\n" - "fn main() i32 = {\n" - " let p: *holder = alloc(holder { s = \"hello\" })!;\n" - " return p.s.len;\n" - "};\n", - 5 }, - /* i32 field before str. Verifies field iteration restarts cleanly - * after the str-field branch and that the i32 store path is - * unaffected. Pre-fix: 100 + 0 = 100; post-fix: 100 + 2 = 102. */ - { "alloc_i32_then_str", - "package main;\n" - "import rt;\n" - "type holder = struct { n: i32, s: str };\n" - "fn main() i32 = {\n" - " let p: *holder = alloc(holder { n = 100, s = \"hi\" })!;\n" - " return p.n + p.s.len;\n" - "};\n", - 102 }, - /* str field before i32. Verifies foff>0 routing for the trailing - * i32 store after the str-field branch. Pre-fix: 0 + 7 = 7; - * post-fix: 5 + 7 = 12. */ - { "alloc_str_then_i32", - "package main;\n" - "import rt;\n" - "type holder = struct { s: str, n: i32 };\n" - "fn main() i32 = {\n" - " let p: *holder = alloc(holder { s = \"world\", n = 7 })!;\n" - " return p.s.len + p.n;\n" - "};\n", - 12 }, - /* Two str fields. Pre-fix both lens stayed 0; post-fix 3 + 6 = 9. - * Pins that `fi = nil` advances correctly between str fields. */ - { "alloc_two_str", - "package main;\n" - "import rt;\n" - "type holder = struct { a: str, b: str };\n" - "fn main() i32 = {\n" - " let p: *holder = alloc(holder { a = \"foo\", b = \"barbaz\" })!;\n" - " return p.a.len + p.b.len;\n" - "};\n", - 9 }, - /* Float-then-str-then-int. Pre-fix str.len=0 so result is - * 0 + 10 = 10; post-fix 3 + 10 = 13. Confirms the str-field - * branch slots between the existing float and integer branches - * without disrupting either. */ - { "alloc_f64_str_i32", - "package main;\n" - "import rt;\n" - "type holder = struct { f: f64, s: str, n: i32 };\n" - "fn main() i32 = {\n" - " let p: *holder = alloc(holder { f = 1.5f64, s = \"abc\", n = 10 })!;\n" - " return p.s.len + p.n;\n" - "};\n", - 13 }, - /* Read-back via p.s[i]: confirms str.ptr survived the store - * (foff=0 case) while still pinning str.len. Pre-fix len=0 so - * the loop body never executes; post-fix sum is 'h'+'i' = 209. - * Indexes the string by hand to avoid pulling in stdlib. */ - { "alloc_str_readback", - "package main;\n" - "import rt;\n" - "type holder = struct { s: str };\n" - "fn main() i32 = {\n" - " let p: *holder = alloc(holder { s = \"hi\" })!;\n" - " let sum: i32 = 0;\n" - " let i: i32 = 0;\n" - " for (i < p.s.len) {\n" - " sum += p.s[i]: i32;\n" - " i += 1;\n" - " };\n" - " return sum;\n" - "};\n", - 209 }, -}; - -/* run_driver — compile r->src via the given driver and exec; return - * the process exit code. Mirror of 703/704. */ -static int -run_driver(const char *driver, const struct row *r, int i) -{ - char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/wcas_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/wcas_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/wcas_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); - - FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return -1; } - fputs(r->src, f); - fclose(f); - - snprintf(cmd, sizeof cmd, "%s build -o %s %s", driver, outbin, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: build via %s failed\n", - r->label, driver); - runwait(rmcmd); - return -1; - } - - int got = runwait(outbin); - - runwait(rmcmd); - return got; -} - /* asm_row — fixture for the ffiresolve CALL-line check. `want_sym` * is the unqualified symbol expected inside `CALL\t(SB)` from * both stages' .s output. Single-file `w6c` compile (no `ww build` @@ -287,15 +175,15 @@ asm_call_check(const char *bin, const struct asm_row *r, int i) snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src); if (runwait(cmd) != 0) { fprintf(stderr, "asm[%s]: w6c errored\n", r->label); - unlink(src); - return -1; + rc = -1; + goto cleanup; } snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null", bin, ws, src); if (runwait(cmd) != 0) { fprintf(stderr, "asm[%s]: w6c_ww errored\n", r->label); - unlink(src); unlink(cs); - return -1; + rc = -1; + goto cleanup; } char want[64]; @@ -351,7 +239,25 @@ asm_call_check(const char *bin, const struct asm_row *r, int i) } if (fc) fclose(fc); if (fw) fclose(fw); - unlink(src); unlink(cs); unlink(ws); + +cleanup:; + /* a failing compile can still leave a partial .s — ENOENT is the + * only tolerable unlink error on the never-created legs. */ + int cleanfail = 0; + if (unlink(src) != 0 && errno != ENOENT) { + perror(src); + cleanfail = 1; + } + if (unlink(cs) != 0 && errno != ENOENT) { + perror(cs); + cleanfail = 1; + } + if (unlink(ws) != 0 && errno != ENOENT) { + perror(ws); + cleanfail = 1; + } + if (cleanfail && rc == 0) + rc = -1; return rc; } @@ -398,15 +304,15 @@ asm_disp_check(const char *bin, const struct asm_disp_row *r, int i) snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src); if (runwait(cmd) != 0) { fprintf(stderr, "disp[%s]: w6c errored\n", r->label); - unlink(src); - return -1; + rc = -1; + goto cleanup; } snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null", bin, ws, src); if (runwait(cmd) != 0) { fprintf(stderr, "disp[%s]: w6c_ww errored\n", r->label); - unlink(src); unlink(cs); - return -1; + rc = -1; + goto cleanup; } int chit = file_contains(cs, r->want_line); @@ -419,7 +325,24 @@ asm_disp_check(const char *bin, const struct asm_disp_row *r, int i) rc = -1; } - unlink(src); unlink(cs); unlink(ws); +cleanup:; + /* a failing compile can still leave a partial .s — ENOENT is the + * only tolerable unlink error on the never-created legs. */ + int cleanfail = 0; + if (unlink(src) != 0 && errno != ENOENT) { + perror(src); + cleanfail = 1; + } + if (unlink(cs) != 0 && errno != ENOENT) { + perror(cs); + cleanfail = 1; + } + if (unlink(ws) != 0 && errno != ENOENT) { + perror(ws); + cleanfail = 1; + } + if (cleanfail && rc == 0) + rc = -1; return rc; } @@ -436,41 +359,8 @@ main(void) bin = absbin; } - char cdrv[1024]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - char wdrv[1024]; - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - - struct { const char *name; const char *path; int gated_on_existence; } - drivers[] = { - { "cstage", cdrv, 0 }, - { "wwstage", wdrv, 1 }, - { NULL, NULL, 0 }, - }; - - int n = (int)(sizeof rows / sizeof rows[0]); int total = 0, fail = 0; - for (int d = 0; drivers[d].name; d++) { - if (drivers[d].gated_on_existence - && access(drivers[d].path, X_OK) != 0) { - fprintf(stderr, "cgalloc_str_field: skip %s (no %s)\n", - drivers[d].name, drivers[d].path); - continue; - } - for (int i = 0; i < n; i++) { - int got = run_driver(drivers[d].path, &rows[i], i); - total++; - if (got != rows[i].want) { - fprintf(stderr, - "cgalloc_str_field[%s][%s]: exit=%d want=%d\n", - drivers[d].name, rows[i].label, - got, rows[i].want); - fail++; - } - } - } - /* Asm CALL-line check via direct w6c / w6c_ww. Gated on w6c_ww * existence — when wwstage isn't built yet the cstage half alone * can't catch the divergence. */ diff --git a/test/wcc/760_def_widen_const.c b/test/wcc/760_def_widen_const.c index 28cf1f21..78e02a15 100644 --- a/test/wcc/760_def_widen_const.c +++ b/test/wcc/760_def_widen_const.c @@ -23,12 +23,8 @@ * folded value, so the emitted asm is byte-identical. * * Coverage: - * 1. EXEC (cstage `ww`, + wwstage `ww_ww` if built): i32 → int (a - * negative value, to pin sign-extension across the widen) and - * u64 → size (a value above the u32 range, to pin the full 8-byte - * slot) — each read back at a use site to prove the DATA row links - * and carries the right value. - * 2. BYTE-ID: cstage w6c vs wwstage w6c_ww `.s` for every exec row + * 1. The r76_def_widen_* fixtures own symmetric build/runtime semantics. + * 2. BYTE-ID: cstage w6c vs wwstage w6c_ww `.s` for every fixture row * (proves the const-fold stamp is bit-identical across stages). * 3. FAIL-LOUD (rule 7), cstage only: an out-of-range narrowing * def-ref (`def S: i16 = BIG;` where BIG overflows i16) must still @@ -53,81 +49,17 @@ runwait(const char *cmd) return -1; } -/* ---- 1. EXEC rows (compile+link+run) -------------------------------- */ - -struct row { const char *label; const char *src; int want; }; +struct row { const char *label; const char *path; }; static const struct row exec_rows[] = { - /* i32 → int widening of a negative value: `def INT_MIN`-shaped. - * I32_MIN = -2147483648; read back as int it must sign-extend to - * the full 8-byte slot. main returns 42 iff the value round-trips. */ { "i32-to-int-neg", - "def A_I32: i32 = -2147483648;\n" - "def A_INT: int = A_I32;\n" - "fn main() i32 = {\n" - "\tlet x: int = A_INT;\n" - "\tif (x == -2147483648) { return 42; };\n" - "\treturn 1;\n" - "};\n", - 42 }, - - /* small negative i32 → int, to pin -7 specifically (the task's - * worked example). */ + "test/wcc/data/r76_def_widen_i32_int_neg/case.ww" }, { "i32-to-int-small", - "def A_I32: i32 = -7;\n" - "def A_INT: int = A_I32;\n" - "fn main() i32 = {\n" - "\tlet x: int = A_INT;\n" - "\tif (x == -7) { return 42; };\n" - "\treturn 1;\n" - "};\n", - 42 }, - - /* u64 → size widening of a value above the u32 range: pins the - * full 8-byte slot survives the widen (0x1_0000_0003 = 4294967299). */ + "test/wcc/data/r76_def_widen_i32_int_small/case.ww" }, { "u64-to-size", - "def BIG_U64: u64 = 4294967299;\n" - "def S_SIZE: size = BIG_U64;\n" - "fn main() i32 = {\n" - "\tlet x: size = S_SIZE;\n" - "\tif (x == 4294967299) { return 42; };\n" - "\treturn 1;\n" - "};\n", - 42 }, + "test/wcc/data/r76_def_widen_u64_size/case.ww" }, }; -static int -run_driver(const char *driver, const struct row *r, int i) -{ - char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/dwc_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/dwc_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/dwc_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); - - FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return -1; } - wwtest_fputs(r->src, f); - fclose(f); - - /* timeout 180 per repo convention; test/run does not bound - * individual binaries, so an unguarded hang would stall make test. */ - snprintf(cmd, sizeof cmd, "timeout 180 %s build -o %s %s 2>/dev/null", - driver, outbin, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: build via %s failed\n", - r->label, driver); - runwait(rmcmd); - return -1; - } - - int got = runwait(outbin); - - runwait(rmcmd); - return got; -} - /* ---- shared .s emit -------------------------------------------------- */ static int @@ -143,30 +75,23 @@ emit_s(const char *w6c, const char *src, char *out_s, size_t cap) /* ---- 2. byte-identity of cstage vs wwstage .s ----------------------- */ static int -asm_byte_identical(const char *bin, const char *src, const char *label, int i) +asm_byte_identical(const char *bin, const struct row *r, int i) { - char wwsrc[64], cs[64], ws[64]; - snprintf(wwsrc, sizeof wwsrc, "/tmp/dwc_bi_%d_%d.ww", getpid(), i); + char cs[64], ws[64]; snprintf(cs, sizeof cs, "/tmp/dwc_bi_%d_%d_c.s", getpid(), i); snprintf(ws, sizeof ws, "/tmp/dwc_bi_%d_%d_w.s", getpid(), i); - FILE *f = fopen(wwsrc, "wb"); - if (!f) return -1; - wwtest_fputs(src, f); - fclose(f); - char w6c[640], w6c_ww[640]; snprintf(w6c, sizeof w6c, "%s/w6c", bin); snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin); - if (emit_s(w6c, wwsrc, cs, sizeof cs) != 0) { - fprintf(stderr, "row[%s]: w6c errored\n", label); - unlink(wwsrc); + if (emit_s(w6c, r->path, cs, sizeof cs) != 0) { + fprintf(stderr, "row[%s]: w6c errored\n", r->label); return -1; } - if (emit_s(w6c_ww, wwsrc, ws, sizeof ws) != 0) { - fprintf(stderr, "row[%s]: w6c_ww errored\n", label); - unlink(wwsrc); unlink(cs); + if (emit_s(w6c_ww, r->path, ws, sizeof ws) != 0) { + fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label); + unlink(cs); return -1; } @@ -187,8 +112,8 @@ asm_byte_identical(const char *bin, const char *src, const char *label, int i) if (fw) fclose(fw); if (rc != 0) fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n", - label); - unlink(wwsrc); unlink(cs); unlink(ws); + r->label); + unlink(cs); unlink(ws); return rc; } @@ -246,48 +171,19 @@ main(void) bin = absbin; } - char cdrv[1024], wdrv[1024], w6c[1024], w6c_ww[1024]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); + char w6c[1024], w6c_ww[1024]; snprintf(w6c, sizeof w6c, "%s/w6c", bin); snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin); int have_ww = (access(w6c_ww, X_OK) == 0); int total = 0, fail = 0; - /* 1. exec via cstage `ww` (+ wwstage `ww_ww` if built) */ - struct { const char *name; const char *path; int gated; } - drivers[] = { - { "cstage", cdrv, 0 }, - { "wwstage", wdrv, 1 }, - { NULL, NULL, 0 }, - }; int nexec = (int)(sizeof exec_rows / sizeof exec_rows[0]); - for (int d = 0; drivers[d].name; d++) { - if (drivers[d].gated && access(drivers[d].path, X_OK) != 0) { - fprintf(stderr, "def_widen_const: skip %s (no %s)\n", - drivers[d].name, drivers[d].path); - continue; - } - for (int i = 0; i < nexec; i++) { - int got = run_driver(drivers[d].path, &exec_rows[i], i); - total++; - if (got != exec_rows[i].want) { - fprintf(stderr, - "def_widen_const[%s][%s]: exit=%d want=%d\n", - drivers[d].name, exec_rows[i].label, - got, exec_rows[i].want); - fail++; - } - } - } - - /* 2. byte-id on every exec row (only when wwstage is built) */ + /* Runtime rows moved to r76_def_widen_*; retain byte-id and cstage reject. */ if (have_ww) { for (int i = 0; i < nexec; i++) { total++; - if (asm_byte_identical(bin, exec_rows[i].src, - exec_rows[i].label, i) != 0) + if (asm_byte_identical(bin, &exec_rows[i], i) != 0) fail++; } } diff --git a/test/wcc/764_amp_fn_ident.c b/test/wcc/764_amp_fn_ident.c index d752d783..d78b11cb 100644 --- a/test/wcc/764_amp_fn_ident.c +++ b/test/wcc/764_amp_fn_ident.c @@ -40,16 +40,13 @@ * 5. fn-with-tuple-return — (i64, str) return shape * 6. cross-module — `let f = &pkg.fn` (Phase 1 = FINE) * - * Gates per row: - * a. cstage builds + runs (exit 0); proves the LEAQ emits and - * frame layout survives the AX-store (pre-fix this was junk - * data, not necessarily a segfault — the segfault only fired - * on the deref-call). - * b. cstage .s contains the expected `LEAQ (SB)` line — + * Runtime for the five symmetric rows now lives in r764_* fixtures. + * This artifact wrapper retains: + * a. cstage .s contains the expected `LEAQ (SB)` line — * pre-fix that line is absent (silent drop). - * c. wwstage builds + runs, if wwstage driver present. - * d. cstage .s == wwstage .s byte-identical — symmetry gate per + * b. cstage .s == wwstage .s byte-identical — symmetry gate per * CLAUDE.md rule 10. + * c. the cross-module C-stage-only runtime gate pending #184. * * GATE POLARITY: must stay GREEN. A red here means either the cgen * TK_AMP IDENT TY_FN arm regressed, or stage symmetry drifted. @@ -57,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -79,6 +77,8 @@ runwait(const char *cmd) struct row { const char *label; + /* Symmetric rows use their corpus source as the sole fixture body. */ + const char *fixture; /* Source written into /.ww; for cross-mod * (row 6) the secondary module lives at // * .ww. modname/modsrc NULL for single-file rows. */ @@ -94,51 +94,32 @@ struct row { static const struct row rows[] = { { "minimal", - "fn add1(x: i32) i32 = { return x + 1; };\n" - "export fn main() i32 = {\n" - " let f = &add1;\n" - " return 0;\n" - "};\n", + "test/wcc/data/r764_amp_fn_ident_minimal/case.ww", + NULL, NULL, NULL, "LEAQ\tmain.add1(SB)", STAGE_CS | STAGE_WW }, { "branched_callee", - "fn aa(x: i32) i32 = { return x; };\n" - "fn bb(x: i32) i32 = { return x + 1; };\n" - "export fn main() i32 = {\n" - " let pick: i32 = 1;\n" - " let f = &aa;\n" - " if (pick != 0) { f = &bb; };\n" - " return 0;\n" - "};\n", + "test/wcc/data/r764_amp_fn_ident_branched/case.ww", + NULL, NULL, NULL, "LEAQ\tmain.bb(SB)", STAGE_CS | STAGE_WW }, { "alias_chain", - "fn add1(x: i32) i32 = { return x + 1; };\n" - "export fn main() i32 = {\n" - " let f = &add1;\n" - " let g = f;\n" - " return 0;\n" - "};\n", + "test/wcc/data/r764_amp_fn_ident_alias_chain/case.ww", + NULL, NULL, NULL, "LEAQ\tmain.add1(SB)", STAGE_CS | STAGE_WW }, { "fn_with_args", - "fn many(a: i32, b: i64, p: *i32) i64 = { return b; };\n" - "export fn main() i32 = {\n" - " let f = &many;\n" - " return 0;\n" - "};\n", + "test/wcc/data/r764_amp_fn_ident_args/case.ww", + NULL, NULL, NULL, "LEAQ\tmain.many(SB)", STAGE_CS | STAGE_WW }, { "fn_tuple_return", - "fn pair() (i64, str) = { return (7: i64, \"x\"); };\n" - "export fn main() i32 = {\n" - " let f = &pair;\n" - " return 0;\n" - "};\n", + "test/wcc/data/r764_amp_fn_ident_tuple_return/case.ww", + NULL, NULL, NULL, "LEAQ\tmain.pair(SB)", STAGE_CS | STAGE_WW }, @@ -151,6 +132,7 @@ static const struct row rows[] = { * &-of hint routes through use_hint and the LEAQ matches the * mangled definition. */ { "cross_module", + NULL, "import wcamffn764mod;\n" "export fn main() i32 = {\n" " let f = &wcamffn764mod.somefn;\n" @@ -163,73 +145,125 @@ static const struct row rows[] = { STAGE_CS }, }; -/* write_sources — writes the row's main source plus, for cross-mod - * rows, the secondary module file. Returns the basename of the main - * source (without .ww) via *base_out. */ +static int cleanup_sources(const struct row *, const char *, const char *, int); + +/* write_sources — copies a migrated fixture into an isolated package-main + * root, or writes the one asymmetric cross-module row into that tree. */ static int write_sources(const struct row *r, char *src, size_t srcsz, char *tmpdir, size_t tdsz, int seq, char *base_out, size_t basz) { - snprintf(tmpdir, tdsz, "/tmp/wcamffn_%d_d_%d", getpid(), seq); + (void)seq; + int moddir_owned = 0; + snprintf(tmpdir, tdsz, "/tmp/wcamffn_XXXXXX"); + if (mkdtemp(tmpdir) == NULL) return -1; snprintf(src, srcsz, "%s/main764.ww", tmpdir); snprintf(base_out, basz, "main764"); - mkdir(tmpdir, 0755); + if (r->fixture != NULL) { + FILE *in = fopen(r->fixture, "rb"); + if (in == NULL) goto fail; + if (fseek(in, 0, SEEK_END) != 0) { fclose(in); goto fail; } + long n = ftell(in); + if (n < 0 || fseek(in, 0, SEEK_SET) != 0) { + fclose(in); + goto fail; + } + char *body = malloc((size_t)n + 1); + if (body == NULL) { fclose(in); goto fail; } + size_t got = fread(body, 1, (size_t)n, in); + int readerr = ferror(in); + fclose(in); + if (got != (size_t)n || readerr) { free(body); goto fail; } + body[got] = '\0'; + + FILE *out = fopen(src, "wb"); + if (out == NULL) { free(body); goto fail; } + wwtest_fputs(body, out); + int wr = ferror(out) ? -1 : 0; + free(body); + if (fclose(out) != 0) wr = -1; + if (wr != 0) goto fail; + return 0; + } if (r->modname != NULL) { char moddir[256], modfile[256]; snprintf(moddir, sizeof moddir, "%s/%s", tmpdir, r->modname); snprintf(modfile, sizeof modfile, "%s/%s.ww", moddir, r->modname); - mkdir(moddir, 0755); + if (mkdir(moddir, 0755) != 0) goto fail; + moddir_owned = 1; FILE *mf = fopen(modfile, "wb"); - if (!mf) return -1; + if (!mf) goto fail; wwtest_fputs(r->modsrc, mf); - fclose(mf); + int wr = ferror(mf) ? -1 : 0; + if (fclose(mf) != 0) wr = -1; + if (wr != 0) goto fail; } FILE *f = fopen(src, "wb"); - if (!f) return -1; + if (!f) goto fail; wwtest_fputs(r->src, f); - fclose(f); + int wr = ferror(f) ? -1 : 0; + if (fclose(f) != 0) wr = -1; + if (wr != 0) goto fail; return 0; + +fail: + if (cleanup_sources(r, tmpdir, "main764", moddir_owned) != 0) + fprintf(stderr, "amp_fn_ident: setup cleanup failed for %s\n", tmpdir); + return -1; } -static void -cleanup_sources(const struct row *r, const char *tmpdir, const char *base) +static int +cleanup_sources(const struct row *r, const char *tmpdir, const char *base, + int moddir_owned) { char p[512]; - snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "%s/%s.s", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "%s/%s.o", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "%s/%s", tmpdir, base); unlink(p); - /* #93: the sep scratch dir + the tmpdir-pinned pkgcache. */ - snprintf(p, sizeof p, "rm -rf %s/%s.sepwork %s/pkgc", tmpdir, base, tmpdir); - if (system(p) != 0) { /* best-effort */ } - if (r->modname != NULL) { + int rc = 0; + snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); + if (unlink(p) != 0 && errno != ENOENT) rc = -1; + snprintf(p, sizeof p, "%s/%s", tmpdir, base); + if (unlink(p) != 0 && errno != ENOENT) rc = -1; + /* #93: the sep scratch dir + the tmpdir-owned outputs. */ + snprintf(p, sizeof p, "rm -rf %s/%s.sepwork", tmpdir, base); + if (runwait(p) != 0) rc = -1; + if (moddir_owned) { char moddir[256]; snprintf(moddir, sizeof moddir, "%s/%s", tmpdir, r->modname); - snprintf(p, sizeof p, "%s/%s.ww", moddir, r->modname); unlink(p); - rmdir(moddir); + snprintf(p, sizeof p, "%s/%s.ww", moddir, r->modname); + if (unlink(p) != 0 && errno != ENOENT) rc = -1; + if (rmdir(moddir) != 0 && errno != ENOENT) rc = -1; } - rmdir(tmpdir); + if (rmdir(tmpdir) != 0) rc = -1; + return rc; +} + +/* compile_via_driver — runs compiler-only driver output in ; returns + * the compiler exit code without assembling or linking. */ +static int +compile_via_driver(const char *driver, const char *tmpdir, const char *src) +{ + /* #93 sep layout: `-o ` emits the asm to + * .sepwork/__root.s (the post-flip layout); all outputs stay + * under tmpdir. */ + char cmd[2048]; + snprintf(cmd, sizeof cmd, + "cd %s && timeout 180 %s build -S -o %s/main764 %s " + "2>/dev/null", tmpdir, driver, tmpdir, src); + return runwait(cmd); } -/* build_via_driver — runs ` build ` in ; returns - * the build exit code. */ static int build_via_driver(const char *driver, const char *tmpdir, const char *src) { - /* #93 sep layout: `--sep -o ` emits the asm to - * .sepwork/__root.s (the post-flip layout); WW_PKGCACHE is - * pinned under tmpdir so the shared out/.pkgcache is untouched and - * the cache dies with the tmpdir. */ char stem[512], cmd[1280]; snprintf(stem, sizeof stem, "%s", src); char *dot = strrchr(stem, '.'); if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; snprintf(cmd, sizeof cmd, - "cd %s && WW_PKGCACHE=%s/pkgc timeout 180 %s build --sep -o %s %s " - "2>/dev/null", tmpdir, tmpdir, driver, stem, src); + "cd %s && timeout 180 %s build -o %s %s " + "2>/dev/null", tmpdir, driver, stem, src); return runwait(cmd); } @@ -247,7 +281,10 @@ run_row(const char *driver, const struct row *r, int seq) snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); if (runwait(outbin) == 0) rc = 0; } - cleanup_sources(r, tmpdir, base); + if (cleanup_sources(r, tmpdir, base, r->modname != NULL) != 0) { + fprintf(stderr, "amp_fn_ident[%s]: cleanup failed\n", r->label); + rc = -1; + } return rc; } @@ -277,11 +314,14 @@ check_leaq(const char *driver, const struct row *r, int seq) seq, base, sizeof base) != 0) return -1; int rc = -1; - if (build_via_driver(driver, tmpdir, src) == 0) { + if (compile_via_driver(driver, tmpdir, src) == 0) { snprintf(asmf, sizeof asmf, "%s/%s.sepwork/__root.s", tmpdir, base); if (file_contains(asmf, r->expected_leaq)) rc = 0; } - cleanup_sources(r, tmpdir, base); + if (cleanup_sources(r, tmpdir, base, r->modname != NULL) != 0) { + fprintf(stderr, "amp_fn_ident[%s]: cleanup failed\n", r->label); + rc = -1; + } return rc; } @@ -295,17 +335,17 @@ asm_byte_identical(const char *cdrv, const char *wdrv, seq, base, sizeof base) != 0) return -1; int rc = -1; - if (build_via_driver(cdrv, tdc, src) != 0) goto out; + int have_tdw = 0; + if (compile_via_driver(cdrv, tdc, src) != 0) goto out; snprintf(cs, sizeof cs, "%s/%s.sepwork/__root.s", tdc, base); - /* Build a parallel tree for wwstage so we don't clobber the - * cstage .s. ww_ww writes intermediates next to the .ww source - * (filed bug per CLAUDE.md rule 14 phase split). */ + /* Build a parallel tree so each driver's explicitly retained + * .sepwork/__root.s remains available for comparison. */ if (write_sources(r, src, sizeof src, tdw, sizeof tdw, seq + 100000, base, sizeof base) != 0) goto out; - if (build_via_driver(wdrv, tdw, src) != 0) { - cleanup_sources(r, tdw, base); + have_tdw = 1; + if (compile_via_driver(wdrv, tdw, src) != 0) { goto out; } snprintf(ws, sizeof ws, "%s/%s.sepwork/__root.s", tdw, base); @@ -323,9 +363,16 @@ asm_byte_identical(const char *cdrv, const char *wdrv, } if (fc) fclose(fc); if (fw) fclose(fw); - cleanup_sources(r, tdw, base); out: - cleanup_sources(r, tdc, base); + if (have_tdw + && cleanup_sources(r, tdw, base, r->modname != NULL) != 0) { + fprintf(stderr, "amp_fn_ident[%s]: ww cleanup failed\n", r->label); + rc = -1; + } + if (cleanup_sources(r, tdc, base, r->modname != NULL) != 0) { + fprintf(stderr, "amp_fn_ident[%s]: c cleanup failed\n", r->label); + rc = -1; + } return rc; } @@ -353,7 +400,10 @@ main(void) int seq = 0; for (int i = 0; i < n; i++) { - if (rows[i].stage_mask & STAGE_CS) { + /* Symmetric runtime rows are owned by r764_* fixtures. Keep + * only the cross-module C-stage-only runtime evidence here. */ + if ((rows[i].stage_mask & STAGE_CS) + && !(rows[i].stage_mask & STAGE_WW)) { total++; if (run_row(cdrv, &rows[i], seq++) != 0) { fprintf(stderr, @@ -361,6 +411,8 @@ main(void) rows[i].label); fail++; } + } + if (rows[i].stage_mask & STAGE_CS) { total++; if (check_leaq(cdrv, &rows[i], seq++) != 0) { fprintf(stderr, @@ -370,13 +422,6 @@ main(void) } } if (wwpresent && (rows[i].stage_mask & STAGE_WW)) { - total++; - if (run_row(wdrv, &rows[i], seq++) != 0) { - fprintf(stderr, - "amp_fn_ident[wwstage run][%s]: build/run failed\n", - rows[i].label); - fail++; - } total++; if (asm_byte_identical(cdrv, wdrv, &rows[i], seq++) != 0) { fprintf(stderr, diff --git a/test/wcc/765_star_fn_deref.c b/test/wcc/765_star_fn_deref.c index e5bdf75d..6394026b 100644 --- a/test/wcc/765_star_fn_deref.c +++ b/test/wcc/765_star_fn_deref.c @@ -57,6 +57,7 @@ */ #include #include +#include #include #include #include @@ -146,16 +147,24 @@ write_source(const char *src_path, const char *src) FILE *f = fopen(src_path, "wb"); if (!f) return -1; wwtest_fputs(src, f); - fclose(f); - return 0; + int rc = ferror(f) ? -1 : 0; + if (fclose(f) != 0) rc = -1; + return rc; } -static void +static int cleanup_tmp(const char *tmpdir) { char p[512]; - snprintf(p, sizeof p, "rm -rf %s", tmpdir); - runwait(p); + int rc = 0; + snprintf(p, sizeof p, "%s/main765.ww", tmpdir); + if (unlink(p) != 0 && errno != ENOENT) rc = -1; + snprintf(p, sizeof p, "%s/main765", tmpdir); + if (unlink(p) != 0 && errno != ENOENT) rc = -1; + snprintf(p, sizeof p, "rm -rf %s/main765.sepwork", tmpdir); + if (runwait(p) != 0) rc = -1; + if (rmdir(tmpdir) != 0) rc = -1; + return rc; } static int @@ -172,19 +181,23 @@ build_via_driver(const char *driver, const char *outbin, const char *src) static int run_row(const char *driver, const struct row *r, int seq) { - char tmpdir[256], src[256], outbin[512]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/wcsfd_%d_d_%d", getpid(), seq); + (void)seq; + char tmpdir[] = "/tmp/wcsfd_XXXXXX"; + char src[256], outbin[512]; + if (mkdtemp(tmpdir) == NULL) return -1; snprintf(src, sizeof src, "%s/main765.ww", tmpdir); snprintf(outbin, sizeof outbin, "%s/main765", tmpdir); - mkdir(tmpdir, 0755); - if (write_source(src, r->src) != 0) { - cleanup_tmp(tmpdir); - return -1; - } int rc = -1; + if (write_source(src, r->src) != 0) { + goto out; + } if (build_via_driver(driver, outbin, src) == 0) rc = runwait(outbin); - cleanup_tmp(tmpdir); +out: + if (cleanup_tmp(tmpdir) != 0) { + fprintf(stderr, "star_fn_deref[%s]: cleanup failed\n", r->label); + rc = -1; + } return rc; } diff --git a/test/wcc/771_widen_transitive.c b/test/wcc/771_widen_transitive.c index 8938ced1..dada6599 100644 --- a/test/wcc/771_widen_transitive.c +++ b/test/wcc/771_widen_transitive.c @@ -1,96 +1,27 @@ /* - * 771_widen_transitive — project #199 (α): cstage type_assignable + - * wwstage isassignable must REJECT transitive nested-tagged widen. - * Cgen has no wrapped-slot layout (#199b deferred future-work); the - * pre-α recursive `type_assignable(p->type, src)` walk admitted - * `let r: (size|io.eof|io.error) = u` for u: io.underread, then cgen - * silently miscompiled (cg_tag_for_variant returned -1 → tag=0 → r - * reads as variant 0 = size at runtime). + * 771_widen_transitive — residual artifact owner. * - * Fix (cmd/wcc/type.c:308 + selfhost/cmd/wcc/check.ww:3063): the - * tagged-non-tagged arm now matches direct variants ONLY. NAMED-tagged - * wrapper variants (vu.kind == TY_TAGGED) compare nominally (type_eq / - * typeeqast); other variants keep the leaf recursion for primitive - * compat / untyped resolution. wwstage additionally preserves the - * spread-recursion path: `...wrapper` variants (op == TK_ELLIPSIS) drill - * through because the AST list hasn't pre-flattened (cstage flattens at - * resolve_type, wwstage stays AST-keyed). - * - * Restores SSoT inside the checker pair: `is` / `as` / `match` arm - * lookup is already non-recursive (#198 sibling), and the LET-init / - * return / assign arms now agree. Aligns DOWN to the leaner side - * (rule-10 stage symmetry). ww-stricter than Hare; harec keeps the - * drill at ref/harec/src/types.c:702-739 (#199b — wrapped-slot layout - * port deferred per drew). - * - * Coverage (5 rows): - * 1. reject_transitive_widen — `let r: (size|wrapper) = u` with - * u: underread, wrapper = !(underread | nomem). BOTH stages - * MUST reject at the checker. The repro. - * 2. spread_alt_widen — `let r: (size|...wrapper) = u;`. The - * `...` spread inlines wrapper's variants into the parent flat - * set, so underread becomes a DIRECT variant; widen succeeds. - * CS-only runtime: wwstage's `is`/`match` cross-spread variant - * lookup is open-bug #190/#198 (wwstage walks original AST list, - * doesn't see expanded variants); byte-id still gates the asm. - * 3. direct_flat_variant — `let r: (size|underread|nomem) = u` - * with the underlying variants flat in the parent (no wrapper). - * Regression gate: direct widen unchanged by the fix. - * 4. branched_callee_widen — fn returns (size|underread|nomem), - * runtime picks between two flat-variant returns. Verifies the - * cg_widen_tagged_store scalar branch's tag synthesis is - * unchanged for direct widens. - * 5. wrapper_typed_widen — `let r: wrapper = u` with u: - * underread (DIRECT variant of wrapper). Regression gate: the - * single-level wrapper widen is the supported escape hatch and - * MUST continue working. - * - * Per-row gates: cstage compile + runtime, wwstage compile + runtime, - * cs.s == ww.s byte-identical (rule-10). Row 1 uses - * expected_exit = -2 to indicate "build must fail" — the driver - * invocation is expected to return non-zero from the checker. + * Symmetric runtime and rejection coverage lives in wwfixture under + * test/wcc/data/r771_*. This wrapper retains only compiler-output byte + * identity plus the intentionally C-stage-only spread runtime row. */ #include #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 - -#define EXPECT_REJECT (-2) - struct row { const char *label; const char *src; - int expected_exit; /* EXPECT_REJECT means build must fail */ - int stage_mask; - int byte_id; /* run cs.s == ww.s diff (works rows only) */ + const char *fixture; + int byte_id; + int cstage_run; + int expected_exit; }; static const struct row rows[] = { - { "reject_transitive_widen", - "package main;\n" - "type underread = !i32;\n" - "type nomem = !void;\n" - "type wrapper = !(underread | nomem);\n" - "export fn main() i32 = {\n" - " let u: underread;\n" - " let r: (size | wrapper) = u;\n" - " return 0;\n" - "};\n", - EXPECT_REJECT, - STAGE_CS | STAGE_WW, 0 }, { "spread_alt_widen", "package main;\n" "type underread = !i32;\n" @@ -102,145 +33,143 @@ static const struct row rows[] = { " if (r is underread) { return 70; };\n" " return 99;\n" "};\n", - 70, - STAGE_CS, 0 }, + NULL, 0, 1, 70 }, { "direct_flat_variant", - "package main;\n" - "type underread = !i32;\n" - "type nomem = !void;\n" - "export fn main() i32 = {\n" - " let u: underread;\n" - " let r: (size | underread | nomem) = u;\n" - " if (r is underread) { return 71; };\n" - " return 99;\n" - "};\n", - 71, - STAGE_CS | STAGE_WW, 1 }, + NULL, "test/wcc/data/r771_direct_flat_variant/case.ww", + 1, 0, 71 }, { "branched_callee_widen", - "package main;\n" - "type underread = !i32;\n" - "type nomem = !void;\n" - "fn pick(b: bool) (size | underread | nomem) = {\n" - " if (b) { let u: underread; return u; };\n" - " return 42: size;\n" - "};\n" - "export fn main() i32 = {\n" - " let r: (size | underread | nomem) = pick(true);\n" - " if (r is underread) { return 72; };\n" - " return 99;\n" - "};\n", - 72, - STAGE_CS | STAGE_WW, 1 }, + NULL, "test/wcc/data/r771_branched_callee_widen/case.ww", + 1, 0, 72 }, { "wrapper_typed_widen", - "package main;\n" - "type underread = !i32;\n" - "type nomem = !void;\n" - "type wrapper = !(underread | nomem);\n" - "export fn main() i32 = {\n" - " let u: underread;\n" - " let r: wrapper = u;\n" - " if (r is underread) { return 73; };\n" - " return 99;\n" - "};\n", - 73, - STAGE_CS | STAGE_WW, 1 }, + NULL, "test/wcc/data/r771_wrapper_typed_widen/case.ww", + 1, 0, 73 }, }; +static int +runwait(const char *cmd) +{ + int rc = system(cmd); + if (rc == -1) return -1; + if (WIFEXITED(rc)) return WEXITSTATUS(rc); + return -1; +} static int write_source(const char *path, const char *src) { FILE *f = fopen(path, "wb"); if (!f) return -1; - fputs(src, f); - fclose(f); - return 0; -} - -static void -cleanup_tmp(const char *tmpdir, const char *base) -{ - char p[512]; - snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "%s/%s.o", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "%s/%s", tmpdir, base); unlink(p); - /* #93: the sep scratch dir + the tmpdir-pinned pkgcache. */ - snprintf(p, sizeof p, "rm -rf %s/%s.sepwork %s/pkgc", tmpdir, base, tmpdir); - if (system(p)) {} /* best-effort */ - rmdir(tmpdir); + int rc = fputs(src, f) == EOF ? -1 : 0; + if (fclose(f) != 0) rc = -1; + return rc; } static int -build_via_driver(const char *driver, const char *tmpdir, const char *src) +materialize_source(const char *cwd, const struct row *r, const char *dst) { - char cmd[1024]; - /* #93 sep layout: emit asm to .sepwork/__root.s; pin - * WW_PKGCACHE under tmpdir so out/.pkgcache is untouched. */ + if (r->src) return write_source(dst, r->src); + char path[1024], buf[4096]; + snprintf(path, sizeof path, "%s/%s", cwd, r->fixture); + FILE *in = fopen(path, "rb"); + if (!in) return -1; + FILE *out = fopen(dst, "wb"); + if (!out) { fclose(in); return -1; } + int rc = 0; + for (;;) { + size_t n = fread(buf, 1, sizeof buf, in); + if (n != 0 && fwrite(buf, 1, n, out) != n) { rc = -1; break; } + if (n < sizeof buf) { + if (ferror(in)) rc = -1; + break; + } + } + if (fclose(out) != 0) rc = -1; + fclose(in); + return rc; +} + +static int +cleanup_tmp(const char *tmpdir, const char *base) +{ + char p[640]; + int rc = 0; + snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); + if (unlink(p) != 0 && errno != ENOENT) rc = -1; + snprintf(p, sizeof p, "%s/%s", tmpdir, base); + if (unlink(p) != 0 && errno != ENOENT) rc = -1; + snprintf(p, sizeof p, "rm -rf %s/%s.sepwork", tmpdir, base); + if (runwait(p) != 0) rc = -1; + if (rmdir(tmpdir) != 0) rc = -1; + return rc; +} + +static int +build_via_driver(const char *driver, const char *tmpdir, const char *cwd, + const char *src, int asm_only) +{ + char cmd[2048]; + (void)cwd; snprintf(cmd, sizeof cmd, - "cd %s && b='%s'; WW_PKGCACHE=%s/pkgc timeout 180 %s build --sep " + "cd %s && b='%s'; timeout 180 %s build %s" "-o \"${b%%.ww}\" \"$b\" 2>/dev/null", - tmpdir, src, tmpdir, driver); + tmpdir, src, driver, asm_only ? "-S " : ""); return runwait(cmd); } static int -run_row(const char *driver, const struct row *r, int seq) +run_cstage(const char *driver, const char *cwd, const struct row *r, int seq) { - char tmpdir[256], src[512], base[64], outbin[768]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/wt_%d_d_%d", getpid(), seq); - snprintf(src, sizeof src, "%s/main771.ww", tmpdir); - snprintf(base, sizeof base, "main771"); - mkdir(tmpdir, 0755); - if (write_source(src, r->src) != 0) { - cleanup_tmp(tmpdir, base); - return -1; + (void)seq; + char td[] = "/tmp/r771_r_XXXXXX"; + char src[512], base[64], exe[768]; + if (mkdtemp(td) == NULL) return -1; + snprintf(base, sizeof base, "main771"); + snprintf(src, sizeof src, "%s/%s.ww", td, base); + int rc = -1; + if (materialize_source(cwd, r, src) != 0) goto out; + rc = build_via_driver(driver, td, cwd, src, 0); + if (rc == 0) { + snprintf(exe, sizeof exe, "%s/%s", td, base); + rc = runwait(exe); } - int rc; - int br = build_via_driver(driver, tmpdir, src); - if (r->expected_exit == EXPECT_REJECT) { - /* Build must fail with a non-zero exit (checker error). */ - rc = (br != 0) ? r->expected_exit : 0; - } else if (br == 0) { - snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); - rc = runwait(outbin); - } else { +out: + if (cleanup_tmp(td, base) != 0) { + fprintf(stderr, "widen_transitive[%s]: run cleanup failed\n", + r->label); rc = -1; } - cleanup_tmp(tmpdir, base); return rc; } -/* asm_byte_identical — diff cstage vs wwstage .s. Parallel trees so - * ww_ww writing intermediates next to the source doesn't clobber the - * cstage .s (CLAUDE.md rule 14 phase split). */ static int -asm_byte_identical(const char *cdrv, const char *wdrv, +asm_byte_identical(const char *cdrv, const char *wdrv, const char *cwd, const struct row *r, int seq) { - char src[512], tdc[256], tdw[256], base[64], cs[512], ws[512]; - snprintf(tdc, sizeof tdc, "/tmp/wt_%d_c_%d", getpid(), seq); - snprintf(tdw, sizeof tdw, "/tmp/wt_%d_w_%d", getpid(), seq); + (void)seq; + char tc[] = "/tmp/r771_c_XXXXXX"; + char tw[] = "/tmp/r771_w_XXXXXX"; + char src[512], base[64], cs[512], ws[512]; snprintf(base, sizeof base, "main771"); - mkdir(tdc, 0755); - mkdir(tdw, 0755); - snprintf(src, sizeof src, "%s/main771.ww", tdc); - if (write_source(src, r->src) != 0) { cleanup_tmp(tdc, base); cleanup_tmp(tdw, base); return -1; } + if (mkdtemp(tc) == NULL) return -1; + if (mkdtemp(tw) == NULL) { + if (cleanup_tmp(tc, base) != 0) + fprintf(stderr, "widen_transitive[%s]: setup cleanup failed\n", + r->label); + return -1; + } int rc = -1; - if (build_via_driver(cdrv, tdc, src) != 0) goto out; - snprintf(cs, sizeof cs, "%s/%s.sepwork/__root.s", tdc, base); - - snprintf(src, sizeof src, "%s/main771.ww", tdw); - if (write_source(src, r->src) != 0) goto out; - if (build_via_driver(wdrv, tdw, src) != 0) goto out; - snprintf(ws, sizeof ws, "%s/%s.sepwork/__root.s", tdw, base); - - FILE *fc = fopen(cs, "rb"); - FILE *fw = fopen(ws, "rb"); + snprintf(src, sizeof src, "%s/%s.ww", tc, base); + if (materialize_source(cwd, r, src) != 0) goto out; + if (build_via_driver(cdrv, tc, cwd, src, 1) != 0) goto out; + snprintf(cs, sizeof cs, "%s/%s.sepwork/__root.s", tc, base); + snprintf(src, sizeof src, "%s/%s.ww", tw, base); + if (materialize_source(cwd, r, src) != 0) goto out; + if (build_via_driver(wdrv, tw, cwd, src, 1) != 0) goto out; + snprintf(ws, sizeof ws, "%s/%s.sepwork/__root.s", tw, base); + FILE *fc = fopen(cs, "rb"), *fw = fopen(ws, "rb"); if (fc && fw) { rc = 0; for (;;) { - int a = fgetc(fc); - int b = fgetc(fw); + int a = fgetc(fc), b = fgetc(fw); if (a != b) { rc = -1; break; } if (a == EOF) break; } @@ -248,73 +177,58 @@ asm_byte_identical(const char *cdrv, const char *wdrv, if (fc) fclose(fc); if (fw) fclose(fw); out: - cleanup_tmp(tdc, base); - cleanup_tmp(tdw, base); + if (cleanup_tmp(tc, base) != 0) { + fprintf(stderr, "widen_transitive[%s]: c cleanup failed\n", r->label); + rc = -1; + } + if (cleanup_tmp(tw, base) != 0) { + fprintf(stderr, "widen_transitive[%s]: ww cleanup failed\n", r->label); + rc = -1; + } return rc; } int main(void) { + char cwd[512]; + if (!getcwd(cwd, sizeof cwd)) return 1; const char *bin = getenv("BIN"); if (!bin) bin = "out/bin"; - char absbin[512]; + char absbin[1024]; if (bin[0] != '/') { - char cwd[256]; - if (getcwd(cwd, sizeof cwd) == NULL) return 1; snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); bin = absbin; } - - char cdrv[640], wdrv[640]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); + char cdrv[1200], wdrv[1200]; + snprintf(cdrv, sizeof cdrv, "%s/ww", bin); snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - int wwpresent = (access(wdrv, X_OK) == 0); - int seq = 0; - - for (int i = 0; i < n; i++) { - if (rows[i].stage_mask & STAGE_CS) { + int fail = 0, total = 0, seq = 0; + int wwpresent = access(wdrv, X_OK) == 0; + for (size_t i = 0; i < sizeof rows / sizeof rows[0]; i++) { + if (rows[i].cstage_run) { total++; - int got = run_row(cdrv, &rows[i], seq++); + int got = run_cstage(cdrv, cwd, &rows[i], seq++); if (got != rows[i].expected_exit) { - fprintf(stderr, - "widen_transitive[cstage run][%s]: exit=%d want=%d\n", + fprintf(stderr, "widen_transitive[cstage-only run][%s]: exit=%d want=%d\n", rows[i].label, got, rows[i].expected_exit); fail++; } } - if (wwpresent && (rows[i].stage_mask & STAGE_WW)) { + if (wwpresent && rows[i].byte_id) { total++; - int got = run_row(wdrv, &rows[i], seq++); - if (got != rows[i].expected_exit) { - fprintf(stderr, - "widen_transitive[wwstage run][%s]: exit=%d want=%d\n", - rows[i].label, got, rows[i].expected_exit); + if (asm_byte_identical(cdrv, wdrv, cwd, &rows[i], seq++) != 0) { + fprintf(stderr, "widen_transitive[byte-id][%s]: assembly differs\n", + rows[i].label); fail++; } - if (rows[i].byte_id) { - total++; - if (asm_byte_identical(cdrv, wdrv, &rows[i], seq++) != 0) { - fprintf(stderr, - "widen_transitive[byte-id][%s]: cstage vs wwstage asm differs\n", - rows[i].label); - fail++; - } - } } } - - if (!wwpresent) - fprintf(stderr, "widen_transitive: skip wwstage (no %s)\n", wdrv); - + if (!wwpresent) fprintf(stderr, "widen_transitive: skip byte-id (no %s)\n", wdrv); if (fail) { - fprintf(stderr, "widen_transitive: %d/%d fixtures failed\n", - fail, total); + fprintf(stderr, "widen_transitive: %d/%d residual gates failed\n", fail, total); return 1; } - printf("widen_transitive: %d/%d ok\n", total, total); + printf("widen_transitive: %d/%d residual gates ok\n", total, total); return 0; } diff --git a/test/wcc/779_log_vstream_run.c b/test/wcc/779_log_vstream_run.c index cd186445..8e18a6c1 100644 --- a/test/wcc/779_log_vstream_run.c +++ b/test/wcc/779_log_vstream_run.c @@ -87,6 +87,7 @@ */ #include #include +#include #include #include #include @@ -189,25 +190,27 @@ write_source(const char *path, const char *src) { FILE *f = fopen(path, "wb"); if (!f) return -1; - fputs(src, f); - fclose(f); - return 0; + int bad = fputs(src, f) == EOF || ferror(f); + if (fclose(f) != 0) bad = 1; + return bad ? -1 : 0; } -/* Per-row tmpdir cleanup. ww_ww writes intermediates next to the - * source (filed task #15), so each row's build leaves - * .{combined.ww,s,o} + bare exe alongside. Sweep all then - * rmdir. Mirror of 778's cleanup_tmp. */ -static void +/* Per-row tmpdir cleanup. `ww build` retains its exact + * .sepwork tree; remove that tree, the source, and the bare + * executable before removing the invocation-owned directory. */ +static int cleanup_tmp(const char *tmpdir, const char *base) { char p[640]; - snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "%s/%s.s", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "%s/%s.o", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "%s/%s", tmpdir, base); unlink(p); - rmdir(tmpdir); + int rc = 0; + snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); + if (unlink(p) != 0 && errno != ENOENT) rc = -1; + snprintf(p, sizeof p, "rm -rf -- %s/%s.sepwork", tmpdir, base); + if (runwait(p) != 0) rc = -1; + snprintf(p, sizeof p, "%s/%s", tmpdir, base); + if (unlink(p) != 0 && errno != ENOENT) rc = -1; + if (rmdir(tmpdir) != 0) rc = -1; + return rc; } static int @@ -228,8 +231,12 @@ run_row(const char *driver, const char *cwd, const struct row *r, int seq) snprintf(tmpdir, sizeof tmpdir, "/tmp/lvs_%d_d_%d", getpid(), seq); snprintf(base, sizeof base, "main779"); snprintf(src, sizeof src, "%s/%s.ww", tmpdir, base); - mkdir(tmpdir, 0755); - if (write_source(src, r->src) != 0) { cleanup_tmp(tmpdir, base); return -1; } + if (mkdir(tmpdir, 0755) != 0) return -1; + if (write_source(src, r->src) != 0) { + if (cleanup_tmp(tmpdir, base) != 0) + fprintf(stderr, "row[%s]: setup cleanup failed\n", r->label); + return -1; + } int rc; int br = build_via_driver(driver, tmpdir, cwd, src); if (br == 0) { @@ -243,13 +250,12 @@ run_row(const char *driver, const char *cwd, const struct row *r, int seq) } else { rc = -1; } - cleanup_tmp(tmpdir, base); + if (cleanup_tmp(tmpdir, base) != 0) return -1; return rc; } -/* asm_byte_identical — diff cstage vs wwstage .s. Parallel trees so - * ww_ww writing intermediates next to the source doesn't clobber the - * cstage .s (CLAUDE.md rule 14 phase split). Mirror of 778's. Unused +/* asm_byte_identical — diff cstage vs wwstage .s. Parallel trees keep + * each driver's retained .sepwork output separate. Unused * for fold-e5 — every row is STAGE_CS-only per #226/#227 (#209, the * checker bail, is closed) — but kept scaffolded for when those cgen * folds land and byte-id graduates. */ @@ -261,18 +267,28 @@ asm_byte_identical(const char *cdrv, const char *wdrv, const char *cwd, snprintf(tdc, sizeof tdc, "/tmp/lvs_%d_c_%d", getpid(), seq); snprintf(tdw, sizeof tdw, "/tmp/lvs_%d_w_%d", getpid(), seq); snprintf(base, sizeof base, "main779"); - mkdir(tdc, 0755); - mkdir(tdw, 0755); + if (mkdir(tdc, 0755) != 0) return -1; + if (mkdir(tdw, 0755) != 0) { + if (cleanup_tmp(tdc, base) != 0) + fprintf(stderr, "row[%s]: setup cleanup failed\n", r->label); + return -1; + } snprintf(src, sizeof src, "%s/%s.ww", tdc, base); - if (write_source(src, r->src) != 0) { cleanup_tmp(tdc, base); cleanup_tmp(tdw, base); return -1; } + if (write_source(src, r->src) != 0) { + int clean_c = cleanup_tmp(tdc, base); + int clean_w = cleanup_tmp(tdw, base); + if (clean_c != 0 || clean_w != 0) + fprintf(stderr, "row[%s]: setup cleanup failed\n", r->label); + return -1; + } int rc = -1; if (build_via_driver(cdrv, tdc, cwd, src) != 0) goto out; - snprintf(cs, sizeof cs, "%s/%s.s", tdc, base); + snprintf(cs, sizeof cs, "%s/%s.sepwork/__root.s", tdc, base); snprintf(src, sizeof src, "%s/%s.ww", tdw, base); if (write_source(src, r->src) != 0) goto out; if (build_via_driver(wdrv, tdw, cwd, src) != 0) goto out; - snprintf(ws, sizeof ws, "%s/%s.s", tdw, base); + snprintf(ws, sizeof ws, "%s/%s.sepwork/__root.s", tdw, base); FILE *fc = fopen(cs, "rb"); FILE *fw = fopen(ws, "rb"); @@ -288,8 +304,8 @@ asm_byte_identical(const char *cdrv, const char *wdrv, const char *cwd, if (fc) fclose(fc); if (fw) fclose(fw); out: - cleanup_tmp(tdc, base); - cleanup_tmp(tdw, base); + if (cleanup_tmp(tdc, base) != 0) rc = -1; + if (cleanup_tmp(tdw, base) != 0) rc = -1; return rc; } diff --git a/test/wcc/783_amp_fn_assign_run.c b/test/wcc/783_amp_fn_assign_run.c index 3c8e5e1a..c1ed6d50 100644 --- a/test/wcc/783_amp_fn_assign_run.c +++ b/test/wcc/783_amp_fn_assign_run.c @@ -20,22 +20,22 @@ * hint through the bottom-up expression checker. * * ROW POLARITY: - * POSITIVE rows build + run on BOTH stages and assert cs.s == ww.s - * (rule-10). `let_call` and `structlit_build` pin the let-binding and - * struct-literal-field-init gate sites. `fieldstore_dispatch` pins the - * eFinal SHIPPING shape: a bare &fn field-STORE into one slot of the - * three-slot io.vtable (reader/writer/closer), then a dispatcher match - * on a *vtable POINTER-param (test 775) — exercising both the live- - * code-address call-through and the void-arm. It is byte-id-clean: the + * POSITIVE execution assertions now live in the declarative compiler + * corpus. Their residual rows here compile BOTH stages and assert + * cs.s == ww.s (rule-10). `let_call` and `structlit_build` pin the + * let-binding and struct-literal-field-init gate sites. + * `fieldstore_dispatch` pins the eFinal SHIPPING shape: a bare &fn + * field-STORE into one slot of the three-slot io.vtable + * (reader/writer/closer), then a dispatcher match on a *vtable + * POINTER-param. It remains byte-id-clean: the * single-slot local-composite zero-init divergence (project #213, * reproduces with the cast form and a pure `(i32|void)` field — NOT a * #206 regression) is a single-field artifact the real three-slot * io.vtable does not hit. * - * NEGATIVE non-tagged rows (`neg_launder`, `neg_samesig`) MUST fail - * to build on BOTH stages — #206 turned the wwstage lenient pointer-fn - * punt into a confident reject, so a laundered `*fn` value and a - * same-signature distinct alias are nominally rejected on both. + * The symmetric non-tagged rejection rows (`neg_launder`, + * `neg_samesig`) live in the declarative compiler corpus. Both checker + * paths report the common diagnostic fragment "not assignable". * * NEGATIVE tagged-slot rows (`neg_ambiguous_tagged`, * `neg_launder_tagged`) are pinned CSTAGE-ONLY. cstage REJECTS both. @@ -47,11 +47,11 @@ * when #214 closes. Mirrors the 777/780/781/782 STAGE_CS carve-out. * * GATE POLARITY: must stay GREEN. Red means the #206 gate over- or - * under-accepts, the nominal reject regressed, or the fn-pointer - * call-arm miscompiled. + * under-accepts or the stage output diverged. */ #include #include +#include #include #include #include @@ -70,39 +70,28 @@ runwait(const char *cmd) struct row { const char *label; - const char *src; - int want_exit; /* expected program exit (build+run rows) */ + const char *src; /* local source for asymmetric controls */ + const char *fixture; /* declarative corpus source for byte rows */ int stage_mask; int byte_id; /* assert cs.s == ww.s */ - int expect_fail; /* 1 = build MUST fail; want_exit ignored */ + int expect_fail; /* 1 = build MUST fail */ }; static const struct row rows[] = { /* POSITIVE byte-id: bare &fn into a * let, then call. */ { "let_call", - "package main;\n" - "type reader = fn(x: i32) i32;\n" - "fn rd(x: i32) i32 = { return x + 1; };\n" - "fn main() i32 = {\n" - " let p: *reader = &rd;\n" - " return (*p)(41);\n" - "};\n", - 42, STAGE_CS | STAGE_WW, 1, 0 }, + NULL, + "test/wcc/data/r78_amp_fn_assign_let_call/case.ww", + STAGE_CS | STAGE_WW, 1, 0 }, /* POSITIVE byte-id: struct-literal field-init of a (*alias|void) * slot with bare &fn (the Hare `vtable{reader=&fn}` shape). Build * only — no match (project #212 blocks struct-lit-tagged + match * on wwstage cgen); the checker gate acceptance is what this pins. */ { "structlit_build", - "package main;\n" - "type reader = fn(x: i32) i32;\n" - "type vtable = struct { r: (*reader | void) };\n" - "fn rd(x: i32) i32 = { return x + 1; };\n" - "fn main() i32 = {\n" - " let v = vtable { r = &rd };\n" - " return 0;\n" - "};\n", - 0, STAGE_CS | STAGE_WW, 1, 0 }, + NULL, + "test/wcc/data/r78_amp_fn_assign_structlit/case.ww", + STAGE_CS | STAGE_WW, 1, 0 }, /* POSITIVE byte-id: bare &fn FIELD-STORE into a (*reader|void) slot * of the THREE-slot vtable (the exact io.vtable reader/writer/closer @@ -113,63 +102,9 @@ static const struct row rows[] = { * slot local-composite zero-init divergence (#213) is a single-field * artifact that the real three-slot io.vtable does not hit. */ { "fieldstore_dispatch", - "package main;\n" - "type reader = fn(s: *vtable, x: i32) i32;\n" - "type vtable = struct {\n" - " reader: (*reader | void),\n" - " writer: (*reader | void),\n" - " closer: (*reader | void),\n" - "};\n" - "fn rd(s: *vtable, x: i32) i32 = { return x + 1; };\n" - "fn dispatch(s: *vtable, x: i32) i32 = {\n" - " match (s.reader) {\n" - " case void => { return -1; };\n" - " case let f: *reader => { return (*f)(s, x); };\n" - " };\n" - "};\n" - "fn main() i32 = {\n" - " let a: vtable;\n" - " a.reader = &rd;\n" - " let b: vtable;\n" - " b.reader = void;\n" - " return dispatch(&a, 41) + dispatch(&b, 0) + 11;\n" - "};\n", - 52, STAGE_CS | STAGE_WW, 1, 0 }, - - /* NEGATIVE both stages: a materialized *fn value laundered into a - * *reader slot. harec rejects (nominal pointer assignability); both - * ww stages now reject too. */ - { "neg_launder", - "package main;\n" - "type reader = fn(x: i32) i32;\n" - "fn rd(x: i32) i32 = { return x + 1; };\n" - "fn main() i32 = {\n" - " let p = &rd;\n" - " let s: *reader = p;\n" - " return 0;\n" - "};\n", - 0, STAGE_CS | STAGE_WW, 0, 1 }, - - /* NEGATIVE both stages: distinct same-signature aliases. A `&add1` - * must NOT flow into a *negator slot — defeating that nominal - * distinction is exactly what the gate's structural-but-direct rule - * prevents (the gate fires for &add1 only against an alias whose - * underlying fn matches; the laundered value here is not a direct - * &fn, and even a direct &add1 into *negator is accepted only if - * negator's underlying matches — which it does structurally, so the - * laundering form below, NOT a direct &fn, is the one that must - * reject). */ - { "neg_samesig", - "package main;\n" - "type adder = fn(x: i32) i32;\n" - "type negator = fn(x: i32) i32;\n" - "fn add1(x: i32) i32 = { return x + 1; };\n" - "fn main() i32 = {\n" - " let p = &add1;\n" - " let n: *negator = p;\n" - " return 0;\n" - "};\n", - 0, STAGE_CS | STAGE_WW, 0, 1 }, + NULL, + "test/wcc/data/r78_amp_fn_assign_fieldstore_dispatch/case.ww", + STAGE_CS | STAGE_WW, 1, 0 }, /* NEGATIVE cstage-only (#214 wwstage void-variant over-acceptance): * two same-signature ptr-to-fn variants in the tagged dst — a direct @@ -183,7 +118,8 @@ static const struct row rows[] = { " let x: (*reader | *writer | void) = &myfn;\n" " return 0;\n" "};\n", - 0, STAGE_CS, 0, 1 }, + NULL, + STAGE_CS, 0, 1 }, /* NEGATIVE cstage-only (#214): laundering a materialized *fn into a * (*reader|void) tagged slot. cstage rejects nominally; wwstage's @@ -197,106 +133,121 @@ static const struct row rows[] = { " let s: (*reader | void) = p;\n" " return 0;\n" "};\n", - 0, STAGE_CS, 0, 1 }, + NULL, + STAGE_CS, 0, 1 }, }; -static void +static int cleanup_tmp(const char *tmpdir, const char *base) { char p[1024]; - snprintf(p, sizeof p, "%s/%s", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "%s/%s.o", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base); unlink(p); - /* #93: the sep scratch dir + the tmpdir-pinned pkgcache. */ - snprintf(p, sizeof p, "rm -rf %s/%s.sepwork %s/pkgc", tmpdir, base, tmpdir); - if (system(p)) {} /* best-effort */ - rmdir(tmpdir); + int rc = 0; + snprintf(p, sizeof p, "%s/%s", tmpdir, base); + if (unlink(p) != 0 && errno != ENOENT) rc = -1; + snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); + if (unlink(p) != 0 && errno != ENOENT) rc = -1; + /* #93: the sep scratch dir + the tmpdir-owned outputs. */ + snprintf(p, sizeof p, "rm -rf %s/%s.sepwork", tmpdir, base); + if (runwait(p) != 0) rc = -1; + if (rmdir(tmpdir) != 0) rc = -1; + return rc; } static int -write_source(const char *path, const char *src) +write_source(const char *path, const struct row *r) { + FILE *in = NULL; + if (r->fixture != NULL) { + in = fopen(r->fixture, "rb"); + if (in == NULL) return -1; + } FILE *f = fopen(path, "wb"); - if (!f) return -1; - fputs(src, f); - fclose(f); - return 0; + if (!f) { + if (in != NULL) fclose(in); + return -1; + } + if (in != NULL) { + int ioerr = 0; + for (;;) { + int ch = fgetc(in); + if (ch == EOF) break; + if (fputc(ch, f) == EOF) { ioerr = 1; break; } + } + if (ferror(in)) ioerr = 1; + if (fclose(in) != 0) ioerr = 1; + if (fclose(f) != 0) ioerr = 1; + return ioerr ? -1 : 0; + } else { + if (fputs(r->src, f) == EOF) { + fclose(f); + return -1; + } + } + return fclose(f) == 0 ? 0 : -1; } static int build_via_driver(const char *driver, const char *tmpdir, const char *src) { char cmd[2048]; - /* #93 sep layout: `--sep -o ` relocates artifacts to - * .sepwork/; WW_PKGCACHE is pinned under tmpdir so the shared - * out/.pkgcache is untouched. expect_fail rows still fail: a checker + /* #93 sep layout: `-o ` 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'; WW_PKGCACHE=%s/pkgc timeout 180 %s build --sep " + "cd %s && b='%s'; timeout 180 %s build " "-o \"${b%%.ww}\" \"$b\" 2>/dev/null", - tmpdir, src, tmpdir, driver); + tmpdir, src, driver); return runwait(cmd); } -/* run_row — build via driver, run the binary, return exit (or -1 on - * build failure). */ -static int -run_row(const char *driver, const struct row *r, int seq) -{ - char tmpdir[256], src[512], base[64], outbin[768]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/afa_%d_d_%d", getpid(), seq); - snprintf(base, sizeof base, "main783"); - snprintf(src, sizeof src, "%s/%s.ww", tmpdir, base); - mkdir(tmpdir, 0755); - if (write_source(src, r->src) != 0) { cleanup_tmp(tmpdir, base); return -1; } - int rc; - if (build_via_driver(driver, tmpdir, src) == 0) { - snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); - rc = runwait(outbin); - } else { - rc = -1; - } - cleanup_tmp(tmpdir, base); - return rc; -} - /* build_ok — 1 iff the build succeeds (used by expect_fail rows). */ static int build_ok(const char *driver, const struct row *r, int seq) { - char tmpdir[256], src[512], base[64]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/afa_%d_nf_%d", getpid(), seq); + (void)seq; + char tmpdir[] = "/tmp/afa_nf_XXXXXX"; + char src[512], base[64]; + if (mkdtemp(tmpdir) == NULL) return -1; snprintf(base, sizeof base, "main783"); snprintf(src, sizeof src, "%s/%s.ww", tmpdir, base); - mkdir(tmpdir, 0755); - if (write_source(src, r->src) != 0) { cleanup_tmp(tmpdir, base); return -1; } + if (write_source(src, r) != 0) { + if (cleanup_tmp(tmpdir, base) != 0) + fprintf(stderr, "amp_fn_assign[%s]: setup cleanup failed\n", r->label); + return -1; + } int br = build_via_driver(driver, tmpdir, src); - cleanup_tmp(tmpdir, base); + if (cleanup_tmp(tmpdir, base) != 0) { + fprintf(stderr, "amp_fn_assign[%s]: cleanup failed\n", r->label); + return -1; + } return br == 0; } -/* asm_byte_identical — diff cstage vs wwstage .s. Parallel trees so - * ww_ww writing intermediates next to the source doesn't clobber the - * cstage .s (CLAUDE.md rule 14 phase split). */ +/* asm_byte_identical — diff cstage vs wwstage .s. Parallel owned trees + * keep each driver's retained .sepwork/__root.s distinct. */ static int asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r, int seq) { - char src[512], tdc[256], tdw[256], base[64], cs[512], ws[512]; - snprintf(tdc, sizeof tdc, "/tmp/afa_%d_c_%d", getpid(), seq); - snprintf(tdw, sizeof tdw, "/tmp/afa_%d_w_%d", getpid(), seq); + (void)seq; + char src[512], base[64], cs[512], ws[512]; + char tdc[] = "/tmp/afa_c_XXXXXX"; + char tdw[] = "/tmp/afa_w_XXXXXX"; snprintf(base, sizeof base, "main783"); - mkdir(tdc, 0755); - mkdir(tdw, 0755); + if (mkdtemp(tdc) == NULL) return -1; + if (mkdtemp(tdw) == NULL) { + if (cleanup_tmp(tdc, base) != 0) + fprintf(stderr, "amp_fn_assign[%s]: setup cleanup failed\n", r->label); + return -1; + } snprintf(src, sizeof src, "%s/%s.ww", tdc, base); - if (write_source(src, r->src) != 0) { cleanup_tmp(tdc, base); cleanup_tmp(tdw, base); return -1; } int rc = -1; + if (write_source(src, r) != 0) goto out; if (build_via_driver(cdrv, tdc, src) != 0) goto out; snprintf(cs, sizeof cs, "%s/%s.sepwork/__root.s", tdc, base); snprintf(src, sizeof src, "%s/%s.ww", tdw, base); - if (write_source(src, r->src) != 0) goto out; + if (write_source(src, r) != 0) goto out; if (build_via_driver(wdrv, tdw, src) != 0) goto out; snprintf(ws, sizeof ws, "%s/%s.sepwork/__root.s", tdw, base); @@ -314,8 +265,14 @@ asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r, if (fc) fclose(fc); if (fw) fclose(fw); out: - cleanup_tmp(tdc, base); - cleanup_tmp(tdw, base); + if (cleanup_tmp(tdc, base) != 0) { + fprintf(stderr, "amp_fn_assign[%s]: c cleanup failed\n", r->label); + rc = -1; + } + if (cleanup_tmp(tdw, base) != 0) { + fprintf(stderr, "amp_fn_assign[%s]: ww cleanup failed\n", r->label); + rc = -1; + } return rc; } @@ -343,47 +300,40 @@ main(void) for (int i = 0; i < n; i++) { const struct row *r = &rows[i]; - if (r->stage_mask & STAGE_CS) { + if ((r->stage_mask & STAGE_CS) && r->expect_fail) { total++; - if (r->expect_fail) { - if (build_ok(cdrv, r, seq++)) { + int got = build_ok(cdrv, r, seq++); + if (got != 0) { + if (got > 0) fprintf(stderr, "amp_fn_assign[cs][%s]: built but expected reject\n", r->label); - fail++; - } - } else { - int got = run_row(cdrv, r, seq++); - if (got != r->want_exit) { - fprintf(stderr, "amp_fn_assign[cs][%s]: exit=%d want=%d\n", - r->label, got, r->want_exit); - fail++; - } + else + fprintf(stderr, "amp_fn_assign[cs][%s]: setup or cleanup failed\n", + r->label); + fail++; } } if (wwpresent && (r->stage_mask & STAGE_WW)) { - total++; if (r->expect_fail) { - if (build_ok(wdrv, r, seq++)) { - fprintf(stderr, "amp_fn_assign[ww][%s]: built but expected reject\n", + total++; + int got = build_ok(wdrv, r, seq++); + if (got != 0) { + if (got > 0) + fprintf(stderr, "amp_fn_assign[ww][%s]: built but expected reject\n", + r->label); + else + fprintf(stderr, "amp_fn_assign[ww][%s]: setup or cleanup failed\n", + r->label); + fail++; + } + } else if (r->byte_id) { + total++; + if (asm_byte_identical(cdrv, wdrv, r, seq++) != 0) { + fprintf(stderr, "amp_fn_assign[byte-id][%s]: cstage vs wwstage asm differs\n", r->label); fail++; } - } else { - int got = run_row(wdrv, r, seq++); - if (got != r->want_exit) { - fprintf(stderr, "amp_fn_assign[ww][%s]: exit=%d want=%d\n", - r->label, got, r->want_exit); - fail++; - } - if (r->byte_id) { - total++; - if (asm_byte_identical(cdrv, wdrv, r, seq++) != 0) { - fprintf(stderr, "amp_fn_assign[byte-id][%s]: cstage vs wwstage asm differs\n", - r->label); - fail++; - } - } } } } diff --git a/test/wcc/784_xmod_alias_struct_collide_run.c b/test/wcc/784_xmod_alias_struct_collide_run.c index 5e7307c6..3855758a 100644 --- a/test/wcc/784_xmod_alias_struct_collide_run.c +++ b/test/wcc/784_xmod_alias_struct_collide_run.c @@ -54,6 +54,7 @@ */ #include #include +#include #include #include #include @@ -80,7 +81,9 @@ slurp_eq(const char *a, const char *b) if (ca != cb) { rc = -1; break; } if (ca == EOF) break; } - fclose(fa); fclose(fb); + if (ferror(fa) || ferror(fb)) rc = -1; + if (fclose(fa) != 0) rc = -1; + if (fclose(fb) != 0) rc = -1; return rc; } @@ -260,15 +263,17 @@ run_scenario(const char *bin, const char *cdrv, const char *wdrv, FILE *f = fopen(path, "wb"); if (!f) { fprintf(stderr, "784[%s]: write %s\n", sc->label, sc->files[i].name); rc = -1; goto done; } - fputs(sc->files[i].src, f); - fclose(f); + int bad = fputs(sc->files[i].src, f) == EOF; + if (fclose(f) != 0) bad = 1; + if (bad) { fprintf(stderr, "784[%s]: write %s\n", sc->label, + sc->files[i].name); rc = -1; goto done; } } - /* #94 sep layout: cstage --sep build + run pins runtime; pin - * WW_PKGCACHE under the scratch dir so out/.pkgcache is untouched. */ + /* #94 sep layout: cstage build + run pins runtime; pin + * all outputs stay under the scratch dir. */ snprintf(cmd, sizeof cmd, - "cd %s && WW_PKGCACHE=%s/pkgc_c %s build --sep -I %s -o %s/main %s/main.ww", - dir, dir, cdrv, dir, dir, dir); + "cd %s && %s build -I %s -o %s/main %s/main.ww", + dir, cdrv, dir, dir, dir); if (runwait(cmd) != 0) { fprintf(stderr, "784[%s]: cstage build failed\n", sc->label); rc = -1; goto done; @@ -281,24 +286,36 @@ run_scenario(const char *bin, const char *cdrv, const char *wdrv, rc = -1; } - /* Byte-id net (the #223 discriminator): the wwstage driver's --sep + /* Byte-id net (the #223 discriminator): the wwstage driver's * build emits per-package w6c_ww asm; pre-fix the dispatcher diverges. * The root + each imported pkg compile to separate .sepwork/.s; * concat (sorted glob, identical set both stages) for the compare. */ char cs_s[1024], ws_s[1024]; snprintf(cmd, sizeof cmd, - "cd %s && WW_PKGCACHE=%s/pkgc_w %s build --sep -I %s -o %s/mainww %s/main.ww", - dir, dir, wdrv, dir, dir, dir); + "cd %s && %s build -I %s -o %s/mainww %s/main.ww", + dir, wdrv, dir, dir, dir); if (runwait(cmd) != 0) { fprintf(stderr, "784[%s]: ww_ww build failed\n", sc->label); rc = -1; goto done; } snprintf(cs_s, sizeof cs_s, "%s/all_cs.s", dir); snprintf(ws_s, sizeof ws_s, "%s/all_ww.s", dir); - snprintf(cmd, sizeof cmd, "cat %s/main.sepwork/*.s > %s 2>/dev/null", - dir, cs_s); if (system(cmd)) {} - snprintf(cmd, sizeof cmd, "cat %s/mainww.sepwork/*.s > %s 2>/dev/null", - dir, ws_s); if (system(cmd)) {} + snprintf(cmd, sizeof cmd, + "cat %s/main.sepwork/*.s > %s 2>/dev/null && test -s %s", + dir, cs_s, cs_s); + if (runwait(cmd) != 0) { + fprintf(stderr, "784[%s]: cstage asm aggregation failed\n", + sc->label); + rc = -1; goto done; + } + snprintf(cmd, sizeof cmd, + "cat %s/mainww.sepwork/*.s > %s 2>/dev/null && test -s %s", + dir, ws_s, ws_s); + if (runwait(cmd) != 0) { + fprintf(stderr, "784[%s]: wwstage asm aggregation failed\n", + sc->label); + rc = -1; goto done; + } if (slurp_eq(cs_s, ws_s) != 0) { fprintf(stderr, "784[%s]: cs.s/ww.s DIFFER (rule-10 byte-id " "violation — #223 regression)\n", sc->label); @@ -306,9 +323,33 @@ run_scenario(const char *bin, const char *cdrv, const char *wdrv, } done: - /* Best-effort cleanup of the private dir. */ - snprintf(cmd, sizeof cmd, "rm -rf %s", dir); - (void)runwait(cmd); + snprintf(cmd, sizeof cmd, "rm -rf %s/main.sepwork", dir); + if (runwait(cmd) != 0) { fprintf(stderr, "784[%s]: cleanup main.sepwork\n", + sc->label); rc = -1; } + snprintf(cmd, sizeof cmd, "rm -rf %s/mainww.sepwork", dir); + if (runwait(cmd) != 0) { fprintf(stderr, "784[%s]: cleanup mainww.sepwork\n", + sc->label); rc = -1; } + for (int i = 0; sc->files[i].name; i++) { + snprintf(path, sizeof path, "%s/%s", dir, sc->files[i].name); + if (unlink(path) != 0 && errno != ENOENT) { + fprintf(stderr, "784[%s]: cleanup %s\n", sc->label, + sc->files[i].name); + rc = -1; + } + } + const char *children[] = { "main", "mainww", "all_cs.s", "all_ww.s", NULL }; + for (int i = 0; children[i]; i++) { + snprintf(path, sizeof path, "%s/%s", dir, children[i]); + if (unlink(path) != 0 && errno != ENOENT) { + fprintf(stderr, "784[%s]: cleanup %s\n", sc->label, + children[i]); + rc = -1; + } + } + if (rmdir(dir) != 0) { + fprintf(stderr, "784[%s]: cleanup directory\n", sc->label); + rc = -1; + } return rc; } diff --git a/test/wcc/785_structvariant_largeunion_return.c b/test/wcc/785_structvariant_largeunion_return.c index 50a01819..4a7c76df 100644 --- a/test/wcc/785_structvariant_largeunion_return.c +++ b/test/wcc/785_structvariant_largeunion_return.c @@ -47,6 +47,7 @@ */ #include #include +#include #include #include #include @@ -111,17 +112,28 @@ static const struct row rows[] = { 22, STAGE_CS | STAGE_WW, 1 }, }; -static void +/* nonzero on any cleanup failure; ENOENT tolerated because legs that + * fail early never create the later artifacts. */ +static int cleanup_tmp(const char *tmpdir, const char *base) { char p[1024]; - snprintf(p, sizeof p, "%s/%s", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "rm -rf %s/%s.sepwork %s/pkgc", tmpdir, base, tmpdir); - if (system(p)) {} /* best-effort */ - snprintf(p, sizeof p, "%s/%s.o", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base); unlink(p); - rmdir(tmpdir); + int bad = 0; + snprintf(p, sizeof p, "%s/%s", tmpdir, base); + if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; } + snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); + if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; } + snprintf(p, sizeof p, "rm -rf %s/%s.sepwork", tmpdir, base); + if (runwait(p) != 0) { + fprintf(stderr, "cleanup failed: %s\n", p); + bad = 1; + } + snprintf(p, sizeof p, "%s/%s.o", tmpdir, base); + if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; } + snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base); + if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; } + if (rmdir(tmpdir) != 0) { perror(tmpdir); bad = 1; } + return bad; } static int @@ -129,8 +141,11 @@ write_source(const char *path, const char *src) { FILE *f = fopen(path, "wb"); if (!f) return -1; - fputs(src, f); - fclose(f); + if (fputs(src, f) == EOF) { + fclose(f); + return -1; + } + if (fclose(f) != 0) return -1; return 0; } @@ -139,36 +154,14 @@ build_via_driver(const char *driver, const char *tmpdir, const char *src) { char cmd[2048]; /* #93 sep layout: emit asm to .sepwork/__root.s; pin - * WW_PKGCACHE under tmpdir so out/.pkgcache is untouched. */ + * all outputs stay under tmpdir. */ snprintf(cmd, sizeof cmd, - "cd %s && b='%s'; WW_PKGCACHE=%s/pkgc timeout 180 %s build --sep " + "cd %s && b='%s'; timeout 180 %s build -S " "-o \"${b%%.ww}\" \"$b\" 2>/dev/null", - tmpdir, src, tmpdir, driver); + tmpdir, src, driver); return runwait(cmd); } -/* run_row — build via driver, run the binary, return exit (or -1 on - * build failure). */ -static int -run_row(const char *driver, const struct row *r, int seq) -{ - char tmpdir[256], src[512], base[64], outbin[768]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/svlu_%d_d_%d", getpid(), seq); - snprintf(base, sizeof base, "main785"); - snprintf(src, sizeof src, "%s/%s.ww", tmpdir, base); - mkdir(tmpdir, 0755); - if (write_source(src, r->src) != 0) { cleanup_tmp(tmpdir, base); return -1; } - int rc; - if (build_via_driver(driver, tmpdir, src) == 0) { - snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); - rc = runwait(outbin); - } else { - rc = -1; - } - cleanup_tmp(tmpdir, base); - return rc; -} - /* asm_byte_identical — diff cstage vs wwstage .s. Parallel trees so * ww_ww writing intermediates next to the source doesn't clobber the * cstage .s (CLAUDE.md rule 14 phase split). */ @@ -180,11 +173,18 @@ asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r, snprintf(tdc, sizeof tdc, "/tmp/svlu_%d_c_%d", getpid(), seq); snprintf(tdw, sizeof tdw, "/tmp/svlu_%d_w_%d", getpid(), seq); snprintf(base, sizeof base, "main785"); - mkdir(tdc, 0755); - mkdir(tdw, 0755); + int rc = -1, tdw_owned = 0, cleanfail = 0; + if (mkdir(tdc, 0755) != 0) { + perror(tdc); + return -1; + } + if (mkdir(tdw, 0755) != 0) { + perror(tdw); + goto out; + } + tdw_owned = 1; snprintf(src, sizeof src, "%s/%s.ww", tdc, base); - if (write_source(src, r->src) != 0) { cleanup_tmp(tdc, base); cleanup_tmp(tdw, base); return -1; } - int rc = -1; + if (write_source(src, r->src) != 0) goto out; if (build_via_driver(cdrv, tdc, src) != 0) goto out; snprintf(cs, sizeof cs, "%s/%s.sepwork/__root.s", tdc, base); @@ -207,8 +207,13 @@ asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r, if (fc) fclose(fc); if (fw) fclose(fw); out: - cleanup_tmp(tdc, base); - cleanup_tmp(tdw, base); + cleanfail = cleanup_tmp(tdc, base); + if (tdw_owned && cleanup_tmp(tdw, base) != 0) + cleanfail = 1; + /* a leaked tree fails an otherwise-green row; a real diff result + * (rc == -1) is never overwritten (110's cleanfail shape). */ + if (cleanfail && rc == 0) + rc = -1; return rc; } @@ -236,33 +241,14 @@ main(void) for (int i = 0; i < n; i++) { const struct row *r = &rows[i]; - if (r->stage_mask & STAGE_CS) { + if (wwpresent && r->byte_id) { total++; - int got = run_row(cdrv, r, seq++); - if (got != r->want_exit) { - fprintf(stderr, "structvariant[cs][%s]: exit=%d want=%d\n", - r->label, got, r->want_exit); + if (asm_byte_identical(cdrv, wdrv, r, seq++) != 0) { + fprintf(stderr, "structvariant[byte-id][%s]: cstage vs wwstage asm differs\n", + r->label); fail++; } } - - if (wwpresent && (r->stage_mask & STAGE_WW)) { - total++; - int got = run_row(wdrv, r, seq++); - if (got != r->want_exit) { - fprintf(stderr, "structvariant[ww][%s]: exit=%d want=%d\n", - r->label, got, r->want_exit); - fail++; - } - if (r->byte_id) { - total++; - if (asm_byte_identical(cdrv, wdrv, r, seq++) != 0) { - fprintf(stderr, "structvariant[byte-id][%s]: cstage vs wwstage asm differs\n", - r->label); - fail++; - } - } - } } if (fail) { diff --git a/test/wcc/786_narrow_alias_deref_store.c b/test/wcc/786_narrow_alias_deref_store.c index c234c05e..2c2e5321 100644 --- a/test/wcc/786_narrow_alias_deref_store.c +++ b/test/wcc/786_narrow_alias_deref_store.c @@ -44,6 +44,7 @@ */ #include #include +#include #include #include #include @@ -111,17 +112,28 @@ static const struct row rows[] = { 42, STAGE_CS | STAGE_WW, 1 }, }; -static void +/* nonzero on any cleanup failure; ENOENT tolerated because legs that + * fail early never create the later artifacts. */ +static int cleanup_tmp(const char *tmpdir, const char *base) { char p[1024]; - snprintf(p, sizeof p, "%s/%s", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "rm -rf %s/%s.sepwork %s/pkgc", tmpdir, base, tmpdir); - if (system(p)) {} /* best-effort */ - snprintf(p, sizeof p, "%s/%s.o", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base); unlink(p); - rmdir(tmpdir); + int bad = 0; + snprintf(p, sizeof p, "%s/%s", tmpdir, base); + if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; } + snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); + if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; } + snprintf(p, sizeof p, "rm -rf %s/%s.sepwork", tmpdir, base); + if (runwait(p) != 0) { + fprintf(stderr, "cleanup failed: %s\n", p); + bad = 1; + } + snprintf(p, sizeof p, "%s/%s.o", tmpdir, base); + if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; } + snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base); + if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; } + if (rmdir(tmpdir) != 0) { perror(tmpdir); bad = 1; } + return bad; } static int @@ -129,8 +141,11 @@ write_source(const char *path, const char *src) { FILE *f = fopen(path, "wb"); if (!f) return -1; - fputs(src, f); - fclose(f); + if (fputs(src, f) == EOF) { + fclose(f); + return -1; + } + if (fclose(f) != 0) return -1; return 0; } @@ -139,36 +154,14 @@ build_via_driver(const char *driver, const char *tmpdir, const char *src) { char cmd[2048]; /* #93 sep layout: emit asm to .sepwork/__root.s; pin - * WW_PKGCACHE under tmpdir so out/.pkgcache is untouched. */ + * all outputs stay under tmpdir. */ snprintf(cmd, sizeof cmd, - "cd %s && b='%s'; WW_PKGCACHE=%s/pkgc timeout 180 %s build --sep " + "cd %s && b='%s'; timeout 180 %s build -S " "-o \"${b%%.ww}\" \"$b\" 2>/dev/null", - tmpdir, src, tmpdir, driver); + tmpdir, src, driver); return runwait(cmd); } -/* run_row — build via driver, run the binary, return exit (or -1 on - * build failure). */ -static int -run_row(const char *driver, const struct row *r, int seq) -{ - char tmpdir[256], src[512], base[64], outbin[768]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/nas_%d_d_%d", getpid(), seq); - snprintf(base, sizeof base, "main786"); - snprintf(src, sizeof src, "%s/%s.ww", tmpdir, base); - mkdir(tmpdir, 0755); - if (write_source(src, r->src) != 0) { cleanup_tmp(tmpdir, base); return -1; } - int rc; - if (build_via_driver(driver, tmpdir, src) == 0) { - snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); - rc = runwait(outbin); - } else { - rc = -1; - } - cleanup_tmp(tmpdir, base); - return rc; -} - /* asm_byte_identical — diff cstage vs wwstage .s. Parallel trees so * ww_ww writing intermediates next to the source doesn't clobber the * cstage .s (CLAUDE.md rule 14 phase split). */ @@ -180,11 +173,18 @@ asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r, snprintf(tdc, sizeof tdc, "/tmp/nas_%d_c_%d", getpid(), seq); snprintf(tdw, sizeof tdw, "/tmp/nas_%d_w_%d", getpid(), seq); snprintf(base, sizeof base, "main786"); - mkdir(tdc, 0755); - mkdir(tdw, 0755); + int rc = -1, tdw_owned = 0, cleanfail = 0; + if (mkdir(tdc, 0755) != 0) { + perror(tdc); + return -1; + } + if (mkdir(tdw, 0755) != 0) { + perror(tdw); + goto out; + } + tdw_owned = 1; snprintf(src, sizeof src, "%s/%s.ww", tdc, base); - if (write_source(src, r->src) != 0) { cleanup_tmp(tdc, base); cleanup_tmp(tdw, base); return -1; } - int rc = -1; + if (write_source(src, r->src) != 0) goto out; if (build_via_driver(cdrv, tdc, src) != 0) goto out; snprintf(cs, sizeof cs, "%s/%s.sepwork/__root.s", tdc, base); @@ -207,8 +207,13 @@ asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r, if (fc) fclose(fc); if (fw) fclose(fw); out: - cleanup_tmp(tdc, base); - cleanup_tmp(tdw, base); + cleanfail = cleanup_tmp(tdc, base); + if (tdw_owned && cleanup_tmp(tdw, base) != 0) + cleanfail = 1; + /* a leaked tree fails an otherwise-green row; a real diff result + * (rc == -1) is never overwritten (110's cleanfail shape). */ + if (cleanfail && rc == 0) + rc = -1; return rc; } @@ -236,33 +241,14 @@ main(void) for (int i = 0; i < n; i++) { const struct row *r = &rows[i]; - if (r->stage_mask & STAGE_CS) { + if (wwpresent && r->byte_id) { total++; - int got = run_row(cdrv, r, seq++); - if (got != r->want_exit) { - fprintf(stderr, "narrow_alias[cs][%s]: exit=%d want=%d\n", - r->label, got, r->want_exit); + if (asm_byte_identical(cdrv, wdrv, r, seq++) != 0) { + fprintf(stderr, "narrow_alias[byte-id][%s]: cstage vs wwstage asm differs\n", + r->label); fail++; } } - - if (wwpresent && (r->stage_mask & STAGE_WW)) { - total++; - int got = run_row(wdrv, r, seq++); - if (got != r->want_exit) { - fprintf(stderr, "narrow_alias[ww][%s]: exit=%d want=%d\n", - r->label, got, r->want_exit); - fail++; - } - if (r->byte_id) { - total++; - if (asm_byte_identical(cdrv, wdrv, r, seq++) != 0) { - fprintf(stderr, "narrow_alias[byte-id][%s]: cstage vs wwstage asm differs\n", - r->label); - fail++; - } - } - } } if (fail) { diff --git a/test/wcc/787_xmod_variant_match.c b/test/wcc/787_xmod_variant_match.c index 5fa65a5a..c39917ab 100644 --- a/test/wcc/787_xmod_variant_match.c +++ b/test/wcc/787_xmod_variant_match.c @@ -56,6 +56,7 @@ */ #include #include +#include #include #include #include @@ -82,7 +83,9 @@ slurp_eq(const char *a, const char *b) if (ca != cb) { rc = -1; break; } if (ca == EOF) break; } - fclose(fa); fclose(fb); + if (ferror(fa) || ferror(fb)) rc = -1; + if (fclose(fa) != 0) rc = -1; + if (fclose(fb) != 0) rc = -1; return rc; } @@ -241,28 +244,30 @@ run_scenario(const char *bin, const char *cdrv, const char *wdrv, FILE *f = fopen(path, "wb"); if (!f) { fprintf(stderr, "787[%s]: write %s\n", sc->label, sc->files[i].name); rc = -1; goto done; } - fputs(sc->files[i].src, f); - fclose(f); + int bad = fputs(sc->files[i].src, f) == EOF; + if (fclose(f) != 0) bad = 1; + if (bad) { fprintf(stderr, "787[%s]: write %s\n", sc->label, + sc->files[i].name); rc = -1; goto done; } } if (sc->expect_reject) { /* #94 sep layout: there is no combined unit to re-check. The - * checker reject now fires inside each driver's --sep w6c pass, + * checker reject now fires inside each driver's w6c pass, * so BOTH driver builds MUST fail. w6c_ww (via ww_ww) accepting * here is the #13 false-ACCEPT regression. */ snprintf(cmd, sizeof cmd, - "cd %s && WW_PKGCACHE=%s/pkgc_c %s build --sep -I %s " + "cd %s && %s build -I %s " "-o %s/main %s/main.ww >/dev/null 2>&1", - dir, dir, cdrv, dir, dir, dir); + dir, cdrv, dir, dir, dir); if (runwait(cmd) == 0) { fprintf(stderr, "787[%s]: cstage build SUCCEEDED, " "expected reject\n", sc->label); rc = -1; goto done; } snprintf(cmd, sizeof cmd, - "cd %s && WW_PKGCACHE=%s/pkgc_w %s build --sep -I %s " + "cd %s && %s build -I %s " "-o %s/mainww %s/main.ww >/dev/null 2>&1", - dir, dir, wdrv, dir, dir, dir); + dir, wdrv, dir, dir, dir); if (runwait(cmd) == 0) { fprintf(stderr, "787[%s]: ww_ww ACCEPTED foreign variant " "(#13 false-accept), expected reject\n", sc->label); @@ -271,11 +276,11 @@ run_scenario(const char *bin, const char *cdrv, const char *wdrv, goto done; } - /* cstage driver --sep build + run: pins runtime routing. Pin - * WW_PKGCACHE under the scratch dir so out/.pkgcache is untouched. */ + /* cstage driver build + run: pins runtime routing. Pin + * all outputs stay under the scratch dir. */ snprintf(cmd, sizeof cmd, - "cd %s && WW_PKGCACHE=%s/pkgc_c %s build --sep -I %s -o %s/main %s/main.ww", - dir, dir, cdrv, dir, dir, dir); + "cd %s && %s build -I %s -o %s/main %s/main.ww", + dir, cdrv, dir, dir, dir); if (runwait(cmd) != 0) { fprintf(stderr, "787[%s]: cstage build failed\n", sc->label); rc = -1; goto done; @@ -288,15 +293,15 @@ run_scenario(const char *bin, const char *cdrv, const char *wdrv, rc = -1; } - /* The #13 discriminator: the wwstage driver's --sep build runs the + /* The #13 discriminator: the wwstage driver's build runs the * wwstage checker over the same units. Pre-fix it REJECTED the * cross-module variant arms (build fails); post-fix it accepts AND * its per-package w6c_ww asm is byte-id with cstage's. The root + * each imported pkg compile to separate .sepwork/.s; * concat (sorted glob, identical set both stages) for the compare. */ snprintf(cmd, sizeof cmd, - "cd %s && WW_PKGCACHE=%s/pkgc_w %s build --sep -I %s -o %s/mainww %s/main.ww", - dir, dir, wdrv, dir, dir, dir); + "cd %s && %s build -I %s -o %s/mainww %s/main.ww", + dir, wdrv, dir, dir, dir); if (runwait(cmd) != 0) { fprintf(stderr, "787[%s]: ww_ww build failed " "(#13 cross-module variant reject?)\n", sc->label); @@ -305,10 +310,22 @@ run_scenario(const char *bin, const char *cdrv, const char *wdrv, char cs_s[1024], ws_s[1024]; snprintf(cs_s, sizeof cs_s, "%s/all_cs.s", dir); snprintf(ws_s, sizeof ws_s, "%s/all_ww.s", dir); - snprintf(cmd, sizeof cmd, "cat %s/main.sepwork/*.s > %s 2>/dev/null", - dir, cs_s); if (system(cmd)) {} - snprintf(cmd, sizeof cmd, "cat %s/mainww.sepwork/*.s > %s 2>/dev/null", - dir, ws_s); if (system(cmd)) {} + snprintf(cmd, sizeof cmd, + "cat %s/main.sepwork/*.s > %s 2>/dev/null && test -s %s", + dir, cs_s, cs_s); + if (runwait(cmd) != 0) { + fprintf(stderr, "787[%s]: cstage asm aggregation failed\n", + sc->label); + rc = -1; goto done; + } + snprintf(cmd, sizeof cmd, + "cat %s/mainww.sepwork/*.s > %s 2>/dev/null && test -s %s", + dir, ws_s, ws_s); + if (runwait(cmd) != 0) { + fprintf(stderr, "787[%s]: wwstage asm aggregation failed\n", + sc->label); + rc = -1; goto done; + } if (slurp_eq(cs_s, ws_s) != 0) { fprintf(stderr, "787[%s]: cs.s/ww.s DIFFER (rule-10 byte-id " "violation)\n", sc->label); @@ -316,8 +333,33 @@ run_scenario(const char *bin, const char *cdrv, const char *wdrv, } done: - snprintf(cmd, sizeof cmd, "rm -rf %s", dir); - (void)runwait(cmd); + snprintf(cmd, sizeof cmd, "rm -rf %s/main.sepwork", dir); + if (runwait(cmd) != 0) { fprintf(stderr, "787[%s]: cleanup main.sepwork\n", + sc->label); rc = -1; } + snprintf(cmd, sizeof cmd, "rm -rf %s/mainww.sepwork", dir); + if (runwait(cmd) != 0) { fprintf(stderr, "787[%s]: cleanup mainww.sepwork\n", + sc->label); rc = -1; } + for (int i = 0; sc->files[i].name; i++) { + snprintf(path, sizeof path, "%s/%s", dir, sc->files[i].name); + if (unlink(path) != 0 && errno != ENOENT) { + fprintf(stderr, "787[%s]: cleanup %s\n", sc->label, + sc->files[i].name); + rc = -1; + } + } + const char *children[] = { "main", "mainww", "all_cs.s", "all_ww.s", NULL }; + for (int i = 0; children[i]; i++) { + snprintf(path, sizeof path, "%s/%s", dir, children[i]); + if (unlink(path) != 0 && errno != ENOENT) { + fprintf(stderr, "787[%s]: cleanup %s\n", sc->label, + children[i]); + rc = -1; + } + } + if (rmdir(dir) != 0) { + fprintf(stderr, "787[%s]: cleanup directory\n", sc->label); + rc = -1; + } return rc; } diff --git a/test/wcc/789_named_ptr_alias_variant_widen.c b/test/wcc/789_named_ptr_alias_variant_widen.c index 189f9d0b..ef16fde3 100644 --- a/test/wcc/789_named_ptr_alias_variant_widen.c +++ b/test/wcc/789_named_ptr_alias_variant_widen.c @@ -31,17 +31,18 @@ * | (= *vtable) variant of (file|stream), | | * | summed 10x via the stream match arm | | * | (pre-fix took the file arm → 86) | | - * degenerate_ambig | (sa | sb), both = *vtable; a bare | both | (reject - * | *vtable widen matches BOTH NAMED |reject| net) - * | variants → guard must LOUD hard-error | | - * | on BOTH stages (drew proviso lock) | | * * GATE POLARITY: must stay GREEN. Red on handle_widen means the - * NAMED-ptr-alias-variant widen regressed (wrong tag); red on - * degenerate_ambig means the >=2 ambiguity guard stopped firing. + * NAMED-ptr-alias-variant widen regressed (wrong tag). + * + * The symmetric degenerate_ambig rejection now lives in + * test/wcc/data/r78_named_ptr_alias_degenerate_ambig/case.ww, where the + * shared C/WW diagnostic pins the >=2 ambiguity guard directly. This + * wrapper retains the positive runtime and assembly-byte-identity evidence. */ #include #include +#include #include #include #include @@ -64,7 +65,6 @@ struct row { int want_exit; int stage_mask; int byte_id; - int expect_reject; /* 1 = BOTH stages must hard-error */ }; static const struct row rows[] = { @@ -90,33 +90,31 @@ static const struct row rows[] = { " if (sum == 40u64) { return 42; };\n" " return (sum: i32);\n" "};\n", - 42, STAGE_CS | STAGE_WW, 1, 0 }, - - /* GUARD lock (drew proviso): a bare *vtable widened into a union of - * TWO NAMED *vtable aliases is ambiguous → BOTH stages hard-error. */ - { "degenerate_ambig", - "package main;\n" - "type vtable = struct { x: i32 };\n" - "type sa = *vtable;\n" - "type sb = *vtable;\n" - "type h = (sa | sb);\n" - "fn w(x: h) i32 = { match (x) { case sa => return 1; case sb => return 2; }; };\n" - "fn emit(v: *vtable) i32 = { return w(v); };\n" - "export fn main() i32 = { let vt: vtable; vt.x = 0; return emit(&vt); };\n", - 0, STAGE_CS | STAGE_WW, 0, 1 }, + 42, STAGE_CS | STAGE_WW, 1 }, }; -static void +/* nonzero on any cleanup failure; ENOENT tolerated because legs that + * fail early never create the later artifacts. */ +static int cleanup_tmp(const char *tmpdir, const char *base) { char p[1024]; - snprintf(p, sizeof p, "%s/%s", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "rm -rf %s/%s.sepwork %s/pkgc", tmpdir, base, tmpdir); - if (system(p)) {} /* best-effort */ - snprintf(p, sizeof p, "%s/%s.o", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base); unlink(p); - rmdir(tmpdir); + int bad = 0; + snprintf(p, sizeof p, "%s/%s", tmpdir, base); + if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; } + snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); + if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; } + snprintf(p, sizeof p, "rm -rf %s/%s.sepwork", tmpdir, base); + if (runwait(p) != 0) { + fprintf(stderr, "cleanup failed: %s\n", p); + bad = 1; + } + snprintf(p, sizeof p, "%s/%s.o", tmpdir, base); + if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; } + snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base); + if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; } + if (rmdir(tmpdir) != 0) { perror(tmpdir); bad = 1; } + return bad; } static int @@ -124,8 +122,11 @@ write_source(const char *path, const char *src) { FILE *f = fopen(path, "wb"); if (!f) return -1; - fputs(src, f); - fclose(f); + if (fputs(src, f) == EOF) { + fclose(f); + return -1; + } + if (fclose(f) != 0) return -1; return 0; } @@ -134,49 +135,14 @@ build_via_driver(const char *driver, const char *tmpdir, const char *src) { char cmd[2048]; /* #93 sep layout: emit asm to .sepwork/__root.s; pin - * WW_PKGCACHE under tmpdir so out/.pkgcache is untouched. */ + * all outputs stay under tmpdir. */ snprintf(cmd, sizeof cmd, - "cd %s && b='%s'; WW_PKGCACHE=%s/pkgc timeout 180 %s build --sep " + "cd %s && b='%s'; timeout 180 %s build -S " "-o \"${b%%.ww}\" \"$b\" 2>/dev/null", - tmpdir, src, tmpdir, driver); + tmpdir, src, driver); return runwait(cmd); } -static int -run_row(const char *driver, const struct row *r, int seq) -{ - char tmpdir[256], src[512], base[64], outbin[768]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/npav_%d_d_%d", getpid(), seq); - snprintf(base, sizeof base, "main789"); - snprintf(src, sizeof src, "%s/%s.ww", tmpdir, base); - mkdir(tmpdir, 0755); - if (write_source(src, r->src) != 0) { cleanup_tmp(tmpdir, base); return -1; } - int rc; - if (build_via_driver(driver, tmpdir, src) == 0) { - snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); - rc = runwait(outbin); - } else { - rc = -1; - } - cleanup_tmp(tmpdir, base); - return rc; -} - -/* build_fails — 1 iff the driver build FAILS (used by expect_reject). */ -static int -build_fails(const char *driver, const struct row *r, int seq) -{ - char tmpdir[256], src[512], base[64]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/npav_%d_x_%d", getpid(), seq); - snprintf(base, sizeof base, "main789"); - snprintf(src, sizeof src, "%s/%s.ww", tmpdir, base); - mkdir(tmpdir, 0755); - if (write_source(src, r->src) != 0) { cleanup_tmp(tmpdir, base); return 0; } - int br = build_via_driver(driver, tmpdir, src); - cleanup_tmp(tmpdir, base); - return br != 0; -} - static int asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r, int seq) @@ -185,11 +151,18 @@ asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r, snprintf(tdc, sizeof tdc, "/tmp/npav_%d_c_%d", getpid(), seq); snprintf(tdw, sizeof tdw, "/tmp/npav_%d_w_%d", getpid(), seq); snprintf(base, sizeof base, "main789"); - mkdir(tdc, 0755); - mkdir(tdw, 0755); + int rc = -1, tdw_owned = 0, cleanfail = 0; + if (mkdir(tdc, 0755) != 0) { + perror(tdc); + return -1; + } + if (mkdir(tdw, 0755) != 0) { + perror(tdw); + goto out; + } + tdw_owned = 1; snprintf(src, sizeof src, "%s/%s.ww", tdc, base); - if (write_source(src, r->src) != 0) { cleanup_tmp(tdc, base); cleanup_tmp(tdw, base); return -1; } - int rc = -1; + if (write_source(src, r->src) != 0) goto out; if (build_via_driver(cdrv, tdc, src) != 0) goto out; snprintf(cs, sizeof cs, "%s/%s.sepwork/__root.s", tdc, base); snprintf(src, sizeof src, "%s/%s.ww", tdw, base); @@ -210,8 +183,13 @@ asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r, if (fc) fclose(fc); if (fw) fclose(fw); out: - cleanup_tmp(tdc, base); - cleanup_tmp(tdw, base); + cleanfail = cleanup_tmp(tdc, base); + if (tdw_owned && cleanup_tmp(tdw, base) != 0) + cleanfail = 1; + /* a leaked tree fails an otherwise-green row; a real diff result + * (rc == -1) is never overwritten (110's cleanfail shape). */ + if (cleanfail && rc == 0) + rc = -1; return rc; } @@ -239,47 +217,12 @@ main(void) for (int i = 0; i < n; i++) { const struct row *r = &rows[i]; - if (r->stage_mask & STAGE_CS) { + if (wwpresent && r->byte_id) { total++; - if (r->expect_reject) { - if (!build_fails(cdrv, r, seq++)) { - fprintf(stderr, "npav[cs][%s]: built but expected reject\n", - r->label); - fail++; - } - } else { - int got = run_row(cdrv, r, seq++); - if (got != r->want_exit) { - fprintf(stderr, "npav[cs][%s]: exit=%d want=%d\n", - r->label, got, r->want_exit); - fail++; - } - } - } - - if (wwpresent && (r->stage_mask & STAGE_WW)) { - total++; - if (r->expect_reject) { - if (!build_fails(wdrv, r, seq++)) { - fprintf(stderr, "npav[ww][%s]: built but expected reject\n", - r->label); - fail++; - } - } else { - int got = run_row(wdrv, r, seq++); - if (got != r->want_exit) { - fprintf(stderr, "npav[ww][%s]: exit=%d want=%d\n", - r->label, got, r->want_exit); - fail++; - } - if (r->byte_id) { - total++; - if (asm_byte_identical(cdrv, wdrv, r, seq++) != 0) { - fprintf(stderr, "npav[byte-id][%s]: cstage vs wwstage asm differs\n", - r->label); - fail++; - } - } + if (asm_byte_identical(cdrv, wdrv, r, seq++) != 0) { + fprintf(stderr, "npav[byte-id][%s]: cstage vs wwstage asm differs\n", + r->label); + fail++; } } } diff --git a/test/wcc/790_single_field_struct_zeroinit.c b/test/wcc/790_single_field_struct_zeroinit.c index c4ace0ff..e1ddfb1c 100644 --- a/test/wcc/790_single_field_struct_zeroinit.c +++ b/test/wcc/790_single_field_struct_zeroinit.c @@ -35,6 +35,7 @@ */ #include #include +#include #include #include #include @@ -91,17 +92,29 @@ static const struct row rows[] = { 0, STAGE_CS | STAGE_WW, 1 }, }; -static void +/* nonzero on any cleanup failure; ENOENT tolerated because legs that + * fail early never create the later artifacts (a -S build also never + * produces the base binary / .o / .combined.ww). */ +static int cleanup_tmp(const char *tmpdir, const char *base) { char p[1024]; - snprintf(p, sizeof p, "%s/%s", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "rm -rf %s/%s.sepwork %s/pkgc", tmpdir, base, tmpdir); - if (system(p)) {} /* best-effort */ - snprintf(p, sizeof p, "%s/%s.o", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base); unlink(p); - rmdir(tmpdir); + int bad = 0; + snprintf(p, sizeof p, "%s/%s", tmpdir, base); + if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; } + snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); + if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; } + snprintf(p, sizeof p, "rm -rf %s/%s.sepwork", tmpdir, base); + if (runwait(p) != 0) { + fprintf(stderr, "cleanup failed: %s\n", p); + bad = 1; + } + snprintf(p, sizeof p, "%s/%s.o", tmpdir, base); + if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; } + snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base); + if (unlink(p) != 0 && errno != ENOENT) { perror(p); bad = 1; } + if (rmdir(tmpdir) != 0) { perror(tmpdir); bad = 1; } + return bad; } static int @@ -109,8 +122,11 @@ write_source(const char *path, const char *src) { FILE *f = fopen(path, "wb"); if (!f) return -1; - fputs(src, f); - fclose(f); + if (fputs(src, f) == EOF) { + fclose(f); + return -1; + } + if (fclose(f) != 0) return -1; return 0; } @@ -119,34 +135,14 @@ build_via_driver(const char *driver, const char *tmpdir, const char *src) { char cmd[2048]; /* #93 sep layout: emit asm to .sepwork/__root.s; pin - * WW_PKGCACHE under tmpdir so out/.pkgcache is untouched. */ + * all outputs stay under tmpdir. */ snprintf(cmd, sizeof cmd, - "cd %s && b='%s'; WW_PKGCACHE=%s/pkgc timeout 180 %s build --sep " + "cd %s && b='%s'; timeout 180 %s build -S " "-o \"${b%%.ww}\" \"$b\" 2>/dev/null", - tmpdir, src, tmpdir, driver); + tmpdir, src, driver); return runwait(cmd); } -static int -run_row(const char *driver, const struct row *r, int seq) -{ - char tmpdir[256], src[512], base[64], outbin[768]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/sfsz_%d_d_%d", getpid(), seq); - snprintf(base, sizeof base, "main790"); - snprintf(src, sizeof src, "%s/%s.ww", tmpdir, base); - mkdir(tmpdir, 0755); - if (write_source(src, r->src) != 0) { cleanup_tmp(tmpdir, base); return -1; } - int rc; - if (build_via_driver(driver, tmpdir, src) == 0) { - snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); - rc = runwait(outbin); - } else { - rc = -1; - } - cleanup_tmp(tmpdir, base); - return rc; -} - static int asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r, int seq) @@ -155,11 +151,18 @@ asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r, snprintf(tdc, sizeof tdc, "/tmp/sfsz_%d_c_%d", getpid(), seq); snprintf(tdw, sizeof tdw, "/tmp/sfsz_%d_w_%d", getpid(), seq); snprintf(base, sizeof base, "main790"); - mkdir(tdc, 0755); - mkdir(tdw, 0755); + int rc = -1, tdw_owned = 0, cleanfail = 0; + if (mkdir(tdc, 0755) != 0) { + perror(tdc); + return -1; + } + if (mkdir(tdw, 0755) != 0) { + perror(tdw); + goto out; + } + tdw_owned = 1; snprintf(src, sizeof src, "%s/%s.ww", tdc, base); - if (write_source(src, r->src) != 0) { cleanup_tmp(tdc, base); cleanup_tmp(tdw, base); return -1; } - int rc = -1; + if (write_source(src, r->src) != 0) goto out; if (build_via_driver(cdrv, tdc, src) != 0) goto out; snprintf(cs, sizeof cs, "%s/%s.sepwork/__root.s", tdc, base); snprintf(src, sizeof src, "%s/%s.ww", tdw, base); @@ -180,8 +183,13 @@ asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r, if (fc) fclose(fc); if (fw) fclose(fw); out: - cleanup_tmp(tdc, base); - cleanup_tmp(tdw, base); + cleanfail = cleanup_tmp(tdc, base); + if (tdw_owned && cleanup_tmp(tdw, base) != 0) + cleanfail = 1; + /* a leaked tree fails an otherwise-green row; a real diff result + * (rc == -1) is never overwritten (110's cleanfail shape). */ + if (cleanfail && rc == 0) + rc = -1; return rc; } @@ -209,33 +217,14 @@ main(void) for (int i = 0; i < n; i++) { const struct row *r = &rows[i]; - if (r->stage_mask & STAGE_CS) { + if (wwpresent && r->byte_id) { total++; - int got = run_row(cdrv, r, seq++); - if (got != r->want_exit) { - fprintf(stderr, "sfsz[cs][%s]: exit=%d want=%d\n", - r->label, got, r->want_exit); + if (asm_byte_identical(cdrv, wdrv, r, seq++) != 0) { + fprintf(stderr, "sfsz[byte-id][%s]: cstage vs wwstage asm differs\n", + r->label); fail++; } } - - if (wwpresent && (r->stage_mask & STAGE_WW)) { - total++; - int got = run_row(wdrv, r, seq++); - if (got != r->want_exit) { - fprintf(stderr, "sfsz[ww][%s]: exit=%d want=%d\n", - r->label, got, r->want_exit); - fail++; - } - if (r->byte_id) { - total++; - if (asm_byte_identical(cdrv, wdrv, r, seq++) != 0) { - fprintf(stderr, "sfsz[byte-id][%s]: cstage vs wwstage asm differs\n", - r->label); - fail++; - } - } - } } if (fail) { diff --git a/test/wcc/793_xmod_struct_argpush_collide_run.c b/test/wcc/793_xmod_struct_argpush_collide_run.c index 938f8c47..0cbde27b 100644 --- a/test/wcc/793_xmod_struct_argpush_collide_run.c +++ b/test/wcc/793_xmod_struct_argpush_collide_run.c @@ -39,13 +39,14 @@ * combined_t16 | {u64,u16} 16B | 24B | recv+field+push| 114 * recvpush_t12 | {u32,u32,u32} 12B | 24B | recv + push | 6 * - * cstage `ww build --sep` + run pins runtime; the wwstage binary is run + * cstage `ww build` + run pins runtime; the wwstage binary is run * too (the bug WAS a wrong wwstage runtime value); raw cs.s vs ww.s cmp * over the driver-produced per-package asm pins rule-10 byte-id (the * discriminating net — pre-fix __root.s diverges at the push/recv/field). */ #include #include +#include #include #include #include @@ -72,7 +73,9 @@ slurp_eq(const char *a, const char *b) if (ca != cb) { rc = -1; break; } if (ca == EOF) break; } - fclose(fa); fclose(fb); + if (ferror(fa) || ferror(fb)) rc = -1; + if (fclose(fa) != 0) rc = -1; + if (fclose(fb) != 0) rc = -1; return rc; } @@ -201,15 +204,17 @@ run_scenario(const char *cdrv, const char *wdrv, const struct scenario *sc) FILE *f = fopen(path, "wb"); if (!f) { fprintf(stderr, "793[%s]: write %s\n", sc->label, sc->files[i].name); rc = -1; goto done; } - fputs(sc->files[i].src, f); - fclose(f); + int bad = fputs(sc->files[i].src, f) == EOF; + if (fclose(f) != 0) bad = 1; + if (bad) { fprintf(stderr, "793[%s]: write %s\n", sc->label, + sc->files[i].name); rc = -1; goto done; } } - /* cstage --sep build + run pins runtime; pin WW_PKGCACHE under the - * scratch dir so out/.pkgcache is untouched. */ + /* cstage build + run pins runtime; all outputs remain under the + * scratch dir. */ snprintf(cmd, sizeof cmd, - "cd %s && WW_PKGCACHE=%s/pkgc_c %s build --sep -I %s -o %s/main %s/main.ww", - dir, dir, cdrv, dir, dir, dir); + "cd %s && %s build -I %s -o %s/main %s/main.ww", + dir, cdrv, dir, dir, dir); if (runwait(cmd) != 0) { fprintf(stderr, "793[%s]: cstage build failed\n", sc->label); rc = -1; goto done; @@ -222,11 +227,11 @@ run_scenario(const char *cdrv, const char *wdrv, const struct scenario *sc) rc = -1; } - /* wwstage --sep build + run: the bug WAS a wrong wwstage runtime + /* wwstage build + run: the bug WAS a wrong wwstage runtime * value (a dropped/over-copied field), so run the wwstage binary too. */ snprintf(cmd, sizeof cmd, - "cd %s && WW_PKGCACHE=%s/pkgc_w %s build --sep -I %s -o %s/mainww %s/main.ww", - dir, dir, wdrv, dir, dir, dir); + "cd %s && %s build -I %s -o %s/mainww %s/main.ww", + dir, wdrv, dir, dir, dir); if (runwait(cmd) != 0) { fprintf(stderr, "793[%s]: ww_ww build failed\n", sc->label); rc = -1; goto done; @@ -245,10 +250,22 @@ run_scenario(const char *cdrv, const char *wdrv, const struct scenario *sc) char cs_s[1024], ws_s[1024]; snprintf(cs_s, sizeof cs_s, "%s/all_cs.s", dir); snprintf(ws_s, sizeof ws_s, "%s/all_ww.s", dir); - snprintf(cmd, sizeof cmd, "cat %s/main.sepwork/*.s > %s 2>/dev/null", - dir, cs_s); if (system(cmd)) {} - snprintf(cmd, sizeof cmd, "cat %s/mainww.sepwork/*.s > %s 2>/dev/null", - dir, ws_s); if (system(cmd)) {} + snprintf(cmd, sizeof cmd, + "cat %s/main.sepwork/*.s > %s 2>/dev/null && test -s %s", + dir, cs_s, cs_s); + if (runwait(cmd) != 0) { + fprintf(stderr, "793[%s]: cstage asm aggregation failed\n", + sc->label); + rc = -1; goto done; + } + snprintf(cmd, sizeof cmd, + "cat %s/mainww.sepwork/*.s > %s 2>/dev/null && test -s %s", + dir, ws_s, ws_s); + if (runwait(cmd) != 0) { + fprintf(stderr, "793[%s]: wwstage asm aggregation failed\n", + sc->label); + rc = -1; goto done; + } if (slurp_eq(cs_s, ws_s) != 0) { fprintf(stderr, "793[%s]: cs.s/ww.s DIFFER (rule-10 byte-id " "violation — #21/#224 regression)\n", sc->label); @@ -256,8 +273,33 @@ run_scenario(const char *cdrv, const char *wdrv, const struct scenario *sc) } done: - snprintf(cmd, sizeof cmd, "rm -rf %s", dir); - (void)runwait(cmd); + snprintf(cmd, sizeof cmd, "rm -rf %s/main.sepwork", dir); + if (runwait(cmd) != 0) { fprintf(stderr, "793[%s]: cleanup main.sepwork\n", + sc->label); rc = -1; } + snprintf(cmd, sizeof cmd, "rm -rf %s/mainww.sepwork", dir); + if (runwait(cmd) != 0) { fprintf(stderr, "793[%s]: cleanup mainww.sepwork\n", + sc->label); rc = -1; } + for (int i = 0; sc->files[i].name; i++) { + snprintf(path, sizeof path, "%s/%s", dir, sc->files[i].name); + if (unlink(path) != 0 && errno != ENOENT) { + fprintf(stderr, "793[%s]: cleanup %s\n", sc->label, + sc->files[i].name); + rc = -1; + } + } + const char *children[] = { "main", "mainww", "all_cs.s", "all_ww.s", NULL }; + for (int i = 0; children[i]; i++) { + snprintf(path, sizeof path, "%s/%s", dir, children[i]); + if (unlink(path) != 0 && errno != ENOENT) { + fprintf(stderr, "793[%s]: cleanup %s\n", sc->label, + children[i]); + rc = -1; + } + } + if (rmdir(dir) != 0) { + fprintf(stderr, "793[%s]: cleanup directory\n", sc->label); + rc = -1; + } return rc; } diff --git a/test/wcc/794_xmod_ident_prefer.c b/test/wcc/794_xmod_ident_prefer.c index 5042b26d..8626786a 100644 --- a/test/wcc/794_xmod_ident_prefer.c +++ b/test/wcc/794_xmod_ident_prefer.c @@ -51,6 +51,7 @@ */ #include #include +#include #include #include #include @@ -119,18 +120,20 @@ main(void) FILE *f = fopen(path, "wb"); if (!f) { fprintf(stderr, "794: write %s\n", files[i].name); rc = 1; goto done; } - fputs(files[i].src, f); - fclose(f); + int bad = fputs(files[i].src, f) == EOF; + if (fclose(f) != 0) bad = 1; + if (bad) { fprintf(stderr, "794: write %s\n", files[i].name); + rc = 1; goto done; } } - /* #94 sep layout: cstage's --sep build (prefer is correct) compiles - * the units and links the binary; pin WW_PKGCACHE under the scratch - * dir so out/.pkgcache is untouched. NO cs==ww byte-id here — the #55 + /* #94 sep layout: cstage's build (prefer is correct) compiles + * the units and links the binary under the scratch dir. NO cs==ww + * byte-id here — the #55 * checker fix has an open cgen-side bare-leaf sibling that diverges the * asm independently; the sound discriminator is the wwstage ACCEPT. */ snprintf(cmd, sizeof cmd, - "cd %s && WW_PKGCACHE=%s/pkgc_c %s build --sep -I %s -o %s/main %s/main.ww", - dir, dir, cdrv, dir, dir, dir); + "cd %s && %s build -I %s -o %s/main %s/main.ww", + dir, cdrv, dir, dir, dir); if (runwait(cmd) != 0) { fprintf(stderr, "794: cstage build failed\n"); rc = 1; goto done; @@ -143,14 +146,14 @@ main(void) rc = 1; goto done; } - /* The #55 discriminator: the wwstage driver's --sep build runs the + /* The #55 discriminator: the wwstage driver's build runs the * wwstage checker over the same units and must ACCEPT. PRE-fix it * rejected (bare `v` -> main.v: i64 -> return mismatch); POST-fix it * prefers curmod=aa -> aa.v: i32 and accepts. */ snprintf(cmd, sizeof cmd, - "cd %s && WW_PKGCACHE=%s/pkgc_w %s build --sep -I %s -o %s/mainww %s/main.ww " + "cd %s && %s build -I %s -o %s/mainww %s/main.ww " ">/dev/null 2>&1", - dir, dir, wdrv, dir, dir, dir); + dir, wdrv, dir, dir, dir); if (runwait(cmd) != 0) { fprintf(stderr, "794: ww_ww REJECTED the units (#55: bare " "value-ident resolved the wrong module's same-leaf symbol)\n"); @@ -158,8 +161,31 @@ main(void) } done: - snprintf(cmd, sizeof cmd, "rm -rf %s", dir); - (void)runwait(cmd); + snprintf(cmd, sizeof cmd, "rm -rf %s/main.sepwork", dir); + if (runwait(cmd) != 0) { fprintf(stderr, "794: cleanup main.sepwork\n"); + rc = 1; } + snprintf(cmd, sizeof cmd, "rm -rf %s/mainww.sepwork", dir); + if (runwait(cmd) != 0) { fprintf(stderr, "794: cleanup mainww.sepwork\n"); + rc = 1; } + for (int i = 0; files[i].name; i++) { + snprintf(path, sizeof path, "%s/%s", dir, files[i].name); + if (unlink(path) != 0 && errno != ENOENT) { + fprintf(stderr, "794: cleanup %s\n", files[i].name); + rc = 1; + } + } + const char *children[] = { "main", "mainww", NULL }; + for (int i = 0; children[i]; i++) { + snprintf(path, sizeof path, "%s/%s", dir, children[i]); + if (unlink(path) != 0 && errno != ENOENT) { + fprintf(stderr, "794: cleanup %s\n", children[i]); + rc = 1; + } + } + if (rmdir(dir) != 0) { + fprintf(stderr, "794: cleanup directory\n"); + rc = 1; + } if (rc) { fprintf(stderr, "794 xmod_ident_prefer: FAILED\n"); return 1; diff --git a/test/wcc/795_xmod_valglobal_run.c b/test/wcc/795_xmod_valglobal_run.c index 178dc5de..8960eb52 100644 --- a/test/wcc/795_xmod_valglobal_run.c +++ b/test/wcc/795_xmod_valglobal_run.c @@ -1,7 +1,7 @@ /* * 795_xmod_valglobal_run — project #1 close, the cgen residual of #55. - * Runtime + cs==ww byte-id net for the cross-module bare value-GLOBAL - * load miscompile. Sibling of the CHECKER test 794_xmod_ident_prefer, + * Assembly byte-id net for the cross-module bare value-GLOBAL load + * miscompile. Sibling of the CHECKER test 794_xmod_ident_prefer, * which deliberately omitted a byte-id assertion because THIS cgen bug * would diverge the asm independently (see 794's NOTE). With #1 fixed, * the byte-id now holds and is asserted here. @@ -29,13 +29,8 @@ * exported aa.v stays `v`, the private main.v stays `main.v`, no * collision. * - * EACH ROW CARRIES BOTH DIMENSIONS (953_f64crossmod_run model): - * (a) cstage `ww build` (in a /tmp scratch dir) + run, asserting the - * exit code — pins that the converged asm is runtime-correct. - * (b) w6c vs w6c_ww `.s` cmp — FAILS if the stages diverge. Necessary - * because the bug was byte-id-blind (both stages were wrong the - * same way); a green byte-id alone never caught it, so the runtime - * row (a) is the real net and (b) guards rule-10 going forward. + * Runtime ownership moved to the matching r795_* fixtures. This carrier + * retains the w6c vs w6c_ww `.s` comparison required by rule 10. * * Single-file multi-package form (like 953): `package aa; ... package * main; import aa; ...` in one source, so w6c/w6c_ww see the cross- @@ -148,37 +143,30 @@ main(void) * rm -rf at the end reclaims everything. */ char tmpdir[64]; snprintf(tmpdir, sizeof tmpdir, "/tmp/wwxmv_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - - char src[128], outbin[128], rmcmd[160]; - snprintf(src, sizeof src, "%s/wwxmv_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/wwxmv_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); - - FILE *f = fopen(src, "wb"); - if (f == NULL) { fail++; runwait(rmcmd); continue; } - fputs(rows[i].src, f); - fclose(f); - - /* (a) cstage build + run. */ - char cmd[2048]; - snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s", bin, outbin, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: cstage build failed\n", - rows[i].label); + /* an unowned dir must never be rm -rf'd below */ + if (mkdir(tmpdir, 0755) != 0) { + perror(tmpdir); fail++; - runwait(rmcmd); continue; } - int got = runwait(outbin); - if (got != rows[i].want_exit) { - fprintf(stderr, "row[%s]: cstage exit %d, want %d\n", - rows[i].label, got, rows[i].want_exit); - fail++; - } + char src[128], rmcmd[160]; + snprintf(src, sizeof src, "%s/wwxmv_%d_%d.ww", tmpdir, getpid(), i); + snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); - /* (b) cs==ww byte-id gate. */ + FILE *f = fopen(src, "wb"); + if (f == NULL) { + fail++; + if (runwait(rmcmd) != 0) + fprintf(stderr, "row[%s]: cleanup failed: %s\n", + rows[i].label, rmcmd); + continue; + } + fputs(rows[i].src, f); + fclose(f); + + char cmd[2048]; + /* Runtime ownership is r79_xmod_valglobal_*; retain byte-id. */ char cs_s[128], ws_s[128]; snprintf(cs_s, sizeof cs_s, "%s/wwxmv_%d_%d_cs.s", tmpdir, getpid(), i); snprintf(ws_s, sizeof ws_s, "%s/wwxmv_%d_%d_ww.s", tmpdir, getpid(), i); @@ -186,26 +174,42 @@ main(void) snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, cs_s, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label); - fail++; runwait(rmcmd); continue; + fail++; + if (runwait(rmcmd) != 0) + fprintf(stderr, "row[%s]: cleanup failed: %s\n", + rows[i].label, rmcmd); + continue; } snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c_ww, ws_s, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c_ww failed\n", rows[i].label); - fail++; runwait(rmcmd); continue; + fail++; + if (runwait(rmcmd) != 0) + fprintf(stderr, "row[%s]: cleanup failed: %s\n", + rows[i].label, rmcmd); + continue; } - if (slurp_eq(cs_s, ws_s) != 0) { + int rowdiff = slurp_eq(cs_s, ws_s) != 0; + if (rowdiff) { fprintf(stderr, "row[%s]: cstage/wwstage .s DIFFER " "(rule-10 byte-id violation)\n", rows[i].label); fail++; } - runwait(rmcmd); + /* a leaked tmpdir fails an otherwise-green row without + * masking the byte-id verdict. */ + if (runwait(rmcmd) != 0) { + fprintf(stderr, "row[%s]: cleanup failed: %s\n", + rows[i].label, rmcmd); + if (!rowdiff) + fail++; + } } if (fail) { fprintf(stderr, "%d/%d xmod value-global tests failed\n", fail, n); return 1; } - printf("xmod_valglobal: %d/%d ok (cstage run + cs==ww byte-id)\n", n, n); + printf("xmod_valglobal: %d/%d ok (cs==ww byte-id)\n", n, n); return 0; } diff --git a/test/wcc/796_xmod_valglobal_dot_run.c b/test/wcc/796_xmod_valglobal_dot_run.c index 73d0c4ab..a5d14870 100644 --- a/test/wcc/796_xmod_valglobal_dot_run.c +++ b/test/wcc/796_xmod_valglobal_dot_run.c @@ -1,7 +1,7 @@ /* * 796_xmod_valglobal_dot_run — project #229, the N_DOT-base read + addr-of - * sibling of #1 (test 795). Runtime + cs==ww byte-id net for the cross- - * module DOTTED value-global miscompile. + * sibling of #1 (test 795). Assembly byte-id net for the cross-module + * DOTTED value-global miscompile. * * THE BUG (cgen-only, both stages emit IDENTICAL wrong asm → byte-id was * BLIND to it): #1 fixed the DATA def-site and the bare-ident load mangle, @@ -19,15 +19,10 @@ * lhs.str / basenm). The TY_FN branches beside each site already use this * dotted polarity via mafn/emitfnname; value globals now match. * - * EACH ROW CARRIES BOTH DIMENSIONS (795 model): - * (a) cstage `ww build` + run, asserting the exit — pins the converged - * asm is runtime-correct. - * (b) w6c vs w6c_ww `.s` cmp — FAILS if the stages diverge. Necessary - * because the bug was byte-id-blind. + * Runtime ownership moved to the matching r796_* fixtures. This carrier + * retains the w6c vs w6c_ww `.s` comparison required by rule 10. * - * GATE POLARITY: must stay GREEN. A wrong exit means the dotted value- - * global mangle regressed; a byte-id FAIL means the stages diverged - * (rule-10 violation). + * GATE POLARITY: a byte-id failure means the stages diverged. */ #include #include @@ -109,56 +104,65 @@ main(void) for (int i = 0; rows[i].src; i++, n++) { char tmpdir[64]; snprintf(tmpdir, sizeof tmpdir, "/tmp/wwxmvd_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); + /* an unowned dir must never be rm -rf'd below */ + if (mkdir(tmpdir, 0755) != 0) { + perror(tmpdir); + fail++; + continue; + } - char src[128], outbin[128], cs_s[128], ws_s[128], rmcmd[160]; + char src[128], cs_s[128], ws_s[128], rmcmd[160]; snprintf(src, sizeof src, "%s/wwxmvd_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/wwxmvd_%d_%d", tmpdir, getpid(), i); snprintf(cs_s, sizeof cs_s, "%s/wwxmvd_%d_%d_cs.s", tmpdir, getpid(), i); snprintf(ws_s, sizeof ws_s, "%s/wwxmvd_%d_%d_ww.s", tmpdir, getpid(), i); snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); FILE *f = fopen(src, "wb"); - if (f == NULL) { fail++; runwait(rmcmd); continue; } + if (f == NULL) { + fail++; + if (runwait(rmcmd) != 0) + fprintf(stderr, "row[%s]: cleanup failed: %s\n", + rows[i].label, rmcmd); + continue; + } fputs(rows[i].src, f); fclose(f); - /* (a) cstage build + run. */ char cmd[2048]; - snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s", bin, outbin, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: cstage build failed\n", - rows[i].label); - fail++; - runwait(rmcmd); - continue; - } - - int got = runwait(outbin); - if (got != rows[i].want_exit) { - fprintf(stderr, "row[%s]: cstage exit %d, want %d\n", - rows[i].label, got, rows[i].want_exit); - fail++; - } - - /* (b) cs==ww byte-id gate. */ + /* Runtime ownership is r79_xmod_valglobal_dot_*; retain byte-id. */ snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, cs_s, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label); - fail++; runwait(rmcmd); continue; + fail++; + if (runwait(rmcmd) != 0) + fprintf(stderr, "row[%s]: cleanup failed: %s\n", + rows[i].label, rmcmd); + continue; } snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c_ww, ws_s, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c_ww failed\n", rows[i].label); - fail++; runwait(rmcmd); continue; + fail++; + if (runwait(rmcmd) != 0) + fprintf(stderr, "row[%s]: cleanup failed: %s\n", + rows[i].label, rmcmd); + continue; } - if (slurp_eq(cs_s, ws_s) != 0) { + int rowdiff = slurp_eq(cs_s, ws_s) != 0; + if (rowdiff) { fprintf(stderr, "row[%s]: cstage/wwstage .s DIFFER " "(rule-10 byte-id violation)\n", rows[i].label); fail++; } - runwait(rmcmd); + /* a leaked tmpdir fails an otherwise-green row without + * masking the byte-id verdict. */ + if (runwait(rmcmd) != 0) { + fprintf(stderr, "row[%s]: cleanup failed: %s\n", + rows[i].label, rmcmd); + if (!rowdiff) + fail++; + } } if (fail) { @@ -166,7 +170,7 @@ main(void) fail, n); return 1; } - printf("xmod_valglobal_dot: %d/%d ok (cstage run + cs==ww byte-id)\n", + printf("xmod_valglobal_dot: %d/%d ok (cs==ww byte-id)\n", n, n); return 0; } diff --git a/test/wcc/797_xmod_struct_field_layout_collide_run.c b/test/wcc/797_xmod_struct_field_layout_collide_run.c index 59523125..40724042 100644 --- a/test/wcc/797_xmod_struct_field_layout_collide_run.c +++ b/test/wcc/797_xmod_struct_field_layout_collide_run.c @@ -1,5 +1,5 @@ /* - * 794_xmod_struct_field_layout_collide_run — project #31 (RULING R2, + * 797_xmod_struct_field_layout_collide_run — project #31 (RULING R2, * commit-1: the OFFSET/SIZE align-up arms) runtime + byte-id net. The * direct continuation of 793 (#21): #21 type-keyed the THREE caller-side * sites the 793 collision drove (push/recv/field-read); #31 closes the @@ -59,7 +59,7 @@ * * Each row reddens under an INDEPENDENT revert of its arm (verified * impl-side: R1 ww=101, C1 cs!=ww OOB, A1/A2 ww=8, W1/W2 ww=107 cs!=ww). - * cstage `ww build --sep` + run pins runtime; the wwstage binary is run too + * cstage `ww build` + run pins runtime; the wwstage binary is run too * (the bug WAS a wrong wwstage runtime value / OOB); raw cs.s vs ww.s over * the driver-produced per-package asm pins rule-10 byte-id. * @@ -72,6 +72,7 @@ */ #include #include +#include #include #include #include @@ -98,7 +99,9 @@ slurp_eq(const char *a, const char *b) if (ca != cb) { rc = -1; break; } if (ca == EOF) break; } - fclose(fa); fclose(fb); + if (ferror(fa) || ferror(fb)) rc = -1; + if (fclose(fa) != 0) rc = -1; + if (fclose(fb) != 0) rc = -1; return rc; } @@ -303,9 +306,9 @@ static const struct scenario scenarios[] = { static int run_scenario(const char *cdrv, const char *wdrv, const struct scenario *sc) { - char dir[] = "/tmp/ww794_XXXXXX"; + char dir[] = "/tmp/ww797_XXXXXX"; if (mkdtemp(dir) == NULL) { - fprintf(stderr, "794[%s]: mkdtemp failed\n", sc->label); + fprintf(stderr, "797[%s]: mkdtemp failed\n", sc->label); return -1; } @@ -315,38 +318,40 @@ run_scenario(const char *cdrv, const char *wdrv, const struct scenario *sc) for (int i = 0; sc->files[i].name; i++) { snprintf(path, sizeof path, "%s/%s", dir, sc->files[i].name); FILE *f = fopen(path, "wb"); - if (!f) { fprintf(stderr, "794[%s]: write %s\n", sc->label, + if (!f) { fprintf(stderr, "797[%s]: write %s\n", sc->label, + sc->files[i].name); rc = -1; goto done; } + int bad = fputs(sc->files[i].src, f) == EOF; + if (fclose(f) != 0) bad = 1; + if (bad) { fprintf(stderr, "797[%s]: write %s\n", sc->label, sc->files[i].name); rc = -1; goto done; } - fputs(sc->files[i].src, f); - fclose(f); } snprintf(cmd, sizeof cmd, - "cd %s && WW_PKGCACHE=%s/pkgc_c %s build --sep -I %s -o %s/main %s/main.ww", - dir, dir, cdrv, dir, dir, dir); + "cd %s && %s build -I %s -o %s/main %s/main.ww", + dir, cdrv, dir, dir, dir); if (runwait(cmd) != 0) { - fprintf(stderr, "794[%s]: cstage build failed\n", sc->label); + fprintf(stderr, "797[%s]: cstage build failed\n", sc->label); rc = -1; goto done; } snprintf(path, sizeof path, "%s/main", dir); int gotc = runwait(path); if (gotc != sc->want_exit) { - fprintf(stderr, "794[%s]: cstage exit %d, want %d\n", + fprintf(stderr, "797[%s]: cstage exit %d, want %d\n", sc->label, gotc, sc->want_exit); rc = -1; } snprintf(cmd, sizeof cmd, - "cd %s && WW_PKGCACHE=%s/pkgc_w %s build --sep -I %s -o %s/mainww %s/main.ww", - dir, dir, wdrv, dir, dir, dir); + "cd %s && %s build -I %s -o %s/mainww %s/main.ww", + dir, wdrv, dir, dir, dir); if (runwait(cmd) != 0) { - fprintf(stderr, "794[%s]: ww_ww build failed\n", sc->label); + fprintf(stderr, "797[%s]: ww_ww build failed\n", sc->label); rc = -1; goto done; } snprintf(path, sizeof path, "%s/mainww", dir); int gotw = runwait(path); if (gotw != sc->want_exit) { - fprintf(stderr, "794[%s]: wwstage exit %d, want %d\n", + fprintf(stderr, "797[%s]: wwstage exit %d, want %d\n", sc->label, gotw, sc->want_exit); rc = -1; } @@ -354,19 +359,56 @@ run_scenario(const char *cdrv, const char *wdrv, const struct scenario *sc) char cs_s[1024], ws_s[1024]; snprintf(cs_s, sizeof cs_s, "%s/all_cs.s", dir); snprintf(ws_s, sizeof ws_s, "%s/all_ww.s", dir); - snprintf(cmd, sizeof cmd, "cat %s/main.sepwork/*.s > %s 2>/dev/null", - dir, cs_s); if (system(cmd)) {} - snprintf(cmd, sizeof cmd, "cat %s/mainww.sepwork/*.s > %s 2>/dev/null", - dir, ws_s); if (system(cmd)) {} + snprintf(cmd, sizeof cmd, + "cat %s/main.sepwork/*.s > %s 2>/dev/null && test -s %s", + dir, cs_s, cs_s); + if (runwait(cmd) != 0) { + fprintf(stderr, "797[%s]: cstage asm aggregation failed\n", + sc->label); + rc = -1; goto done; + } + snprintf(cmd, sizeof cmd, + "cat %s/mainww.sepwork/*.s > %s 2>/dev/null && test -s %s", + dir, ws_s, ws_s); + if (runwait(cmd) != 0) { + fprintf(stderr, "797[%s]: wwstage asm aggregation failed\n", + sc->label); + rc = -1; goto done; + } if (slurp_eq(cs_s, ws_s) != 0) { - fprintf(stderr, "794[%s]: cs.s/ww.s DIFFER (rule-10 byte-id " + fprintf(stderr, "797[%s]: cs.s/ww.s DIFFER (rule-10 byte-id " "violation — #31 regression)\n", sc->label); rc = -1; } done: - snprintf(cmd, sizeof cmd, "rm -rf %s", dir); - (void)runwait(cmd); + snprintf(cmd, sizeof cmd, "rm -rf %s/main.sepwork", dir); + if (runwait(cmd) != 0) { fprintf(stderr, "797[%s]: cleanup main.sepwork\n", + sc->label); rc = -1; } + snprintf(cmd, sizeof cmd, "rm -rf %s/mainww.sepwork", dir); + if (runwait(cmd) != 0) { fprintf(stderr, "797[%s]: cleanup mainww.sepwork\n", + sc->label); rc = -1; } + for (int i = 0; sc->files[i].name; i++) { + snprintf(path, sizeof path, "%s/%s", dir, sc->files[i].name); + if (unlink(path) != 0 && errno != ENOENT) { + fprintf(stderr, "797[%s]: cleanup %s\n", sc->label, + sc->files[i].name); + rc = -1; + } + } + const char *children[] = { "main", "mainww", "all_cs.s", "all_ww.s", NULL }; + for (int i = 0; children[i]; i++) { + snprintf(path, sizeof path, "%s/%s", dir, children[i]); + if (unlink(path) != 0 && errno != ENOENT) { + fprintf(stderr, "797[%s]: cleanup %s\n", sc->label, + children[i]); + rc = -1; + } + } + if (rmdir(dir) != 0) { + fprintf(stderr, "797[%s]: cleanup directory\n", sc->label); + rc = -1; + } return rc; } @@ -387,7 +429,7 @@ main(void) snprintf(cdrv, sizeof cdrv, "%s/ww", bin); snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); if (access(wdrv, X_OK) != 0) { - fprintf(stderr, "794: ww_ww missing — cannot run the cs==ww " + fprintf(stderr, "797: ww_ww missing — cannot run the cs==ww " "byte-id gate (the whole point of this test)\n"); return 1; } @@ -400,7 +442,7 @@ main(void) } if (fail) { - fprintf(stderr, "794 xmod_struct_field_layout_collide: %d/%d " + fprintf(stderr, "797 xmod_struct_field_layout_collide: %d/%d " "scenarios failed\n", fail, n); return 1; } diff --git a/test/wcc/798_tuple_sret_callee.c b/test/wcc/798_tuple_sret_callee.c index 4cf3bfdc..0373b797 100644 --- a/test/wcc/798_tuple_sret_callee.c +++ b/test/wcc/798_tuple_sret_callee.c @@ -37,14 +37,17 @@ */ #include #include +#include #include +#include static int runwait(const char *cmd) { int rc = system(cmd); if (rc == -1) return -1; - return rc; + if (WIFEXITED(rc)) return WEXITSTATUS(rc); + return -1; } struct row { const char *label; const char *src; }; @@ -120,28 +123,39 @@ main(void) char cs_s[64], ws_s[64], cmd[2048]; snprintf(cs_s, sizeof cs_s, "/tmp/wwtsc_%d_%d_cs.s", getpid(), i); snprintf(ws_s, sizeof ws_s, "/tmp/wwtsc_%d_%d_ww.s", getpid(), i); + int rowfail = 0; /* (a) cstage must COMPILE the over-cap tuple callee (no loud-stop). */ snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, cs_s, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c loud-stopped (Fold A regressed)\n", rows[i].label); - fail++; unlink(src); continue; + rowfail = 1; + goto rowcleanup; } /* (a') wwstage must compile it too. */ snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c_ww, ws_s, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c_ww loud-stopped (Fold A regressed)\n", rows[i].label); - fail++; unlink(src); unlink(cs_s); continue; + rowfail = 1; + goto rowcleanup; } /* (b) cs==ww byte-id gate. */ if (slurp_eq(cs_s, ws_s) != 0) { fprintf(stderr, "row[%s]: cstage/wwstage .s DIFFER " "(rule-10 byte-id violation)\n", rows[i].label); - fail++; + rowfail = 1; } - unlink(src); unlink(cs_s); unlink(ws_s); +rowcleanup: + /* a failed leg may still have written a partial .s; ENOENT is + * the leg-never-wrote case, any other cleanup failure must not + * silently leak. */ + if (unlink(src) != 0 && errno != ENOENT) { perror(src); rowfail = 1; } + if (unlink(cs_s) != 0 && errno != ENOENT) { perror(cs_s); rowfail = 1; } + if (unlink(ws_s) != 0 && errno != ENOENT) { perror(ws_s); rowfail = 1; } + if (rowfail) + fail++; } if (fail) { diff --git a/test/wcc/805_placeaddr_store.c b/test/wcc/805_placeaddr_store.c index 60cfe854..0102aa5d 100644 --- a/test/wcc/805_placeaddr_store.c +++ b/test/wcc/805_placeaddr_store.c @@ -134,6 +134,7 @@ #include #include #include +#include #include #include #include @@ -1326,24 +1327,31 @@ errlog_has(const char *path, const char *needle) static int run_driver(const char *driver, const struct row *r, int i) { - char tmpdir[64], src[128], outbin[128], errlog[128], rmcmd[160]; + char tmpdir[] = "/tmp/plad_XXXXXX"; + char src[128], outbin[128], errlog[128], scratch[160], rmcmd[192]; char cmd[1200]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/plad_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); + int result = -2; + if (mkdtemp(tmpdir) == NULL) { + fprintf(stderr, "row[%s]: workspace acquisition failed\n", r->label); + return result; + } snprintf(src, sizeof src, "%s/plad_%d_%d.ww", tmpdir, getpid(), i); snprintf(outbin, sizeof outbin, "%s/plad_%d_%d", tmpdir, getpid(), i); snprintf(errlog, sizeof errlog, "%s/err", tmpdir); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + snprintf(scratch, sizeof scratch, "%s.sepwork", outbin); + snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", scratch); FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return -1; } + if (!f) goto done; fputs(r->src, f); - fclose(f); + int werr = ferror(f); + if (fclose(f) != 0) werr = 1; + if (werr) goto done; snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>%s", driver, outbin, src, errlog); if (runwait(cmd) != 0) { - int rc = -1; + result = -1; if (r->want != BUILD_FAIL) { fprintf(stderr, "row[%s]: build via %s failed\n", r->label, driver); @@ -1352,16 +1360,29 @@ run_driver(const char *driver, const struct row *r, int i) fprintf(stderr, "row[%s]: %s build failed without " "expected diagnostic \"%s\"\n", r->label, driver, r->expect_err); - rc = -3; /* failed, but for the wrong reason */ + result = -3; /* failed, but for the wrong reason */ } - runwait(rmcmd); - return rc; + goto done; } - int got = runwait(outbin); + result = runwait(outbin); - runwait(rmcmd); - return got; +done: + { + int cleanfail = 0; + if (runwait(rmcmd) != 0) cleanfail = 1; + if (unlink(errlog) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(tmpdir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "row[%s]: workspace cleanup failed\n", r->label); + int passed = r->want == BUILD_FAIL + ? result == -1 : result == r->want; + if (passed) result = -2; + } + } + return result; } /* asm_byte_identical — generate .s via cstage's w6c and wwstage's @@ -1371,33 +1392,37 @@ run_driver(const char *driver, const struct row *r, int i) static int asm_byte_identical(const char *bin, const struct row *r, int i) { - char src[64], cs[64], ws[64], cmd[1024]; - snprintf(src, sizeof src, "/tmp/plad_asm_%d_%d.ww", getpid(), i); - snprintf(cs, sizeof cs, "/tmp/plad_asm_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/plad_asm_%d_%d_w.s", getpid(), i); + (void)i; + char dir[] = "/tmp/plad_asm_XXXXXX"; + char src[128], cs[128], ws[128], cmd[1024]; + int rc = -1; + if (mkdtemp(dir) == NULL) return -1; + snprintf(src, sizeof src, "%s/input.ww", dir); + snprintf(cs, sizeof cs, "%s/c.s", dir); + snprintf(ws, sizeof ws, "%s/w.s", dir); FILE *f = fopen(src, "wb"); - if (!f) return -1; + if (!f) goto done; fputs(r->src, f); - fclose(f); + int werr = ferror(f); + if (fclose(f) != 0) werr = 1; + if (werr) goto done; snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c errored\n", r->label); - unlink(src); - return -1; + goto done; } snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null", bin, ws, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label); - unlink(src); unlink(cs); - return -1; + goto done; } FILE *fc = fopen(cs, "rb"); FILE *fw = fopen(ws, "rb"); - int rc = 0; + rc = 0; if (!fc || !fw) { rc = -1; } else { @@ -1413,7 +1438,20 @@ asm_byte_identical(const char *bin, const struct row *r, int i) if (rc != 0) fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n", r->label); - unlink(src); unlink(cs); unlink(ws); + +done: + { + int cleanfail = 0; + if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(cs) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(ws) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(dir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "row[%s]: asm workspace cleanup failed\n", + r->label); + rc = -1; + } + } return rc; } diff --git a/test/wcc/806_append_place.c b/test/wcc/806_append_place.c index 6272a284..d129a34d 100644 --- a/test/wcc/806_append_place.c +++ b/test/wcc/806_append_place.c @@ -90,6 +90,7 @@ #include #include #include +#include #include #include #include @@ -806,24 +807,31 @@ errlog_has(const char *path, const char *needle) static int run_driver(const char *driver, const struct row *r, int i) { - char tmpdir[64], src[128], outbin[128], errlog[160]; - char rmcmd[160], cmd[1200]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/applp_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); + char tmpdir[] = "/tmp/applp_XXXXXX"; + char src[128], outbin[128], errlog[160], scratch[192]; + char rmcmd[224], cmd[1200]; + int result = -2; + if (mkdtemp(tmpdir) == NULL) { + fprintf(stderr, "row[%s]: workspace acquisition failed\n", r->label); + return result; + } snprintf(src, sizeof src, "%s/applp_%d_%d.ww", tmpdir, getpid(), i); snprintf(outbin, sizeof outbin, "%s/applp_%d_%d", tmpdir, getpid(), i); snprintf(errlog, sizeof errlog, "%s/err", tmpdir); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + snprintf(scratch, sizeof scratch, "%s.sepwork", outbin); + snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", scratch); FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return -1; } + if (!f) goto done; fputs(r->src, f); - fclose(f); + int werr = ferror(f); + if (fclose(f) != 0) werr = 1; + if (werr) goto done; snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>%s", driver, outbin, src, errlog); if (runwait(cmd) != 0) { - int rc = -1; + result = -1; if (r->want != BUILD_FAIL) { fprintf(stderr, "row[%s]: build via %s failed\n", r->label, driver); @@ -832,16 +840,29 @@ run_driver(const char *driver, const struct row *r, int i) fprintf(stderr, "row[%s]: %s build failed without " "expected diagnostic \"%s\"\n", r->label, driver, r->expect_err); - rc = -3; /* failed, but for the wrong reason */ + result = -3; /* failed, but for the wrong reason */ } - runwait(rmcmd); - return rc; + goto done; } - int got = runwait(outbin); + result = runwait(outbin); - runwait(rmcmd); - return got; +done: + { + int cleanfail = 0; + if (runwait(rmcmd) != 0) cleanfail = 1; + if (unlink(errlog) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(tmpdir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "row[%s]: workspace cleanup failed\n", r->label); + int passed = r->want == BUILD_FAIL + ? result == -1 : result == r->want; + if (passed) result = -2; + } + } + return result; } /* asm_byte_identical — generate .s via cstage's w6c and wwstage's @@ -851,33 +872,37 @@ run_driver(const char *driver, const struct row *r, int i) static int asm_byte_identical(const char *bin, const struct row *r, int i) { - char src[64], cs[64], ws[64], cmd[1024]; - snprintf(src, sizeof src, "/tmp/applp_asm_%d_%d.ww", getpid(), i); - snprintf(cs, sizeof cs, "/tmp/applp_asm_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/applp_asm_%d_%d_w.s", getpid(), i); + (void)i; + char dir[] = "/tmp/applp_asm_XXXXXX"; + char src[128], cs[128], ws[128], cmd[1024]; + int rc = -1; + if (mkdtemp(dir) == NULL) return -1; + snprintf(src, sizeof src, "%s/input.ww", dir); + snprintf(cs, sizeof cs, "%s/c.s", dir); + snprintf(ws, sizeof ws, "%s/w.s", dir); FILE *f = fopen(src, "wb"); - if (!f) return -1; + if (!f) goto done; fputs(r->src, f); - fclose(f); + int werr = ferror(f); + if (fclose(f) != 0) werr = 1; + if (werr) goto done; snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c errored\n", r->label); - unlink(src); - return -1; + goto done; } snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null", bin, ws, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label); - unlink(src); unlink(cs); - return -1; + goto done; } FILE *fc = fopen(cs, "rb"); FILE *fw = fopen(ws, "rb"); - int rc = 0; + rc = 0; if (!fc || !fw) { rc = -1; } else { @@ -893,7 +918,20 @@ asm_byte_identical(const char *bin, const struct row *r, int i) if (rc != 0) fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n", r->label); - unlink(src); unlink(cs); unlink(ws); + +done: + { + int cleanfail = 0; + if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(cs) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(ws) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(dir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "row[%s]: asm workspace cleanup failed\n", + r->label); + rc = -1; + } + } return rc; } diff --git a/test/wcc/810_dyn.c b/test/wcc/810_dyn.c index 904cd653..71a86446 100644 --- a/test/wcc/810_dyn.c +++ b/test/wcc/810_dyn.c @@ -17,6 +17,7 @@ */ #include #include +#include #include #include #include @@ -129,16 +130,33 @@ main(void) int n = 0, fail = 0; for (int i = 0; rows[i].src; i++, n++) { - char tmpdir[80], src[128], outbin[128], rmcmd[160]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/wwdyn_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); + char tmpdir[] = "/tmp/wwdyn_XXXXXX"; + char src[128], outbin[128], scratch[160], rmcmd[192]; + int rowfail = 0; + if (mkdtemp(tmpdir) == NULL) { + fprintf(stderr, "dyn row %d: mkdtemp failed\n", i); + fail++; + continue; + } snprintf(src, sizeof src, "%s/wwdyn_%d_%d.ww", tmpdir, getpid(), i); snprintf(outbin, sizeof outbin, "%s/wwdyn_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + snprintf(scratch, sizeof scratch, "%s.sepwork", outbin); + snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", scratch); FILE *f = fopen(src, "wb"); + if (f == NULL) { + fprintf(stderr, "dyn row %d: source create failed\n", i); + rowfail = 1; + goto row_done; + } wwtest_fputs(rows[i].src, f); - fclose(f); + int werr = ferror(f); + if (fclose(f) != 0) werr = 1; + if (werr) { + fprintf(stderr, "dyn row %d: source write failed\n", i); + rowfail = 1; + goto row_done; + } char cmd[1024]; snprintf(cmd, sizeof cmd, @@ -146,18 +164,35 @@ main(void) bin, outbin, src, libdir); if (runwait(cmd) != 0) { fprintf(stderr, "dyn row %d: build failed\n", i); - fail++; - runwait(rmcmd); - continue; + rowfail = 1; + goto row_done; } int got = runwait(outbin); if (got != rows[i].want_exit) { fprintf(stderr, "dyn row %d: exit %d, want %d\n", i, got, rows[i].want_exit); - fail++; + rowfail = 1; } - runwait(rmcmd); + + row_done: + if (runwait(rmcmd) != 0) { + fprintf(stderr, "dyn row %d: scratch cleanup failed\n", i); + rowfail = 1; + } + if (unlink(outbin) != 0 && errno != ENOENT) { + fprintf(stderr, "dyn row %d: output cleanup failed\n", i); + rowfail = 1; + } + if (unlink(src) != 0 && errno != ENOENT) { + fprintf(stderr, "dyn row %d: source cleanup failed\n", i); + rowfail = 1; + } + if (rmdir(tmpdir) != 0) { + fprintf(stderr, "dyn row %d: workspace cleanup failed\n", i); + rowfail = 1; + } + if (rowfail) fail++; } if (fail) { fprintf(stderr, "%d/%d dyn tests failed\n", fail, n); diff --git a/test/wcc/815_fmt_int_run.c b/test/wcc/815_fmt_int_run.c index d1c97c84..34380056 100644 --- a/test/wcc/815_fmt_int_run.c +++ b/test/wcc/815_fmt_int_run.c @@ -1,402 +1,99 @@ /* - * 815_fmt_int_run — #6 fmt.formattable widen (bare int / uint). Pins - * that lib/fmt/fmt.ww now renders bare `int` and `uint` as REAL union - * members (formattable = (i64|str|bool|rune|f64|int|uint), int=5, - * uint=6 appended last). Before #6 cstage LOUD-rejected a bare int arg - * to the fmt family (no `int` arm to widen into) while wwstage leniently - * accepted via the #128 size/name-keyed int->i64 coercion — cs!=ww. - * Making int/uint real members makes BOTH accept by membership; this - * test is the cstage-now-accepts + correct-render + cs==ww byte-id gate. - * - * row | what it pins - * ---------------+-------------------------------------------------- - * bare_int | fprint(int 42) over the writeone path -> "42" - * bare_int_neg | fprint(int -7) -> "-7" (signed i64dec path) - * bare_uint | fprint(uint 42) -> "42" - * uint_highbit | fprint(uint 2^63+1) -> "9223372036854775809" - * | LOAD-BEARING: proves the strconv.u64tos UNSIGNED - * | route (len 19, first byte '9' not '-') — i64dec - * | would render this negative ("-9223372036854775807", - * | len 20). Also proves wwstage tags it uint (6), not - * | i64, under the widened union. - * int_printf | bsprintf("{}", int 42) -> "42" (formatfield -> - * | formatone -> formatraw placeholder path) - * unaffected_i64 | fprint(42i64) -> "42" (existing arm regression guard) - * unaffected_str | fprint("hi") -> "hi" (existing arm regression guard) - * mixed | fprint(1:int, 2i64, true) -> "1 2 true": int(tag5) - * | + i64(tag0) + bool(tag2) coexist in one call, - * | distinct tags, ww auto-space separator. - * - * Each row builds + runs through BOTH `ww` (cstage) and `ww_ww` - * (wwstage) via the driver (-I lib for the fmt import), then a byte-id - * pass builds the combined.ww via the cstage driver and diffs the asm - * w6c emits against w6c_ww — any cs!=ww tag/render divergence reds here. - * - * BOOTSTRAP-EMBED: fmt is NOT embedded in any selfhost combined.ww — - * w6c/wwdump main.ww never reach wcc/err.ww (the lone `import fmt`), so - * neither selfhost/cmd/{w6c,wwdump}/main.combined.ww carries an fmt fn - * (grep-verified: 0 putbytes/writeone/formatraw; the 2 "formattable" - * hits are cgen comment text). #6 therefore rides NO combined.ww regen. - * This test covers the user-program path, which is the only fmt consumer. - * - * GATE POLARITY: must stay GREEN. A red means the int/uint arm dropped, - * uint mis-routed through the signed renderer (highbit prints negative), - * the wwstage mis-tagged uint as i64, or an existing i64/str arm - * regressed. + * 815: residual separated-package cstage/wwstage assembly identity. + * Runtime is owned by r815_*; -S stops both driver legs before assembly, + * archive creation, and linking. The sorted package-assembly concatenation + * remains the byte oracle. */ #include #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; -}; +struct row { const char *label, *path; }; static const struct row rows[] = { - { "bare_int", - "package main;\n" - "import os;\n" - "import fmt;\n" - "import io;\n" - "import memio;\n" - "export fn main() i32 = {\n" - " let buf: [32]u8;\n" - " let st: memio.stream = memio.fixed(buf[0:32]);\n" - " let vs: io.stream = &st.vt;\n" - " let x: int = 42;\n" - " match (fmt.fprint(vs, x)) { case size => {}; case io.error => { return 91; }; };\n" - " let v: str = memio.string(&st);\n" - " if (v.len != 2) { return 92; };\n" - " if (v[0] != 52u8 || v[1] != 50u8) { return 93; };\n" - " return 50;\n" - "};\n", - 50 }, - { "bare_int_neg", - "package main;\n" - "import os;\n" - "import fmt;\n" - "import io;\n" - "import memio;\n" - "export fn main() i32 = {\n" - " let buf: [32]u8;\n" - " let st: memio.stream = memio.fixed(buf[0:32]);\n" - " let vs: io.stream = &st.vt;\n" - " let x: int = -7;\n" - " match (fmt.fprint(vs, x)) { case size => {}; case io.error => { return 91; }; };\n" - " let v: str = memio.string(&st);\n" - " if (v.len != 2) { return 92; };\n" - " if (v[0] != 45u8 || v[1] != 55u8) { return 93; };\n" - " return 51;\n" - "};\n", - 51 }, - { "bare_uint", - "package main;\n" - "import os;\n" - "import fmt;\n" - "import io;\n" - "import memio;\n" - "export fn main() i32 = {\n" - " let buf: [32]u8;\n" - " let st: memio.stream = memio.fixed(buf[0:32]);\n" - " let vs: io.stream = &st.vt;\n" - " let x: uint = 42u64: uint;\n" - " match (fmt.fprint(vs, x)) { case size => {}; case io.error => { return 91; }; };\n" - " let v: str = memio.string(&st);\n" - " if (v.len != 2) { return 92; };\n" - " if (v[0] != 52u8 || v[1] != 50u8) { return 93; };\n" - " return 52;\n" - "};\n", - 52 }, - { "uint_highbit", - "package main;\n" - "import os;\n" - "import fmt;\n" - "import io;\n" - "import memio;\n" - "export fn main() i32 = {\n" - " let buf: [32]u8;\n" - " let st: memio.stream = memio.fixed(buf[0:32]);\n" - " let vs: io.stream = &st.vt;\n" - " let x: uint = 9223372036854775809u64: uint;\n" - " match (fmt.fprint(vs, x)) { case size => {}; case io.error => { return 91; }; };\n" - " let v: str = memio.string(&st);\n" - " if (v.len != 19) { return 92; };\n" - " if (v[0] != 57u8) { return 93; };\n" /* '9', not '-' (45) */ - " if (v[18] != 57u8) { return 94; };\n" /* trailing '9' */ - " return 53;\n" - "};\n", - 53 }, - { "int_printf", - "package main;\n" - "import os;\n" - "import fmt;\n" - "import io;\n" - "export fn main() i32 = {\n" - " let buf: [16]u8;\n" - " let x: int = 42;\n" - " let v: str = \"\";\n" - " match (fmt.bsprintf(buf[0:16], \"{}\", x)) { case let s: str => { v = s; }; case io.error => { return 91; }; };\n" - " if (v.len != 2) { return 92; };\n" - " if (v[0] != 52u8 || v[1] != 50u8) { return 93; };\n" - " return 54;\n" - "};\n", - 54 }, - { "unaffected_i64", - "package main;\n" - "import os;\n" - "import fmt;\n" - "import io;\n" - "import memio;\n" - "export fn main() i32 = {\n" - " let buf: [32]u8;\n" - " let st: memio.stream = memio.fixed(buf[0:32]);\n" - " let vs: io.stream = &st.vt;\n" - " match (fmt.fprint(vs, 42i64)) { case size => {}; case io.error => { return 91; }; };\n" - " let v: str = memio.string(&st);\n" - " if (v.len != 2) { return 92; };\n" - " if (v[0] != 52u8 || v[1] != 50u8) { return 93; };\n" - " return 55;\n" - "};\n", - 55 }, - { "unaffected_str", - "package main;\n" - "import os;\n" - "import fmt;\n" - "import io;\n" - "import memio;\n" - "export fn main() i32 = {\n" - " let buf: [32]u8;\n" - " let st: memio.stream = memio.fixed(buf[0:32]);\n" - " let vs: io.stream = &st.vt;\n" - " match (fmt.fprint(vs, \"hi\")) { case size => {}; case io.error => { return 91; }; };\n" - " let v: str = memio.string(&st);\n" - " if (v.len != 2) { return 92; };\n" - " if (v[0] != 104u8 || v[1] != 105u8) { return 93; };\n" - " return 56;\n" - "};\n", - 56 }, - { "mixed", - "package main;\n" - "import os;\n" - "import fmt;\n" - "import io;\n" - "import memio;\n" - "export fn main() i32 = {\n" - " let buf: [32]u8;\n" - " let st: memio.stream = memio.fixed(buf[0:32]);\n" - " let vs: io.stream = &st.vt;\n" - " let x: int = 1;\n" - " match (fmt.fprint(vs, x, 2i64, true)) { case size => {}; case io.error => { return 91; }; };\n" - " let v: str = memio.string(&st);\n" - " if (v.len != 8) { return 92; };\n" /* "1 2 true" */ - " if (v[0] != 49u8 || v[1] != 32u8) { return 93; };\n" /* '1' ' ' */ - " if (v[2] != 50u8 || v[3] != 32u8) { return 94; };\n" /* '2' ' ' */ - " if (v[4] != 116u8 || v[7] != 101u8) { return 95; };\n" /* 't' .. 'e' */ - " return 57;\n" - "};\n", - 57 }, + { "bare_int", "test/wcc/data/r815_bare_int/case.ww" }, + { "bare_int_neg", "test/wcc/data/r815_bare_int_neg/case.ww" }, + { "bare_uint", "test/wcc/data/r815_bare_uint/case.ww" }, + { "uint_highbit", "test/wcc/data/r815_uint_highbit/case.ww" }, + { "int_printf", "test/wcc/data/r815_int_printf/case.ww" }, + { "unaffected_i64", "test/wcc/data/r815_unaffected_i64/case.ww" }, + { "unaffected_str", "test/wcc/data/r815_unaffected_str/case.ww" }, + { "mixed", "test/wcc/data/r815_mixed/case.ww" }, }; -static int -write_source(const char *path, const char *src) -{ - FILE *f = fopen(path, "wb"); - if (!f) return -1; - fputs(src, f); - fclose(f); - return 0; +static int runwait(const char *cmd) { + int rc = system(cmd); + return rc != -1 && WIFEXITED(rc) ? WEXITSTATUS(rc) : -1; } -/* Per-row tmpdir cleanup. ww_ww writes intermediates next to the source - * (task #15), so each row's build leaves .{combined.ww,s,o} + bare - * exe alongside. Mirror of 781's. */ -static void -cleanup_tmp(const char *tmpdir, const char *base) -{ - char p[640]; - snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "%s/%s.s", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "%s/%s.o", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base); unlink(p); - snprintf(p, sizeof p, "%s/%s", tmpdir, base); unlink(p); - rmdir(tmpdir); +static int copyfile(const char *src, const char *dst) { + char buf[8192]; size_t n; int rc = 0; + FILE *in = fopen(src, "rb"), *out; + if (!in) return -1; + out = fopen(dst, "wb"); + if (!out) { fclose(in); return -1; } + while ((n = fread(buf, 1, sizeof buf, in)) != 0) + if (fwrite(buf, 1, n, out) != n) { rc = -1; break; } + if (ferror(in)) rc = -1; + if (fclose(out) != 0) rc = -1; + fclose(in); return rc; } -static int -build_via_driver(const char *driver, const char *tmpdir, const char *cwd, - const char *src) -{ - char cmd[2048]; +static int samefile(const char *a, const char *b) { + FILE *fa = fopen(a, "rb"), *fb = fopen(b, "rb"); int rc = 0; + if (!fa || !fb) rc = -1; + else for (;;) { int ca = fgetc(fa), cb = fgetc(fb); if (ca != cb) { rc = -1; break; } if (ca == EOF) break; } + if (fa) fclose(fa); if (fb) fclose(fb); return rc; +} + +static int byteid(const char *cdrv, const char *wdrv, const char *cwd, + const struct row *r, int seq) { + char tmp[256], src[512], cs[512], ws[512], cmd[2048]; int rc = -1; + snprintf(tmp, sizeof tmp, "/tmp/fic_%d_b_%d", getpid(), seq); + snprintf(src, sizeof src, "%s/main815b.ww", tmp); + snprintf(cs, sizeof cs, "%s/all_cs.s", tmp); + snprintf(ws, sizeof ws, "%s/all_ww.s", tmp); + /* an unowned path (stale dir, full /tmp) must not be built in — or + * rm -rf'd — below. */ + if (mkdir(tmp, 0755) != 0) { perror(tmp); return -1; } + if (copyfile(r->path, src) != 0) goto out; snprintf(cmd, sizeof cmd, - "cd %s && timeout 180 %s build -I %s/lib %s 2>/dev/null", - tmpdir, driver, cwd, src); - return runwait(cmd); -} - -static int -run_row(const char *driver, const char *cwd, const struct row *r, int seq) -{ - char tmpdir[256], src[512], base[64], outbin[768]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/fic_%d_d_%d", getpid(), seq); - snprintf(base, sizeof base, "main815"); - snprintf(src, sizeof src, "%s/%s.ww", tmpdir, base); - mkdir(tmpdir, 0755); - if (write_source(src, r->src) != 0) { cleanup_tmp(tmpdir, base); return -1; } - int rc; - int br = build_via_driver(driver, tmpdir, cwd, src); - if (br == 0) { - snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); - rc = runwait(outbin); - } else { - rc = -1; - } - cleanup_tmp(tmpdir, base); - return rc; -} - -/* asm_byte_identical — #94 sep layout: the cstage driver's --sep build - * (which needs -I lib for fmt import resolution that raw w6c can't do) - * emits per-package w6c asm to _c.sepwork/, and the wwstage driver's - * --sep build emits per-package w6c_ww asm to _w.sepwork/. Concat - * (sorted glob, identical package set) and diff — proves no cs!=ww - * tag/render divergence on the widened union. */ -static int -asm_byte_identical(const char *cdrv, const char *wdrv, const char *cwd, - const struct row *r, int seq) -{ - char tmpdir[256], base[64], src[512], cs[640], ws[640]; - char cmd[2048]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/fic_%d_b_%d", getpid(), seq); - snprintf(base, sizeof base, "main815b"); - snprintf(src, sizeof src, "%s/%s.ww", tmpdir, base); - snprintf(cs, sizeof cs, "%s/all_cs.s", tmpdir); - snprintf(ws, sizeof ws, "%s/all_ww.s", tmpdir); - mkdir(tmpdir, 0755); - if (write_source(src, r->src) != 0) { - snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir); - if (system(cmd)) {} - return -1; - } - - int rc = 0; + "cd %s && timeout 180 %s build -S -I %s/lib " + "-o %s/main815b_c %s 2>/dev/null", tmp, cdrv, cwd, tmp, src); + if (runwait(cmd) != 0) goto out; snprintf(cmd, sizeof cmd, - "cd %s && WW_PKGCACHE=%s/pkgc_c timeout 180 %s build --sep -I %s/lib " - "-o %s/%s_c %s 2>/dev/null", tmpdir, tmpdir, cdrv, cwd, tmpdir, base, src); + "cd %s && timeout 180 %s build -S -I %s/lib " + "-o %s/main815b_w %s 2>/dev/null", tmp, wdrv, cwd, tmp, src); + if (runwait(cmd) != 0) goto out; + snprintf(cmd, sizeof cmd, + "cat %s/main815b_c.sepwork/*.s > %s 2>/dev/null", tmp, cs); + if (runwait(cmd) != 0) goto out; + snprintf(cmd, sizeof cmd, + "cat %s/main815b_w.sepwork/*.s > %s 2>/dev/null", tmp, ws); + if (runwait(cmd) != 0) goto out; + rc = samefile(cs, ws); +out: + if (rc != 0) fprintf(stderr, "fmt_int_run[%s]: byte mismatch/error\n", r->label); + snprintf(cmd, sizeof cmd, "rm -rf %s", tmp); if (runwait(cmd) != 0) { - fprintf(stderr, "fmt_int_run[byteid][%s]: cstage sep build failed\n", - r->label); - rc = -1; + fprintf(stderr, "fmt_int_run[%s]: cleanup %s failed\n", r->label, tmp); + if (rc == 0) rc = -1; } - if (rc == 0) { - snprintf(cmd, sizeof cmd, - "cd %s && WW_PKGCACHE=%s/pkgc_w timeout 180 %s build --sep " - "-I %s/lib -o %s/%s_w %s 2>/dev/null", - tmpdir, tmpdir, wdrv, cwd, tmpdir, base, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "fmt_int_run[byteid][%s]: wwstage sep build " - "failed\n", r->label); - rc = -1; - } - } - if (rc == 0) { - snprintf(cmd, sizeof cmd, "cat %s/%s_c.sepwork/*.s > %s 2>/dev/null", - tmpdir, base, cs); if (system(cmd)) {} - snprintf(cmd, sizeof cmd, "cat %s/%s_w.sepwork/*.s > %s 2>/dev/null", - tmpdir, base, ws); if (system(cmd)) {} - FILE *fc = fopen(cs, "rb"); - FILE *fw = fopen(ws, "rb"); - if (!fc || !fw) { - rc = -1; - } else { - for (;;) { - int a = fgetc(fc); - int b = fgetc(fw); - if (a != b) { rc = -1; break; } - if (a == EOF) break; - } - } - if (fc) fclose(fc); - if (fw) fclose(fw); - if (rc != 0) - fprintf(stderr, - "fmt_int_run[byteid][%s]: cstage vs wwstage asm differs\n", - r->label); - } - - snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir); - if (system(cmd)) {} return rc; } -int -main(void) -{ - const char *bin = getenv("BIN"); +int main(void) { + const char *bin = getenv("BIN"); char cwd[1024], absbin[1024]; + char cdrv[1024], wdrv[1024]; + if (!getcwd(cwd, sizeof cwd)) return 1; if (!bin) bin = "out/bin"; - char cwd[256]; - if (getcwd(cwd, sizeof cwd) == NULL) return 1; - char absbin[512]; - if (bin[0] != '/') { - snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); - bin = absbin; - } - - char cdrv[640], wdrv[640]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); + if (bin[0] != '/') { snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); bin = absbin; } + snprintf(cdrv, sizeof cdrv, "%s/ww", bin); snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - int wwpresent = (access(wdrv, X_OK) == 0); - int seq = 0; - - for (int i = 0; i < n; i++) { - total++; - int got = run_row(cdrv, cwd, &rows[i], seq++); - if (got != rows[i].want_exit) { - fprintf(stderr, "fmt_int_run[cs][%s]: exit=%d want=%d\n", - rows[i].label, got, rows[i].want_exit); - fail++; - } - if (wwpresent) { - total++; - int gw = run_row(wdrv, cwd, &rows[i], seq++); - if (gw != rows[i].want_exit) { - fprintf(stderr, "fmt_int_run[ww][%s]: exit=%d want=%d\n", - rows[i].label, gw, rows[i].want_exit); - fail++; - } - } - } - - if (wwpresent) { - for (int i = 0; i < n; i++) { - total++; - if (asm_byte_identical(cdrv, wdrv, cwd, &rows[i], seq++) != 0) - fail++; - } - } else { - fprintf(stderr, "fmt_int_run: skip wwstage (no %s)\n", wdrv); - } - - if (fail) { - fprintf(stderr, "fmt_int_run: %d/%d fixtures failed\n", fail, total); - return 1; - } - printf("fmt_int_run: %d/%d ok\n", total, total); - return 0; + if (access(wdrv, X_OK) != 0) { fprintf(stderr, "fmt_int_run: skip wwstage\n"); return 0; } + int n = (int)(sizeof rows / sizeof rows[0]), fail = 0; + for (int i = 0; i < n; i++) if (byteid(cdrv, wdrv, cwd, &rows[i], i) != 0) fail++; + if (fail) { fprintf(stderr, "fmt_int_run: %d/%d byte rows failed\n", fail, n); return 1; } + printf("fmt_int_run: %d/%d byte rows ok\n", n, n); return 0; } diff --git a/test/wcc/826_alias_tuple_coerce.c b/test/wcc/826_alias_tuple_coerce.c index fe0e9167..31106452 100644 --- a/test/wcc/826_alias_tuple_coerce.c +++ b/test/wcc/826_alias_tuple_coerce.c @@ -1,57 +1,13 @@ /* - * 826_alias_tuple_coerce — an alias of a tuple type, initialised with an - * UNTYPED tuple literal, must coerce element-wise just like a direct tuple - * (task #99). `type pair = (int, int); let x: pair = (3, 4)` was rejected by - * cstage — `init (untyped_int, untyped_int) not assignable to declared pair` - * — while the direct `let x: (int,int) = (3,4)` and the typed-alias - * `let x: pair = (3:int, 4:int)` both compiled. wwstage already accepted+ran - * the untyped-alias form correctly. - * - * ROOT (cstage only): cmd/wcc/type.c type_assignable's tuple-to-tuple arm - * gated on the UN-chased `dst->kind == TY_TUPLE`. For `pair` (a TY_NAMED - * alias) dst->kind is TY_NAMED, so the arm was skipped and the per-element - * untyped->int coercion never ran. The fix chases TY_NAMED on both sides - * before the tuple check (mirroring the #258 slice-borrow arm just below), - * so an alias-of-tuple gets the same element-wise coercion as a direct tuple. - * type_assignable is the shared assignability predicate, so INIT, FN-ARG and - * RETURN positions all flip reject->accept in one spot. cstage-only — - * wwstage check.ww/cgen are unchanged. - * - * The coercion still type-checks per element, so a genuinely mismatched - * element type or wrong arity STILL louds — those are the negative controls. - * - * row | shape | want - * ------------+------------------------------------------------+------ - * init | type pair=(int,int); let x:pair=(3,4); x.0+x.1 | 7 - * fnarg | fn g(p:pair); g((3,4)); p.0+p.1 | 7 - * ret | fn f() pair = { return (3,4); }; r.0+r.1 | 7 - * mixed | type box=(int,u16); let x:box=(3,4); coerce ea | 7 - * ctrl_dir | direct (int,int)=(3,4) (no-regress) | 7 - * ctrl_typed | alias (3:int,4:int) (no-regress) | 7 - * - * init / fnarg / ret / mixed are the mutation-sane rows: pre-fix cstage - * build-FAILS them (the untyped->alias coercion was rejected); pre-fix - * wwstage built but a two-field alias-tuple fn-arg read garbage (the param - * spill half). Post-fix BOTH stages build+run the want. - * - * Negative controls (cstage must STILL loud — don't over-accept): - * neg_type | let x:pair=(3,"s") (element type mismatch) - * neg_arity | let x:pair=(3,4,5) (wrong arity) - * - * byte-id: the init, fnarg and ret rows are asserted byte-identical - * (cstage==wwstage .s). #99 shipped in two halves — the cstage type.c chase - * (init/fnarg/ret reject->accept) AND the wwstage cgendecl.ww tuple-PARAM - * spill chase (the param twin: an alias param gated on the SYNTACTIC - * N_TTUPLE node fell through to the scalar path, spilling one arg reg and - * dropping the second tuple word, so a two-field read of an alias-tuple - * fn-arg returned garbage in wwstage). With the wwstage half landed the - * fnarg/ret prologues converge to cstage, so both read both fields and - * match byte-for-byte. mixed (an init row) stays runtime-only. + * 826_alias_tuple_coerce — residual asymmetric and assembly byte checks. + * Symmetric runtime ownership is the r826_* compiler fixture set; the three + * byte rows compile those exact files directly. Two negative controls remain + * embedded because cstage must reject them while wwstage still over-accepts + * them, so the symmetric fixture grammar cannot own that assertion. */ #include #include #include -#include #include static int @@ -63,90 +19,15 @@ runwait(const char *cmd) return -1; } -struct row { const char *label; const char *src; int want; int byteid; }; +struct row { const char *label; const char *path; }; static const struct row rows[] = { - /* init — THE construct: untyped tuple literal -> alias, read both - * fields. byte-id clean across stages. */ - { "init", - "package main;\n" - "type pair = (int, int);\n" - "export fn main() i32 = {\n" - "\tlet x: pair = (3, 4);\n" - "\treturn (x.0 + x.1): i32;\n" - "};\n", - 7, 1 }, - - /* fnarg — untyped tuple literal coerced into an alias-typed param at - * the call site, reading BOTH fields. The wwstage tuple-PARAM spill - * chase (cgendecl.ww, #99 second half) sizes the alias param slot 16B - * and spills both arg regs, so p.0+p.1 is correct and byte-identical - * to cstage. */ - { "fnarg", - "package main;\n" - "type pair = (int, int);\n" - "fn g(p: pair) i32 = {\n" - "\treturn (p.0 + p.1): i32;\n" - "};\n" - "export fn main() i32 = {\n" - "\treturn g((3, 4));\n" - "};\n", - 7, 1 }, - - /* ret — untyped tuple literal coerced into the declared alias return - * type; the receiving alias-tuple let reads both fields, byte-id. */ - { "ret", - "package main;\n" - "type pair = (int, int);\n" - "fn f() pair = {\n" - "\treturn (3, 4);\n" - "};\n" - "export fn main() i32 = {\n" - "\tlet r: pair = f();\n" - "\treturn (r.0 + r.1): i32;\n" - "};\n", - 7, 1 }, - - /* mixed — heterogeneous element types under the alias; each untyped - * element coerces to its declared field type. runtime-only. */ - { "mixed", - "package main;\n" - "type box = (int, u16);\n" - "export fn main() i32 = {\n" - "\tlet x: box = (3, 4);\n" - "\treturn (x.0 + (x.1: int)): i32;\n" - "};\n", - 7, 0 }, - - /* ctrl_dir — direct (un-aliased) tuple init; already worked, guards - * against a regression in the original tuple path. */ - { "ctrl_dir", - "package main;\n" - "export fn main() i32 = {\n" - "\tlet x: (int, int) = (3, 4);\n" - "\treturn (x.0 + x.1): i32;\n" - "};\n", - 7, 0 }, - - /* ctrl_typed — alias init with already-typed elements (no untyped - * coercion); already worked via the named-arm type_eq. */ - { "ctrl_typed", - "package main;\n" - "type pair = (int, int);\n" - "export fn main() i32 = {\n" - "\tlet x: pair = (3: int, 4: int);\n" - "\treturn (x.0 + x.1): i32;\n" - "};\n", - 7, 0 }, + { "init", "test/wcc/data/r826_alias_tuple_init/case.ww" }, + { "fnarg", "test/wcc/data/r826_alias_tuple_fnarg/case.ww" }, + { "ret", "test/wcc/data/r826_alias_tuple_ret/case.ww" }, }; -/* neg — cstage must STILL reject these (the coercion type-checks per - * element; a type mismatch or wrong arity is not assignable). cstage-only: - * wwstage pre-existingly over-accepts a mismatched tuple init (a separate - * wwstage bug, out of #99 scope), so the negative is asserted against the - * cstage driver only — the side #99 touches. */ static const char *neg[] = { - /* neg_type — second element str vs declared int. */ "package main;\n" "type pair = (int, int);\n" "export fn main() i32 = {\n" @@ -154,7 +35,6 @@ static const char *neg[] = { "\treturn x.0: i32;\n" "};\n", - /* neg_arity — three elements vs the two-element alias. */ "package main;\n" "type pair = (int, int);\n" "export fn main() i32 = {\n" @@ -164,93 +44,47 @@ static const char *neg[] = { }; static int -run_driver(const char *driver, const struct row *r, int i) +compile_should_fail(const char *compiler, const char *src, int i) { - char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/atc_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/atc_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/atc_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); - - FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return -1; } - fputs(r->src, f); - fclose(f); - - snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null", - driver, outbin, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: build via %s failed\n", - r->label, driver); - runwait(rmcmd); - return -1; - } - - int got = runwait(outbin); - - runwait(rmcmd); - return got; -} - -/* build_should_fail — the negative control must error on `driver`; returns - * 0 when the build correctly FAILS, non-zero when it wrongly succeeded. */ -static int -build_should_fail(const char *driver, const char *src, int i) -{ - char tmpdir[64], s[128], outbin[128], rmcmd[160], cmd[1024]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/atc_neg_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(s, sizeof s, "%s/atc_neg_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/atc_neg_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); - + char s[128], outasm[128], cmd[1024]; + snprintf(s, sizeof s, "/tmp/atc_neg_%d_%d.ww", getpid(), i); + snprintf(outasm, sizeof outasm, "/tmp/atc_neg_%d_%d.s", getpid(), i); FILE *f = fopen(s, "wb"); - if (!f) { runwait(rmcmd); return -1; } + if (!f) return -1; fputs(src, f); fclose(f); - - snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null", - driver, outbin, s); + snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", + compiler, outasm, s); int rc = runwait(cmd); - runwait(rmcmd); - return rc == 0 ? -1 : 0; /* build must NOT succeed */ + unlink(s); + unlink(outasm); + return rc == 0 ? -1 : 0; } -/* asm_byte_identical — w6c vs w6c_ww .s for the same source must match. */ static int asm_byte_identical(const char *bin, const struct row *r, int i) { - char src[64], cs[64], ws[64], cmd[1024]; - snprintf(src, sizeof src, "/tmp/atc_asm_%d_%d.ww", getpid(), i); - snprintf(cs, sizeof cs, "/tmp/atc_asm_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/atc_asm_%d_%d_w.s", getpid(), i); - - FILE *f = fopen(src, "wb"); - if (!f) return -1; - fputs(r->src, f); - fclose(f); - - snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src); + char cs[64], ws[64], cmd[1024]; + snprintf(cs, sizeof cs, "/tmp/atc_asm_%d_%d_c.s", getpid(), i); + snprintf(ws, sizeof ws, "/tmp/atc_asm_%d_%d_w.s", getpid(), i); + snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", + bin, cs, r->path); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c errored\n", r->label); - unlink(src); return -1; } snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null", - bin, ws, src); + bin, ws, r->path); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label); - unlink(src); unlink(cs); + unlink(cs); return -1; } - FILE *fc = fopen(cs, "rb"); FILE *fw = fopen(ws, "rb"); int rc = 0; - if (!fc || !fw) { - rc = -1; - } else { + if (!fc || !fw) rc = -1; + else { for (;;) { int a = fgetc(fc); int b = fgetc(fw); @@ -263,7 +97,8 @@ asm_byte_identical(const char *bin, const struct row *r, int i) if (rc != 0) fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n", r->label); - unlink(src); unlink(cs); unlink(ws); + unlink(cs); + unlink(ws); return rc; } @@ -279,71 +114,33 @@ main(void) snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); bin = absbin; } - - char cdrv[1024]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - char wdrv[1024]; - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - - struct { const char *name; const char *path; int gated_on_existence; } - drivers[] = { - { "cstage", cdrv, 0 }, - { "wwstage", wdrv, 1 }, - { NULL, NULL, 0 }, - }; - + char cdrv[1024], wdrv[1024]; + snprintf(cdrv, sizeof cdrv, "%s/w6c", bin); + snprintf(wdrv, sizeof wdrv, "%s/w6c_ww", bin); int n = (int)(sizeof rows / sizeof rows[0]); int nneg = (int)(sizeof neg / sizeof neg[0]); int total = 0, fail = 0; - - for (int d = 0; drivers[d].name; d++) { - if (drivers[d].gated_on_existence - && access(drivers[d].path, X_OK) != 0) { - fprintf(stderr, "alias_tuple_coerce: skip %s (no %s)\n", - drivers[d].name, drivers[d].path); - continue; - } - for (int i = 0; i < n; i++) { - int got = run_driver(drivers[d].path, &rows[i], i); - total++; - if (got != rows[i].want) { - fprintf(stderr, - "alias_tuple_coerce[%s][%s]: exit=%d want=%d\n", - drivers[d].name, rows[i].label, - got, rows[i].want); - fail++; - } - } - } - - /* Negative controls — cstage must STILL reject (don't over-accept). - * cstage-only: the #99 fix is cstage-side, and wwstage pre-existingly - * over-accepts a mismatched tuple init (out-of-scope wwstage bug). */ for (int i = 0; i < nneg; i++) { total++; - if (build_should_fail(cdrv, neg[i], i) != 0) { + if (compile_should_fail(cdrv, neg[i], i) != 0) { fprintf(stderr, "alias_tuple_coerce[cstage][neg_%d]: built (should loud)\n", i); fail++; } } - - /* byte-id — only rows flagged byteid (the INIT form). */ if (access(wdrv, X_OK) == 0) { for (int i = 0; i < n; i++) { - if (!rows[i].byteid) continue; total++; - if (asm_byte_identical(bin, &rows[i], i) != 0) - fail++; + if (asm_byte_identical(bin, &rows[i], i) != 0) fail++; } } - if (fail) { fprintf(stderr, - "alias_tuple_coerce: %d/%d fixtures failed\n", fail, total); + "alias_tuple_coerce: %d/%d residual checks failed\n", + fail, total); return 1; } - printf("alias_tuple_coerce: %d/%d ok\n", total, total); + printf("alias_tuple_coerce: %d/%d residual checks ok\n", total, total); return 0; } diff --git a/test/wcc/839_xmod_nominal_typeeqast.c b/test/wcc/839_xmod_nominal_typeeqast.c index 205cca95..6568c354 100644 --- a/test/wcc/839_xmod_nominal_typeeqast.c +++ b/test/wcc/839_xmod_nominal_typeeqast.c @@ -53,6 +53,7 @@ */ #include #include +#include #include #include #include @@ -194,8 +195,13 @@ run_one(const struct driver *drv, const struct scenario *sc) drv->name, sc->label, sc->files[i].name); rc = -1; goto done; } - fputs(sc->files[i].src, f); - fclose(f); + int writebad = fputs(sc->files[i].src, f) == EOF || ferror(f); + if (fclose(f) != 0) writebad = 1; + if (writebad) { + fprintf(stderr, "839[%s][%s]: write %s\n", + drv->name, sc->label, sc->files[i].name); + rc = -1; goto done; + } } snprintf(cmd, sizeof cmd, "cd %s && %s build -I %s %s/main.ww " @@ -226,8 +232,25 @@ run_one(const struct driver *drv, const struct scenario *sc) } done: - snprintf(cmd, sizeof cmd, "rm -rf %s", dir); - (void)runwait(cmd); + { + int cleanfail = 0; + for (int i = 0; sc->files[i].name; i++) { + snprintf(path, sizeof path, "%s/%s", dir, + sc->files[i].name); + if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1; + } + snprintf(path, sizeof path, "%s/main", dir); + if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1; + snprintf(path, sizeof path, "%s/main.sepwork", dir); + snprintf(cmd, sizeof cmd, "rm -rf %s", path); + if (runwait(cmd) != 0) cleanfail = 1; + if (rmdir(dir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "839[%s][%s]: workspace cleanup failed\n", + drv->name, sc->label); + if (rc == 0) rc = -1; + } + } return rc; } diff --git a/test/wcc/844_size_untyped_int.c b/test/wcc/844_size_untyped_int.c index 14b5071e..2f368596 100644 --- a/test/wcc/844_size_untyped_int.c +++ b/test/wcc/844_size_untyped_int.c @@ -1,51 +1,12 @@ /* - * 844_size_untyped_int — the size/align/offset builtins fold to an - * untyped_int constant and RETURN untyped_int to the caller (the - * Hare-correct type), so `let x: size = size(T)` is assignable - * (catB-9; 2026-06-14). - * - * The fold stamps the node `e.type_ = untyped_int` in BOTH stages - * (correct), but pre-fix wwstage's check.ww RETURNED `i32` to the - * caller while cstage returned `untyped_int` (check.c:1570/1602). - * untyped_int is assignable to any integer type (size, u64, ...); - * i32 is NOT assignable to `size`. So `let lenbytes: size = - * size(u64);` — the canonical Hare idiom (sha256.ww:189) — was - * FALSE-REJECTED by wwstage (`let: not assignable (i32 -> size)`) - * while cstage accepted. The fix aligns wwstage UP: check.ww:3099/ - * 3108/3125 now return mktname("untyped_int"). cstage is unchanged. - * - * DISCRIMINATION (verified against the pre-fix master out/bin/ww_ww): - * p1 built rc=1 with `let: not assignable (i32 -> size)` on wwstage - * and rc=0 on cstage; post-fix both accept. (A live discrimination - * row can't run here — the post-fix binary accepts p1 — so the - * pre-fix reject is pinned in this comment, not as a runtime row.) - * - * row | shape | want - * --------+----------------------------------------+----- - * p1 | let lenbytes: size = size(u64); | 8 - * p2 | let x: u64 = size(int); | 8 - * p3 | let pad: size = size(u64) - n; (n=1) | 7 - * p7_ctrl | let x = size(u64); (UNANNOTATED) | 8 - * - * p7_ctrl is drew's de-risk control: an unannotated `let` defaults - * the untyped_int to int and behaves IDENTICALLY in both stages, - * pre AND post fix (a both-ACCEPT, run 8). It proves the return-type - * flip did not perturb the default-int path. - * - * BYTE-ID (rule 10): the bid program mixes a `size`-typed local with - * `size(u64)` in a comparison — the context where the fold's new - * untyped_int could converge a type and shift CMPL->CMPQ width. Post- - * fix cstage's w6c and wwstage's w6c_ww emit byte-identical asm (both - * CMPQ); the row diffs the two .s files. - * - * Table-driven, BOTH stages (cstage `ww`, wwstage `ww_ww` gated on - * existence), mirrors 682_arr_enum_elem / 842_dup_main_reject. - * Intermediates land in tmpdirs, never the source tree (rule 14). + * Runtime rows moved to wwfixtures. This residual retains the distinct + * rule-10 probe: a size-typed comparison must emit byte-identical assembly + * through the C and WW frontends. */ #include #include +#include #include -#include #include static int @@ -57,41 +18,6 @@ runwait(const char *cmd) return -1; } -struct row { const char *label; const char *src; int want; }; - -static const struct row rows[] = { - { "p1_let_size", - "package main;\n" - "export fn main() i32 = {\n" - " let lenbytes: size = size(u64);\n" - " return lenbytes: i32;\n" - "};\n", 8 }, - - { "p2_let_u64", - "package main;\n" - "export fn main() i32 = {\n" - " let x: u64 = size(int);\n" - " return x: i32;\n" - "};\n", 8 }, - - { "p3_size_arith", - "package main;\n" - "export fn main() i32 = {\n" - " let n: size = 1;\n" - " let pad: size = size(u64) - n;\n" - " return pad: i32;\n" - "};\n", 7 }, - - { "p7_ctrl_unannotated", - "package main;\n" - "export fn main() i32 = {\n" - " let x = size(u64);\n" - " return x: i32;\n" - "};\n", 8 }, -}; - -/* the byte-id program: a size()-mixing comparison (the CMPL->CMPQ - * width context). Both stages must emit identical asm. */ static const char *bid_src = "package main;\n" "export fn main() i32 = {\n" @@ -101,42 +27,12 @@ static const char *bid_src = "};\n"; static int -run_driver(const char *driver, const struct row *r, int i) -{ - char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/szui_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/szui_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/szui_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); - - FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return -1; } - fputs(r->src, f); - fclose(f); - - snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null", - driver, outbin, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: build via %s failed\n", - r->label, driver); - runwait(rmcmd); - return -1; - } - - int got = runwait(outbin); - - runwait(rmcmd); - return got; -} - -static int -asm_byte_identical(const char *bin, int i) +asm_byte_identical(const char *bin) { char src[64], cs[64], ws[64], cmd[1024]; - snprintf(src, sizeof src, "/tmp/szui_asm_%d_%d.ww", getpid(), i); - snprintf(cs, sizeof cs, "/tmp/szui_asm_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/szui_asm_%d_%d_w.s", getpid(), i); + snprintf(src, sizeof src, "/tmp/szui_asm_%d.ww", getpid()); + snprintf(cs, sizeof cs, "/tmp/szui_asm_%d_c.s", getpid()); + snprintf(ws, sizeof ws, "/tmp/szui_asm_%d_w.s", getpid()); FILE *f = fopen(src, "wb"); if (!f) return -1; @@ -146,14 +42,18 @@ asm_byte_identical(const char *bin, int i) snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src); if (runwait(cmd) != 0) { fprintf(stderr, "bid: w6c errored\n"); - unlink(src); + /* the failed leg may still have written a partial .s. */ + if (unlink(src) != 0 && errno != ENOENT) perror(src); + if (unlink(cs) != 0 && errno != ENOENT) perror(cs); return -1; } snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null", bin, ws, src); if (runwait(cmd) != 0) { fprintf(stderr, "bid: w6c_ww errored\n"); - unlink(src); unlink(cs); + if (unlink(src) != 0 && errno != ENOENT) perror(src); + if (unlink(cs) != 0 && errno != ENOENT) perror(cs); + if (unlink(ws) != 0 && errno != ENOENT) perror(ws); return -1; } @@ -174,7 +74,10 @@ asm_byte_identical(const char *bin, int i) if (fw) fclose(fw); if (rc != 0) fprintf(stderr, "bid: cstage vs wwstage asm differs\n"); - unlink(src); unlink(cs); unlink(ws); + /* a silent cleanup failure must fail an otherwise-passing probe. */ + if (unlink(src) != 0 && errno != ENOENT) { perror(src); rc = -1; } + if (unlink(cs) != 0 && errno != ENOENT) { perror(cs); rc = -1; } + if (unlink(ws) != 0 && errno != ENOENT) { perror(ws); rc = -1; } return rc; } @@ -191,47 +94,19 @@ main(void) bin = absbin; } - char cdrv[1024], wdrv[1024]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - - struct { const char *name; const char *path; int gated; } drivers[] = { - { "cstage", cdrv, 0 }, - { "wwstage", wdrv, 1 }, - { NULL, NULL, 0 }, - }; - - int n = (int)(sizeof rows / sizeof rows[0]); + char wdrv[1024]; + /* gate on the binary the probe actually invokes (w6c_ww), not the + * ww_ww driver — a driver-only or frontend-only build would + * otherwise skip real coverage or fail vacuously. */ + snprintf(wdrv, sizeof wdrv, "%s/w6c_ww", bin); int total = 0, fail = 0; - - for (int d = 0; drivers[d].name; d++) { - if (drivers[d].gated && access(drivers[d].path, X_OK) != 0) { - fprintf(stderr, "size_untyped_int: skip %s (no %s)\n", - drivers[d].name, drivers[d].path); - continue; - } - for (int i = 0; i < n; i++) { - total++; - int got = run_driver(drivers[d].path, &rows[i], i); - if (got != rows[i].want) { - fprintf(stderr, - "size_untyped_int[%s][%s]: exit=%d want=%d\n", - drivers[d].name, rows[i].label, - got, rows[i].want); - fail++; - } - } - } - if (access(wdrv, X_OK) == 0) { total++; - if (asm_byte_identical(bin, 0) != 0) - fail++; + if (asm_byte_identical(bin) != 0) fail++; } - if (fail) { - fprintf(stderr, - "size_untyped_int: %d/%d fixtures failed\n", fail, total); + fprintf(stderr, "size_untyped_int: %d/%d fixtures failed\n", + fail, total); return 1; } printf("size_untyped_int: %d/%d ok\n", total, total); diff --git a/test/wcc/848_xmod_qualstructlit_run.c b/test/wcc/848_xmod_qualstructlit_run.c index 15c48a2b..c12658c6 100644 --- a/test/wcc/848_xmod_qualstructlit_run.c +++ b/test/wcc/848_xmod_qualstructlit_run.c @@ -59,6 +59,7 @@ #include #include #include +#include #include #include #include @@ -143,8 +144,12 @@ run_ast_proof(const char *wwdump, const char *wwdump_ww, FILE *f = fopen(src, "wb"); if (!f) { fprintf(stderr, "848-ast[%s]: write\n", ac->label); rc = -1; goto done; } - fputs(ac->src, f); - fclose(f); + int writebad = fputs(ac->src, f) == EOF || ferror(f); + if (fclose(f) != 0) writebad = 1; + if (writebad) { + fprintf(stderr, "848-ast[%s]: write\n", ac->label); + rc = -1; goto done; + } snprintf(cs, sizeof cs, "%s/cs.ast", dir); snprintf(ww, sizeof ww, "%s/ww.ast", dir); @@ -176,8 +181,21 @@ run_ast_proof(const char *wwdump, const char *wwdump_ww, rc = -1; } done: - snprintf(cmd, sizeof cmd, "rm -rf %s", dir); - (void)runwait(cmd); + { + int cleanfail = 0; + snprintf(src, sizeof src, "%s/a.ww", dir); + if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1; + snprintf(cs, sizeof cs, "%s/cs.ast", dir); + if (unlink(cs) != 0 && errno != ENOENT) cleanfail = 1; + snprintf(ww, sizeof ww, "%s/ww.ast", dir); + if (unlink(ww) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(dir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "848-ast[%s]: workspace cleanup failed\n", + ac->label); + if (rc == 0) rc = -1; + } + } return rc; } @@ -308,47 +326,39 @@ run_scenario(const char *bin, const char *cdrv, const char *wdrv, FILE *f = fopen(path, "wb"); if (!f) { fprintf(stderr, "848[%s]: write %s\n", sc->label, sc->files[i].name); rc = -1; goto done; } - fputs(sc->files[i].src, f); - fclose(f); + int writebad = fputs(sc->files[i].src, f) == EOF || ferror(f); + if (fclose(f) != 0) writebad = 1; + if (writebad) { + fprintf(stderr, "848[%s]: write %s\n", sc->label, + sc->files[i].name); + rc = -1; goto done; + } } - /* #94 sep layout: cstage --sep build + run pins parse + runtime field - * values; pin WW_PKGCACHE under the scratch dir. */ + /* Runtime values moved to wwfixtures. These builds intentionally keep + * their caller-owned compiler artifacts for the byte assertion below. */ snprintf(cmd, sizeof cmd, - "cd %s && WW_PKGCACHE=%s/pkgc_c %s build --sep -I %s -o %s/main %s/main.ww", - dir, dir, cdrv, dir, dir, dir); + "cd %s && %s build -I %s -o %s/main %s/main.ww", + dir, cdrv, dir, dir, dir); if (runwait(cmd) != 0) { fprintf(stderr, "848[%s]: cstage build failed " "(qualified struct literal parse reject?)\n", sc->label); rc = -1; goto done; } - snprintf(path, sizeof path, "%s/main", dir); - int got = runwait(path); - if (got != sc->want_exit) { - fprintf(stderr, "848[%s]: cstage exit %d, want %d\n", - sc->label, got, sc->want_exit); - rc = -1; - } - - /* rule-10 discriminator: the wwstage driver's --sep build emits - * per-package w6c_ww asm that must be byte-identical to cstage's. The - * root + each imported pkg compile to separate .sepwork/.s; - * concat (sorted glob, identical set both stages) for the compare. */ + /* rule-10 discriminator: this colocated two-file fixture composes into + * the exact retained __root.s artifact, which must be byte-identical + * across driver stages. */ char cs_s[1024], ws_s[1024]; snprintf(cmd, sizeof cmd, - "cd %s && WW_PKGCACHE=%s/pkgc_w %s build --sep -I %s -o %s/mainww %s/main.ww", - dir, dir, wdrv, dir, dir, dir); + "cd %s && %s build -I %s -o %s/mainww %s/main.ww", + dir, wdrv, dir, dir, dir); if (runwait(cmd) != 0) { fprintf(stderr, "848[%s]: ww_ww build failed " "(qualified struct literal parse reject?)\n", sc->label); rc = -1; goto done; } - snprintf(cs_s, sizeof cs_s, "%s/all_cs.s", dir); - snprintf(ws_s, sizeof ws_s, "%s/all_ww.s", dir); - snprintf(cmd, sizeof cmd, "cat %s/main.sepwork/*.s > %s 2>/dev/null", - dir, cs_s); if (system(cmd)) {} - snprintf(cmd, sizeof cmd, "cat %s/mainww.sepwork/*.s > %s 2>/dev/null", - dir, ws_s); if (system(cmd)) {} + snprintf(cs_s, sizeof cs_s, "%s/main.sepwork/__root.s", dir); + snprintf(ws_s, sizeof ws_s, "%s/mainww.sepwork/__root.s", dir); if (slurp_eq(cs_s, ws_s) != 0) { fprintf(stderr, "848[%s]: cs.s/ww.s DIFFER (rule-10 byte-id " "violation)\n", sc->label); @@ -356,8 +366,31 @@ run_scenario(const char *bin, const char *cdrv, const char *wdrv, } done: - snprintf(cmd, sizeof cmd, "rm -rf %s", dir); - (void)runwait(cmd); + { + int cleanfail = 0; + for (int i = 0; sc->files[i].name; i++) { + snprintf(path, sizeof path, "%s/%s", dir, + sc->files[i].name); + if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1; + } + const char *files[] = { "main", "mainww", NULL }; + for (int i = 0; files[i]; i++) { + snprintf(path, sizeof path, "%s/%s", dir, files[i]); + if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1; + } + snprintf(path, sizeof path, "%s/main.sepwork", dir); + snprintf(cmd, sizeof cmd, "rm -rf %s", path); + if (runwait(cmd) != 0) cleanfail = 1; + snprintf(path, sizeof path, "%s/mainww.sepwork", dir); + snprintf(cmd, sizeof cmd, "rm -rf %s", path); + if (runwait(cmd) != 0) cleanfail = 1; + if (rmdir(dir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "848[%s]: workspace cleanup failed\n", + sc->label); + if (rc == 0) rc = -1; + } + } return rc; } diff --git a/test/wcc/901_asserttyped_gap.c b/test/wcc/901_asserttyped_gap.c index a097a3ab..84df0c2a 100644 --- a/test/wcc/901_asserttyped_gap.c +++ b/test/wcc/901_asserttyped_gap.c @@ -22,7 +22,7 @@ * * #90 sep-feed: the E3 flip retired the combined.ww amalgamator, so the * library/selfhost gap fixtures (A-D) feed their RESOLVED sep unit (see - * resolveunit — `ww build --sep` composes .sepwork/__root.unit.ww + * resolveunit — `ww build -S` composes .sepwork/__root.unit.ww * from the dep `.wwi` stubs + the root body) rather than a pre-built * .combined.ww. The single-file, import-free test fixtures (E-G) * carry no imports and stay raw-fed. @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -91,47 +92,25 @@ slurp(const char *path, char **outbuf, size_t *outlen) } /* resolveunit — compose 's RESOLVED translation unit the way the - * sep driver does. Mirrors 990_selfhost's resolveunit: copy the fixture's - * whole package directory into a private /tmp dir (so a white-box `_test` - * file's sibling package sources resolve, and the sepwork lands off the repo - * tree), `ww build --sep `, then read .sepwork/__root.unit.ww — + * sep driver does. `ww build -S -o ` reads the fixture and its + * sibling package sources in place while putting caller-owned compiler + * artifacts below the private workspace, then this probe reads the exact + * .sepwork/__root.unit.ww artifact — * the single self-contained unit w6c/wwdump consume for the root. Feeding the * raw module file instead would leave its import refs (os/fmt/strconv …) - * unresolved, a partial unit the asserttyped invariant must never see. The - * link step fails for an import-only module (no main), but the unit is - * written before the link, so we detect the artifact rather than gating on - * the build exit. Writes the owning tmpdir into `td` (caller rm -rf's it) and - * the unit path into `out`. Returns 0 on success, -1 otherwise. */ + * unresolved, a partial unit the asserttyped invariant must never see. + * Returns the unit path in `out`, 0 on success, and -1 otherwise. */ static int resolveunit(const char *bin, const char *cwd, const char *fixture, - int idx, char *td, size_t tdsz, char *out, size_t outsz) + const char *work, char *out, size_t outsz) { - char cmd[2048]; - snprintf(td, tdsz, "/tmp/atgap_ru_%d_%d", getpid(), idx); - snprintf(cmd, sizeof cmd, "rm -rf %s", td); - runwait(cmd); - mkdir(td, 0755); - const char *slash = strrchr(fixture, '/'); - const char *base = slash ? slash + 1 : fixture; - char dir[1024]; - if (slash) { - size_t dl = (size_t)(slash - fixture); - if (dl >= sizeof dir) return -1; - memcpy(dir, fixture, dl); - dir[dl] = '\0'; - } else { - dir[0] = '.'; dir[1] = '\0'; - } - snprintf(cmd, sizeof cmd, "cp %s/%s/*.ww %s/ 2>/dev/null", cwd, dir, td); - runwait(cmd); + char cmd[4096], src[2048], stem[2048]; + snprintf(src, sizeof src, "%s/%s", cwd, fixture); + snprintf(stem, sizeof stem, "%s/unit", work); snprintf(cmd, sizeof cmd, - "cd %s && timeout 180 %s/ww build --sep %s >/dev/null 2>&1", - td, bin, base); - runwait(cmd); - char stem[1024]; - snprintf(stem, sizeof stem, "%s/%s", td, base); - char *dot = strrchr(stem, '.'); - if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; + "timeout 180 %s/ww build -S -o %s %s >/dev/null 2>&1", + bin, stem, src); + if (runwait(cmd) != 0) return -1; snprintf(out, outsz, "%s.sepwork/__root.unit.ww", stem); return access(out, 0) == 0 ? 0 : -1; } @@ -187,30 +166,39 @@ main(void) int fail = 0, n = 0; for (int i = 0; manifest[i].rel; i++) { - char src[2048], errf[64], cmd[4096]; - char td[64] = {0}, unit[1280]; + char work[] = "/tmp/atgap_XXXXXX"; + char src[2048], errf[256], cmd[4096], unit[1280]; + char stem[256], scratch[288]; + int rowfail = 0; + n++; + if (mkdtemp(work) == NULL) { + fprintf(stderr, "asserttyped_gap FAIL: %s [%s] " + "workspace acquisition failed\n", + manifest[i].rel, manifest[i].cls); + fail++; + continue; + } + snprintf(stem, sizeof stem, "%s/unit", work); + snprintf(scratch, sizeof scratch, "%s.sepwork", stem); + snprintf(errf, sizeof errf, "%s/asserttyped.err", work); if (manifest[i].sep) { - if (resolveunit(bin, cwd, manifest[i].rel, i, td, - sizeof td, unit, sizeof unit) != 0) { + if (resolveunit(bin, cwd, manifest[i].rel, work, unit, + sizeof unit) != 0) { fprintf(stderr, "asserttyped_gap FAIL: %s [%s] " "no resolved sep unit\n", manifest[i].rel, manifest[i].cls); - if (td[0]) { snprintf(cmd, sizeof cmd, - "rm -rf %s", td); runwait(cmd); } - fail++; n++; - continue; + rowfail = 1; + goto row_done; } snprintf(src, sizeof src, "%s", unit); } else { snprintf(src, sizeof src, "%s/%s", cwd, manifest[i].rel); } - snprintf(errf, sizeof errf, "/tmp/atgap_%d_%d.err", getpid(), i); snprintf(cmd, sizeof cmd, "timeout 180 %s/wwdump_ww -c %s >/dev/null 2>%s", bin, src, errf); runwait(cmd); int got = count_asserttyped(errf); - n++; if (got != manifest[i].want) { fprintf(stderr, "asserttyped_gap FAIL: %s [%s] expected %d, got %d\n", @@ -220,11 +208,25 @@ main(void) snprintf(dump, sizeof dump, "grep '^asserttyped:' %s 1>&2", errf); runwait(dump); - fail++; + rowfail = 1; } - unlink(errf); - if (td[0]) { snprintf(cmd, sizeof cmd, "rm -rf %s", td); - runwait(cmd); } + + row_done: + { + int cleanfail = 0; + if (unlink(errf) != 0 && errno != ENOENT) cleanfail = 1; + snprintf(cmd, sizeof cmd, "rm -rf %s", scratch); + if (runwait(cmd) != 0) cleanfail = 1; + if (unlink(stem) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(work) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "asserttyped_gap FAIL: %s [%s] " + "workspace cleanup failed\n", + manifest[i].rel, manifest[i].cls); + rowfail = 1; + } + } + if (rowfail) fail++; } if (fail) { diff --git a/test/wcc/911_attest_record.c b/test/wcc/911_attest_record.c index 26f8164e..bd639e59 100644 --- a/test/wcc/911_attest_record.c +++ b/test/wcc/911_attest_record.c @@ -8,7 +8,7 @@ * assert-fail/SIGSEGV/SIGFPE, then a final pass). The runner must * proceed past every failure, emit the right per-test `name ... ok/ * FAIL` line (table-driven below), print the right `P passed, F - * failed` summary, and exit with the failure count (nonzero). + * failed` summary, and clamp any failure set to exit 1. * 2. ALL-PASS — `ww test` the all-pass fixture exits 0 (no false * failure from the new runner). * 3. BYTE-ID (rule 10) — `ww test -c` the mixed fixture into a @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -64,41 +65,57 @@ static const char *expect_lines[] = { "rec_segv ... FAIL", "rec_div0 ... FAIL", "rec_pass_b ... ok", - "2 passed, 3 failed", + "2 passed, 3 failed, 0 skipped, 0 harness errors", + "5 discovered, 5 selected, 5 started, 5 completed", }; static int record_continue(const char *bin) { - int pid = getpid(); + char dir[] = "/tmp/at911_record_XXXXXX"; char out[256], cmd[4096], buf[8192]; - snprintf(out, sizeof out, "/tmp/at911_%d.out", pid); + int result = 1; + if (mkdtemp(dir) == NULL) { + fprintf(stderr, "911 FAIL: record workspace acquisition failed\n"); + return 1; + } + snprintf(out, sizeof out, "%s/output", dir); /* `ww test` builds the fixture (auto-bundling lib/test) and runs it; - * the exit code is the runner's failure count. */ + * three failures remain visible in output while the public exit clamps. */ snprintf(cmd, sizeof cmd, "%s/ww test test/wcc/data/attest_record.ww > %s 2>/dev/null", bin, out); int rc = runwait(cmd); - if (rc != 3) { + if (rc != 1) { fprintf(stderr, "911 FAIL: record-and-continue exit %d " - "(expected 3 failures)\n", rc); - return 1; + "(expected canonical failure exit 1)\n", rc); + goto done; } if (slurp(out, buf, sizeof buf) != 0) { fprintf(stderr, "911 FAIL: cannot read runner output\n"); - return 1; + goto done; } for (size_t i = 0; i < sizeof expect_lines / sizeof expect_lines[0]; i++) { if (strstr(buf, expect_lines[i]) == NULL) { fprintf(stderr, "911 FAIL: missing runner line '%s'\n" "--- got ---\n%s\n", expect_lines[i], buf); - unlink(out); - return 1; + goto done; } } - unlink(out); - return 0; + result = 0; + +done: + { + int cleanfail = 0; + if (unlink(out) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(dir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "911 FAIL: record workspace cleanup failed\n"); + result = 1; + } + } + return result; } static int @@ -119,39 +136,53 @@ all_pass(const char *bin) static int byteid(const char *bin) { - int pid = getpid(); + char dir[] = "/tmp/at911_bid_XXXXXX"; char stemc[256], stemw[256], cs[256], ws[256], cmd[4096]; - snprintf(stemc, sizeof stemc, "/tmp/at911_bid_c_%d", pid); - snprintf(stemw, sizeof stemw, "/tmp/at911_bid_w_%d", pid); - snprintf(cs, sizeof cs, "/tmp/at911_bid_cs_%d.s", pid); - snprintf(ws, sizeof ws, "/tmp/at911_bid_ws_%d.s", pid); + char scratchc[288], scratchw[288]; + int rc = 1; + if (mkdtemp(dir) == NULL) { + fprintf(stderr, "911 FAIL: byteid workspace acquisition failed\n"); + return 1; + } + snprintf(stemc, sizeof stemc, "%s/cstage", dir); + snprintf(stemw, sizeof stemw, "%s/wwstage", dir); + snprintf(cs, sizeof cs, "%s/cstage.s", dir); + snprintf(ws, sizeof ws, "%s/wwstage.s", dir); + snprintf(scratchc, sizeof scratchc, "%s.sepwork", stemc); + snprintf(scratchw, sizeof scratchw, "%s.sepwork", stemw); - /* #94 sep layout: `ww test -c --sep -o ` carries -T to the root + /* #94 sep layout: `ww test -c -o ` carries -T to the root * so w6c synthesizes the test table + fork loop, and emits per-package - * asm to .sepwork/ (compile-only, no harness run; WW_PKGCACHE - * pinned to /tmp). The cstage build (w6c) and wwstage build (w6c_ww) + * asm to .sepwork/ (compile-only, no harness run). The cstage + * build (w6c) and wwstage build (w6c_ww) * must produce byte-identical asm across all packages. */ snprintf(cmd, sizeof cmd, - "WW_PKGCACHE=/tmp/at911_pkgc_c_%d %s/ww test -c --sep -o %s " - "test/wcc/data/attest_record.ww > /dev/null 2>&1", pid, bin, stemc); + "%s/ww test -c -o %s " + "test/wcc/data/attest_record.ww > /dev/null 2>&1", bin, stemc); if (runwait(cmd) != 0) { - fprintf(stderr, "911 FAIL: cstage ww test -c --sep\n"); - return 1; + fprintf(stderr, "911 FAIL: cstage ww test -c \n"); + goto done; } snprintf(cmd, sizeof cmd, - "WW_PKGCACHE=/tmp/at911_pkgc_w_%d %s/ww_ww test -c --sep -o %s " - "test/wcc/data/attest_record.ww > /dev/null 2>&1", pid, bin, stemw); + "%s/ww_ww test -c -o %s " + "test/wcc/data/attest_record.ww > /dev/null 2>&1", bin, stemw); if (runwait(cmd) != 0) { - fprintf(stderr, "911 FAIL: wwstage ww_ww test -c --sep\n"); - return 1; + fprintf(stderr, "911 FAIL: wwstage ww_ww test -c \n"); + goto done; } snprintf(cmd, sizeof cmd, "cat %s.sepwork/*.s > %s 2>/dev/null", stemc, cs); - if (system(cmd)) {} + if (runwait(cmd) != 0) { + fprintf(stderr, "911 FAIL: cstage asm aggregation failed\n"); + goto done; + } snprintf(cmd, sizeof cmd, "cat %s.sepwork/*.s > %s 2>/dev/null", stemw, ws); - if (system(cmd)) {} + if (runwait(cmd) != 0) { + fprintf(stderr, "911 FAIL: wwstage asm aggregation failed\n"); + goto done; + } char bc[262144], bw[262144]; - int rc = 0; + rc = 0; if (slurp(cs, bc, sizeof bc) < 0 || slurp(ws, bw, sizeof bw) < 0) { fprintf(stderr, "911 FAIL: slurp byteid asm\n"); rc = 1; @@ -159,11 +190,23 @@ byteid(const char *bin) fprintf(stderr, "911 FAIL: -T asm differs between stages\n"); rc = 1; } - unlink(cs); unlink(ws); - snprintf(cmd, sizeof cmd, "rm -rf %s.sepwork %s.sepwork %s %s " - "/tmp/at911_pkgc_c_%d /tmp/at911_pkgc_w_%d", - stemc, stemw, stemc, stemw, pid, pid); - if (system(cmd)) {} +done: + { + int cleanfail = 0; + if (unlink(cs) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(ws) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(stemc) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(stemw) != 0 && errno != ENOENT) cleanfail = 1; + snprintf(cmd, sizeof cmd, "rm -rf %s", scratchc); + if (runwait(cmd) != 0) cleanfail = 1; + snprintf(cmd, sizeof cmd, "rm -rf %s", scratchw); + if (runwait(cmd) != 0) cleanfail = 1; + if (rmdir(dir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "911 FAIL: byteid workspace cleanup failed\n"); + rc = 1; + } + } return rc; } @@ -178,6 +221,7 @@ main(void) if (byteid(bin) != 0) return 1; printf("@test record-and-continue: 3 fault classes recorded + run " - "proceeds + counts + all-pass exit 0 + cs/ww byte-id (#17)\n"); + "proceeds + exact counts + failure exit clamps to 1 + all-pass " + "exit 0 + cs/ww byte-id (#17)\n"); return 0; } diff --git a/test/wcc/915_arr_module_index_run.c b/test/wcc/915_arr_module_index_run.c index a34a7e04..a5e3b8b5 100644 --- a/test/wcc/915_arr_module_index_run.c +++ b/test/wcc/915_arr_module_index_run.c @@ -43,6 +43,7 @@ */ #include #include +#include #include #include #include @@ -75,6 +76,7 @@ struct row { * the same u16 sequence strconv used, so the expected exits are unchanged * (idx0→0, idx2→0x0801&0xFF=1, idx4→0x1006&0xFF=6). */ #define PROBE_MOD \ + "package wcmodarr;\n" \ "export let probe_table: [8]u16 = [0u16, 0x0800u16, 0x0801u16, " \ "0x0803u16, 0x1006u16, 0x1009u16, 0x100Du16, 0x1812u16];\n" @@ -169,62 +171,93 @@ main(void) int n = 0, fail = 0; for (int i = 0; rows[i].src; i++, n++) { - char tmpdir[96]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/wwmoda_%d_d_%d", - getpid(), i); - mkdir(tmpdir, 0755); - - char src[160]; + char tmpdir[] = "/tmp/wwmoda_XXXXXX"; + char src[160], moddir[224] = {0}, modf[320] = {0}; + char stem[200] = {0}, scratch[232] = {0}; + char cs_s[200] = {0}, ws_s[200] = {0}, cmd[2048]; + int rowfail = 0, moddir_owned = 0; + if (mkdtemp(tmpdir) == NULL) { + fprintf(stderr, "row[%s]: workspace acquisition failed\n", + rows[i].label); + fail++; + continue; + } snprintf(src, sizeof src, "%s/main915.ww", tmpdir); FILE *f = fopen(src, "wb"); - if (f == NULL) { fail++; rmdir(tmpdir); continue; } + if (f == NULL) { + fprintf(stderr, "row[%s]: source create failed\n", + rows[i].label); + rowfail = 1; + goto row_done; + } fputs(rows[i].src, f); - fclose(f); + int werr = ferror(f); + if (fclose(f) != 0) werr = 1; + if (werr) { + fprintf(stderr, "row[%s]: source write failed\n", + rows[i].label); + rowfail = 1; + goto row_done; + } - char cmd[2048]; /* Exported-probe-table package lives at //.ww * so the sep dep-scan (rooted at the entry's dir) resolves the * `import ;`. */ if (rows[i].modname != NULL) { - char moddir[224], modf[320]; snprintf(moddir, sizeof moddir, "%s/%s", tmpdir, rows[i].modname); - mkdir(moddir, 0755); + if (mkdir(moddir, 0755) != 0) { + fprintf(stderr, "row[%s]: module directory create failed\n", + rows[i].label); + rowfail = 1; + goto row_done; + } + moddir_owned = 1; snprintf(modf, sizeof modf, "%s/%s.ww", moddir, rows[i].modname); FILE *mf = fopen(modf, "wb"); if (mf == NULL) { - fail++; - snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir); - (void)runwait(cmd); - continue; + fprintf(stderr, "row[%s]: module source create failed\n", + rows[i].label); + rowfail = 1; + goto row_done; } fputs(rows[i].modsrc, mf); - fclose(mf); + werr = ferror(mf); + if (fclose(mf) != 0) werr = 1; + if (werr) { + fprintf(stderr, "row[%s]: module source write failed\n", + rows[i].label); + rowfail = 1; + goto row_done; + } } + /* Single-file runtime behavior moved to r915_arr_* wwfixtures. + * Keep auxiliary-package rows here because the fixture grammar + * cannot materialize their directory/import topology. */ + if (rows[i].modname != NULL) { - /* #93 sep layout: build via `--sep -o ` (the exported + /* #93 sep layout: build via `-o ` (the exported * probe table links across packages under separate compilation); - * WW_PKGCACHE pinned under tmpdir. */ - char stem[200]; + * all outputs stay under tmpdir. */ snprintf(stem, sizeof stem, "%s/main915", tmpdir); + snprintf(scratch, sizeof scratch, "%s.sepwork", stem); snprintf(cmd, sizeof cmd, - "cd %s && WW_PKGCACHE=%s/pkgc %s/ww build --sep -o %s %s " - ">/dev/null 2>&1", tmpdir, tmpdir, bin, stem, src); + "cd %s && %s/ww build -o %s %s " + ">/dev/null 2>&1", tmpdir, bin, stem, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: cstage build failed\n", rows[i].label); - fail++; - snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir); - (void)runwait(cmd); - continue; + rowfail = 1; + goto row_done; } int got = runwait(stem); if (got != rows[i].want_exit) { fprintf(stderr, "row[%s]: cstage exit %d, want %d\n", rows[i].label, got, rows[i].want_exit); - fail++; + rowfail = 1; + } } /* Byte-id leg — bare w6c vs w6c_ww (the compiler binaries, NOT @@ -232,7 +265,6 @@ main(void) * fed standalone; the module rows' runtime exit IS their gate * (+ the 994 bootstrap corpus for the cross-stage byte-id). */ if (rows[i].modname == NULL) { - char cs_s[200], ws_s[200]; snprintf(cs_s, sizeof cs_s, "%s/cs.s", tmpdir); snprintf(ws_s, sizeof ws_s, "%s/ww.s", tmpdir); snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", @@ -240,26 +272,49 @@ main(void) if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label); - fail++; + rowfail = 1; } else { snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c_ww, ws_s, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c_ww failed\n", rows[i].label); - fail++; + rowfail = 1; } else if (slurp_eq(cs_s, ws_s) != 0) { fprintf(stderr, "row[%s]: cstage/wwstage .s DIFFER " "(rule-10 byte-id violation)\n", rows[i].label); - fail++; + rowfail = 1; } } } - snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir); - (void)runwait(cmd); + row_done: + { + int cleanfail = 0; + if (cs_s[0] && unlink(cs_s) != 0 && errno != ENOENT) + cleanfail = 1; + if (ws_s[0] && unlink(ws_s) != 0 && errno != ENOENT) + cleanfail = 1; + if (scratch[0]) { + snprintf(cmd, sizeof cmd, "rm -rf %s", scratch); + if (runwait(cmd) != 0) cleanfail = 1; + } + if (stem[0] && unlink(stem) != 0 && errno != ENOENT) + cleanfail = 1; + if (modf[0] && unlink(modf) != 0 && errno != ENOENT) + cleanfail = 1; + if (moddir_owned && rmdir(moddir) != 0) cleanfail = 1; + if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(tmpdir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "row[%s]: workspace cleanup failed\n", + rows[i].label); + rowfail = 1; + } + } + if (rowfail) fail++; } if (fail) { diff --git a/test/wcc/926_tagged_sret_run.c b/test/wcc/926_tagged_sret_run.c index 75a0d6ec..ab9f7f11 100644 --- a/test/wcc/926_tagged_sret_run.c +++ b/test/wcc/926_tagged_sret_run.c @@ -22,14 +22,13 @@ * - runtime: build via the ww / ww_ww drivers and run; the exit * code pins every payload word INCLUDING the last (the * truncation signature). - * Rows flagged `buildfail` must be REJECTED by both stages (the - * rule-7 loud-stops over the unwired #40-family shapes: widening - * sret forward, `?`/arg-position consumption of an sret-class call - * result). + * Rows flagged `buildfail` are source carriers only; their symmetric + * diagnostic checks moved to r926_sret_reject_* wwfixtures. */ #include #include #include +#include #include #include #include @@ -470,24 +469,19 @@ file_has(const char *path, const char *needle) } static int -run_driver(const char *driver, const char *src, const char *label) +run_driver(const char *driver, const char *src, const char *label, + const char *work) { - char tmpdir[128], outbin[256], rmcmd[160], cmd[1024]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/tsret_%d_%s_d", getpid(), driver); - mkdir(tmpdir, 0755); - snprintf(outbin, sizeof outbin, "%s/m", tmpdir); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + char outbin[256], cmd[1024]; + snprintf(outbin, sizeof outbin, "%s/%s.run", work, driver); snprintf(cmd, sizeof cmd, "%s/%s build -o %s %s >/dev/null 2>&1", g_bin, driver, outbin, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: build via %s failed\n", label, driver); - runwait(rmcmd); return -1; } - int got = runwait(outbin); - runwait(rmcmd); - return got; + return runwait(outbin); } int @@ -508,60 +502,57 @@ main(void) int total = 0, fail = 0; for (int i = 0; i < n; i++) { const struct row *r = &rows[i]; - char src[128], cs_s[128], ww_s[128]; - char cs_e[128], ww_e[128]; - snprintf(src, sizeof src, "/tmp/tsret_%d_%d.ww", - getpid(), i); - snprintf(cs_s, sizeof cs_s, "/tmp/tsret_%d_%d_cs.s", - getpid(), i); - snprintf(ww_s, sizeof ww_s, "/tmp/tsret_%d_%d_ww.s", - getpid(), i); - snprintf(cs_e, sizeof cs_e, "/tmp/tsret_%d_%d_cs.err", - getpid(), i); - snprintf(ww_e, sizeof ww_e, "/tmp/tsret_%d_%d_ww.err", - getpid(), i); + /* Loud-reject behavior is owned by r926_sret_reject_* fixtures. */ + if (r->buildfail) continue; + char work[] = "/tmp/tsret_XXXXXX"; + char src[256], cs_s[256], ww_s[256]; + char cs_e[256], ww_e[256], cbin[256], wbin[256]; + char cscratch[288], wscratch[288], cmd[384]; + int rowfail = 0; + if (mkdtemp(work) == NULL) { + fprintf(stderr, "FAIL row[%s]: workspace acquisition failed\n", + r->label); + fail++; + continue; + } + snprintf(src, sizeof src, "%s/input.ww", work); + snprintf(cs_s, sizeof cs_s, "%s/c.s", work); + snprintf(ww_s, sizeof ww_s, "%s/w.s", work); + snprintf(cs_e, sizeof cs_e, "%s/c.err", work); + snprintf(ww_e, sizeof ww_e, "%s/w.err", work); + snprintf(cbin, sizeof cbin, "%s/ww.run", work); + snprintf(wbin, sizeof wbin, "%s/ww_ww.run", work); + snprintf(cscratch, sizeof cscratch, "%s.sepwork", cbin); + snprintf(wscratch, sizeof wscratch, "%s.sepwork", wbin); FILE *f = fopen(src, "wb"); - if (!f) return 1; + if (!f) { + fprintf(stderr, "FAIL row[%s]: source create failed\n", r->label); + rowfail = 1; + goto row_done; + } fputs("package main;\n\n", f); fputs(r->src, f); - fclose(f); + int werr = ferror(f); + if (fclose(f) != 0) werr = 1; + if (werr) { + fprintf(stderr, "FAIL row[%s]: source write failed\n", r->label); + rowfail = 1; + goto row_done; + } int cs_rc = compile_s("w6c", src, cs_s, cs_e); int ww_rc = compile_s("w6c_ww", src, ww_s, ww_e); - if (r->buildfail) { - total++; - if (cs_rc == 0 || ww_rc == 0) { - fprintf(stderr, "FAIL row[%s]: loud-stop " - "expected, cstage rc=%d wwstage rc=%d\n", - r->label, cs_rc, ww_rc); - fail++; - } else if (!(file_has(cs_e, "#38b") || file_has(cs_e, "#40")) - || !(file_has(ww_e, "#38b") || file_has(ww_e, "#40"))) { - /* the rejection must be THE #38b/#40 loud-stop, - * not an unrelated checker error masquerading - * as coverage. */ - fprintf(stderr, "FAIL row[%s]: rejected but " - "without the #38b/#40 loud-stop marker\n", - r->label); - fail++; - } - unlink(src); unlink(cs_s); unlink(ww_s); - unlink(cs_e); unlink(ww_e); - continue; - } total++; if (cs_rc != 0 || ww_rc != 0) { fprintf(stderr, "FAIL row[%s]: compile rc cs=%d " "ww=%d\n", r->label, cs_rc, ww_rc); - fail++; - unlink(src); unlink(cs_s); unlink(ww_s); - unlink(cs_e); unlink(ww_e); - continue; + rowfail = 1; + goto row_done; } if (!file_eq(cs_s, ww_s)) { fprintf(stderr, "FAIL row[%s]: cs != ww .s\n", r->label); - fail++; + rowfail = 1; } /* hidden-RDI dest: present iff the row is sret-class. * The boundary rows pin the 32B class stays register. */ @@ -569,33 +560,54 @@ main(void) if (r->sret && !has_di) { fprintf(stderr, "FAIL row[%s]: expected sret " "hidden-RDI LEAQ, none emitted\n", r->label); - fail++; + rowfail = 1; } if (!r->sret && has_di) { fprintf(stderr, "FAIL row[%s]: 32B-slot row " "flipped to sret (classifier boundary " "off-by-one)\n", r->label); - fail++; + rowfail = 1; } - int got_cs = run_driver("ww", src, r->label); + int got_cs = run_driver("ww", src, r->label, work); if (got_cs != r->want) { fprintf(stderr, "FAIL row[%s] cstage: want %d " "got %d\n", r->label, r->want, got_cs); - fail++; + rowfail = 1; } char wwdrv[600]; snprintf(wwdrv, sizeof wwdrv, "%s/ww_ww", g_bin); if (access(wwdrv, X_OK) == 0) { - int got_ww = run_driver("ww_ww", src, r->label); + int got_ww = run_driver("ww_ww", src, r->label, work); if (got_ww != r->want) { fprintf(stderr, "FAIL row[%s] wwstage: " "want %d got %d\n", r->label, r->want, got_ww); - fail++; + rowfail = 1; } } - unlink(src); unlink(cs_s); unlink(ww_s); - unlink(cs_e); unlink(ww_e); + + row_done: + { + int cleanfail = 0; + if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(cs_s) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(ww_s) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(cs_e) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(ww_e) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(cbin) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(wbin) != 0 && errno != ENOENT) cleanfail = 1; + snprintf(cmd, sizeof cmd, "rm -rf %s", cscratch); + if (runwait(cmd) != 0) cleanfail = 1; + snprintf(cmd, sizeof cmd, "rm -rf %s", wscratch); + if (runwait(cmd) != 0) cleanfail = 1; + if (rmdir(work) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "FAIL row[%s]: workspace cleanup failed\n", + r->label); + rowfail = 1; + } + } + if (rowfail) fail++; } if (fail) { fprintf(stderr, "tagged_sret_run: %d/%d rows failed\n", diff --git a/test/wcc/926_tagscr_sizes_run.c b/test/wcc/926_tagscr_sizes_run.c index 2276ac4f..35621bad 100644 --- a/test/wcc/926_tagscr_sizes_run.c +++ b/test/wcc/926_tagscr_sizes_run.c @@ -18,8 +18,9 @@ * the pre-#44 compiler when the fix landed), and the N_INDEX * tagged-element store site mixing with an sret return. * - * Per row: w6c vs w6c_ww byte-id (rule 10) + runtime via both the ww - * and ww_ww drivers. Payload checks stay within the 32B register + * Runtime behavior now lives in the r926_tagscr_* wwfixtures. This wrapper + * retains only w6c vs w6c_ww byte identity (rule 10). Payload checks stay + * within the 32B register * cursor — wide tagged ELEMENT reads past 32B truncate (the filed #43 * residual, pre-existing, out of #44's scope). */ @@ -190,22 +191,6 @@ file_eq(const char *a, const char *b) return runwait(cmd) == 0; } -static int -run_driver(const char *driver, const char *src, const char *tmpdir, - const char *label) -{ - char cmd[1024], outbin[256]; - snprintf(outbin, sizeof outbin, "%s/drvout", tmpdir); - snprintf(cmd, sizeof cmd, "%s/%s build -o %s %s >/dev/null 2>&1", - g_bin, driver, outbin, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: build via %s failed\n", - label, driver); - return -1; - } - return runwait(outbin); -} - int main(void) { @@ -227,7 +212,12 @@ main(void) char tmpdir[96], src[160], cs_s[160], ww_s[160], rmcmd[160]; snprintf(tmpdir, sizeof tmpdir, "/tmp/tagscr_%d_%d_d", getpid(), i); - mkdir(tmpdir, 0755); + /* an unowned path (stale dir, full /tmp) must not be compiled + * in — or rm -rf'd — below. */ + if (mkdir(tmpdir, 0755) != 0) { + perror(tmpdir); + return 1; + } snprintf(src, sizeof src, "%s/tagscr_%d_%d.ww", tmpdir, getpid(), i); snprintf(cs_s, sizeof cs_s, "%s/tagscr_%d_%d_cs.s", @@ -236,50 +226,45 @@ main(void) tmpdir, getpid(), i); snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return 1; } + if (!f) { + perror(src); + if (runwait(rmcmd) != 0) + fprintf(stderr, "tagscr_sizes_run: cleanup " + "%s failed\n", tmpdir); + return 1; + } fputs("package main;\n\n", f); fputs(r->src, f); fclose(f); total++; + int rowfail = 0; int cs_rc = compile_s("w6c", src, cs_s); int ww_rc = compile_s("w6c_ww", src, ww_s); if (cs_rc != 0 || ww_rc != 0) { fprintf(stderr, "FAIL row[%s]: compile rc cs=%d " "ww=%d\n", r->label, cs_rc, ww_rc); - fail++; - runwait(rmcmd); - continue; - } - if (!file_eq(cs_s, ww_s)) { + rowfail = 1; + } else if (!file_eq(cs_s, ww_s)) { fprintf(stderr, "FAIL row[%s]: cs != ww .s\n", r->label); + rowfail = 1; + } + /* a silent cleanup failure must fail an otherwise-passing + * row without masking its own diagnostic. */ + if (runwait(rmcmd) != 0) { + fprintf(stderr, "FAIL row[%s]: cleanup %s failed\n", + r->label, tmpdir); + rowfail = 1; + } + if (rowfail) fail++; - } - int got_cs = run_driver("ww", src, tmpdir, r->label); - if (got_cs != r->want) { - fprintf(stderr, "FAIL row[%s] cstage: want %d " - "got %d\n", r->label, r->want, got_cs); - fail++; - } - char wwdrv[600]; - snprintf(wwdrv, sizeof wwdrv, "%s/ww_ww", g_bin); - if (access(wwdrv, X_OK) == 0) { - int got_ww = run_driver("ww_ww", src, tmpdir, r->label); - if (got_ww != r->want) { - fprintf(stderr, "FAIL row[%s] wwstage: " - "want %d got %d\n", - r->label, r->want, got_ww); - fail++; - } - } - runwait(rmcmd); } if (fail) { fprintf(stderr, "tagscr_sizes_run: %d/%d rows failed\n", fail, total); return 1; } - printf("tagscr_sizes_run: %d rows ok\n", total); + printf("tagscr_sizes_run: %d assembly rows ok\n", total); return 0; } diff --git a/test/wcc/929_match_4arm_cross_module_run.c b/test/wcc/929_match_4arm_cross_module_run.c index 5fec8282..742ecb0c 100644 --- a/test/wcc/929_match_4arm_cross_module_run.c +++ b/test/wcc/929_match_4arm_cross_module_run.c @@ -19,6 +19,7 @@ */ #include #include +#include #include #include #include @@ -268,14 +269,36 @@ write_file(const char *dir, const char *name, const char *body) static int run_driver(const char *driver, const struct row *r, int i) { - char tmpdir[96], adir[128], cmd[2048]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/m4cmr_%d_%d", getpid(), i); + char tmpdir[128], adir[160], asrc[192], bsrc[192]; + char outbin[192], scratch[208], cmd[2048]; + snprintf(tmpdir, sizeof tmpdir, "/tmp/m4cmr_%d_%d_XXXXXX", + getpid(), i); + if (mkdtemp(tmpdir) == NULL) { + fprintf(stderr, "row[%s]: temporary directory acquisition failed\n", + r->label); + return -1; + } snprintf(adir, sizeof adir, "%s/a", tmpdir); - mkdir(tmpdir, 0755); - mkdir(adir, 0755); + snprintf(asrc, sizeof asrc, "%s/a.ww", adir); + snprintf(bsrc, sizeof bsrc, "%s/b.ww", tmpdir); + snprintf(outbin, sizeof outbin, "%s/b", tmpdir); + snprintf(scratch, sizeof scratch, "%s/b.sepwork", tmpdir); - if (write_file(adir, "a.ww", r->a_src) != 0) return -1; - if (write_file(tmpdir, "b.ww", r->b_src) != 0) return -1; + int result = -1, adir_owned = 0; + if (mkdir(adir, 0755) != 0) { + fprintf(stderr, "row[%s]: module directory creation failed\n", + r->label); + goto cleanup; + } + adir_owned = 1; + if (write_file(adir, "a.ww", r->a_src) != 0) { + fprintf(stderr, "row[%s]: cannot write a/a.ww\n", r->label); + goto cleanup; + } + if (write_file(tmpdir, "b.ww", r->b_src) != 0) { + fprintf(stderr, "row[%s]: cannot write b.ww\n", r->label); + goto cleanup; + } /* Build via the driver from inside tmpdir so `use a;` resolves to * ./a/a.ww and ww's source-dir search hits b's siblings first. */ @@ -284,18 +307,30 @@ run_driver(const char *driver, const struct row *r, int i) if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: build via %s failed\n", r->label, driver); - snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir); - runwait(cmd); - return -1; + goto cleanup; } - char outbin[160]; - snprintf(outbin, sizeof outbin, "%s/b", tmpdir); - int got = runwait(outbin); + result = runwait(outbin); - snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir); - runwait(cmd); - return got; +cleanup: + { + int bad = 0; + snprintf(cmd, sizeof cmd, "rm -rf %s", scratch); + if (runwait(cmd) != 0) bad = 1; + if (unlink(outbin) != 0 && errno != ENOENT) bad = 1; + if (unlink(bsrc) != 0 && errno != ENOENT) bad = 1; + if (adir_owned) { + if (unlink(asrc) != 0 && errno != ENOENT) bad = 1; + if (rmdir(adir) != 0) bad = 1; + } + if (rmdir(tmpdir) != 0) bad = 1; + if (bad) { + fprintf(stderr, "row[%s]: temporary cleanup failed\n", + r->label); + if (result == r->want) result = -1; + } + } + return result; } int diff --git a/test/wcc/929_tagged_memarg_run.c b/test/wcc/929_tagged_memarg_run.c index 9dda5d76..588101de 100644 --- a/test/wcc/929_tagged_memarg_run.c +++ b/test/wcc/929_tagged_memarg_run.c @@ -26,17 +26,15 @@ * - runtime: build via ww / ww_ww and run; exit codes read the tag * AND the late payload words (slot offsets +48/+56, past the old * register cap) FIRST so their loss is the visible failure. - * Rows flagged `buildfail` must be rejected by BOTH stages with that - * row's EXACT #38b diagnostic — pinning WHICH guard fired (the - * remaining unwired sub-shapes: sret-class call source, global-let - * source, exact-type rvalue, variadic element, register-overflow - * mixing caller- and callee-side). #40/FB3 wired the place-resolved + * Rows flagged `buildfail` are source carriers only; their exact symmetric + * diagnostics moved to r929_memarg_reject_* wwfixtures. #40/FB3 wired the place-resolved * sources (slice element, deref-spine, nested index) via the * cgplaceaddr fallback — those are run rows now. */ #include #include #include +#include #include #include #include @@ -623,24 +621,18 @@ file_has(const char *path, const char *needle) } static int -run_driver(const char *driver, const char *src, const char *label) +run_driver(const char *driver, const char *src, const char *outbin, + const char *label) { - char tmpdir[128], outbin[160], rmcmd[200], cmd[1024]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/tmemarg_%d_d", getpid()); - mkdir(tmpdir, 0755); - snprintf(outbin, sizeof outbin, "%s/m", tmpdir); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + char cmd[1024]; snprintf(cmd, sizeof cmd, "%s/%s build -o %s %s >/dev/null 2>&1", g_bin, driver, outbin, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: build via %s failed\n", label, driver); - runwait(rmcmd); return -1; } - int got = runwait(outbin); - runwait(rmcmd); - return got; + return runwait(outbin); } int @@ -661,23 +653,40 @@ main(void) int total = 0, fail = 0; for (int i = 0; i < n; i++) { const struct row *r = &rows[i]; - char src[128], cs_s[128], ww_s[128]; - char cs_e[128], ww_e[128]; - snprintf(src, sizeof src, "/tmp/tmemarg_%d_%d.ww", - getpid(), i); - snprintf(cs_s, sizeof cs_s, "/tmp/tmemarg_%d_%d_cs.s", - getpid(), i); - snprintf(ww_s, sizeof ww_s, "/tmp/tmemarg_%d_%d_ww.s", - getpid(), i); - snprintf(cs_e, sizeof cs_e, "/tmp/tmemarg_%d_%d_cs.err", - getpid(), i); - snprintf(ww_e, sizeof ww_e, "/tmp/tmemarg_%d_%d_ww.err", + char tmpdir[128]; + snprintf(tmpdir, sizeof tmpdir, "/tmp/tmemarg_%d_%d_XXXXXX", getpid(), i); + if (mkdtemp(tmpdir) == NULL) { + fprintf(stderr, "FAIL row[%s]: temporary directory " + "acquisition failed\n", r->label); + fail++; + continue; + } + + char src[192], cs_s[192], ww_s[192], cs_e[192], ww_e[192]; + char cs_bin[192], ww_bin[192], cs_work[208], ww_work[208]; + snprintf(src, sizeof src, "%s/case.ww", tmpdir); + snprintf(cs_s, sizeof cs_s, "%s/cs.s", tmpdir); + snprintf(ww_s, sizeof ww_s, "%s/ww.s", tmpdir); + snprintf(cs_e, sizeof cs_e, "%s/cs.err", tmpdir); + snprintf(ww_e, sizeof ww_e, "%s/ww.err", tmpdir); + snprintf(cs_bin, sizeof cs_bin, "%s/run_c", tmpdir); + snprintf(ww_bin, sizeof ww_bin, "%s/run_w", tmpdir); + snprintf(cs_work, sizeof cs_work, "%s.sepwork", cs_bin); + snprintf(ww_work, sizeof ww_work, "%s.sepwork", ww_bin); + FILE *f = fopen(src, "wb"); - if (!f) return 1; + if (!f) { + fprintf(stderr, "FAIL row[%s]: source setup failed\n", + r->label); + fail++; + goto row_cleanup; + } fputs("package main;\n\n", f); fputs(r->src, f); fclose(f); + /* Loud-reject behavior is owned by r929_memarg_reject_* fixtures. */ + if (r->buildfail) goto row_cleanup; int cs_rc = compile_s("w6c", src, cs_s, cs_e); int ww_rc = compile_s("w6c_ww", src, ww_s, ww_e); @@ -699,18 +708,14 @@ main(void) r->label, r->failmark); fail++; } - unlink(src); unlink(cs_s); unlink(ww_s); - unlink(cs_e); unlink(ww_e); - continue; + goto row_cleanup; } total++; if (cs_rc != 0 || ww_rc != 0) { fprintf(stderr, "FAIL row[%s]: compile rc cs=%d " "ww=%d\n", r->label, cs_rc, ww_rc); fail++; - unlink(src); unlink(cs_s); unlink(ww_s); - unlink(cs_e); unlink(ww_e); - continue; + goto row_cleanup; } if (!file_eq(cs_s, ww_s)) { fprintf(stderr, "FAIL row[%s]: cs != ww .s\n", @@ -728,7 +733,7 @@ main(void) r->label); fail++; } - int got_cs = run_driver("ww", src, r->label); + int got_cs = run_driver("ww", src, cs_bin, r->label); if (got_cs != r->want) { fprintf(stderr, "FAIL row[%s] cstage: want %d " "got %d\n", r->label, r->want, got_cs); @@ -737,7 +742,7 @@ main(void) char wwdrv[600]; snprintf(wwdrv, sizeof wwdrv, "%s/ww_ww", g_bin); if (access(wwdrv, X_OK) == 0) { - int got_ww = run_driver("ww_ww", src, r->label); + int got_ww = run_driver("ww_ww", src, ww_bin, r->label); if (got_ww != r->want) { fprintf(stderr, "FAIL row[%s] wwstage: " "want %d got %d\n", @@ -745,8 +750,29 @@ main(void) fail++; } } - unlink(src); unlink(cs_s); unlink(ww_s); - unlink(cs_e); unlink(ww_e); + + row_cleanup: + { + int bad = 0; + char rmcmd[256]; + snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", cs_work); + if (runwait(rmcmd) != 0) bad = 1; + snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", ww_work); + if (runwait(rmcmd) != 0) bad = 1; + if (unlink(cs_bin) != 0 && errno != ENOENT) bad = 1; + if (unlink(ww_bin) != 0 && errno != ENOENT) bad = 1; + if (unlink(src) != 0 && errno != ENOENT) bad = 1; + if (unlink(cs_s) != 0 && errno != ENOENT) bad = 1; + if (unlink(ww_s) != 0 && errno != ENOENT) bad = 1; + if (unlink(cs_e) != 0 && errno != ENOENT) bad = 1; + if (unlink(ww_e) != 0 && errno != ENOENT) bad = 1; + if (rmdir(tmpdir) != 0) bad = 1; + if (bad) { + fprintf(stderr, "FAIL row[%s]: temporary cleanup failed\n", + r->label); + fail++; + } + } } if (fail) { fprintf(stderr, "tagged_memarg_run: %d/%d rows failed\n", diff --git a/test/wcc/930_free_noop_run.c b/test/wcc/930_free_noop_run.c index c52d93ee..a4b16794 100644 --- a/test/wcc/930_free_noop_run.c +++ b/test/wcc/930_free_noop_run.c @@ -25,10 +25,9 @@ * @symbol("rt_free") decl — covered by the lisp example / stdlib * suites, not re-pinned here. * - * Per row: w6c vs w6c_ww .s byte-id (rule 10), a negative grep that - * no `free` symbol survives anywhere in the .s (byte-id alone would - * pass if BOTH stages still emitted CALL free), and runtime via both - * drivers. + * Runtime behavior is carried by the r930_free_* wwfixtures. This wrapper + * retains w6c vs w6c_ww byte identity plus the negative assembly assertion + * that no `free` symbol survives. */ #include #include @@ -154,22 +153,6 @@ has_free_sym(const char *s_path) return runwait(cmd) == 0; } -static int -run_driver(const char *driver, const char *src, const char *tmpdir, - const char *label) -{ - char cmd[1024], outbin[256]; - snprintf(outbin, sizeof outbin, "%s/%s_out", tmpdir, driver); - snprintf(cmd, sizeof cmd, "%s/%s build -o %s %s >/dev/null 2>&1", - g_bin, driver, outbin, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: build via %s failed\n", - label, driver); - return -1; - } - return runwait(outbin); -} - int main(void) { @@ -192,66 +175,69 @@ main(void) * byte-id .s dumps all live under one tmpdir; rm -rf on every * exit path. Symbol mangling is package/import-derived, not * entry-path derived, so the in-tmpdir source keeps the cs==ww - * .s identical. */ + * .s identical. Temp names must avoid the substring 'free': + * has_free_sym greps the whole .s, so an embedded path would + * false-fail every row. */ char tmpdir[64], src[128], cs_s[128], ww_s[128], rmcmd[160]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/freenoop_%d_d_%d", + snprintf(tmpdir, sizeof tmpdir, "/tmp/noopfr_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/freenoop_%d_%d.ww", + /* an unowned path (stale dir, full /tmp) must not be compiled + * in — or rm -rf'd — below. */ + if (mkdir(tmpdir, 0755) != 0) { + perror(tmpdir); + return 1; + } + snprintf(src, sizeof src, "%s/noopfr_%d_%d.ww", tmpdir, getpid(), i); snprintf(cs_s, sizeof cs_s, "%s/cs.s", tmpdir); snprintf(ww_s, sizeof ww_s, "%s/ww.s", tmpdir); snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return 1; } + if (!f) { + perror(src); + if (runwait(rmcmd) != 0) + fprintf(stderr, "free_noop_run: cleanup " + "%s failed\n", tmpdir); + return 1; + } fputs("package main;\n\n", f); fputs(r->src, f); fclose(f); total++; + int rowfail = 0; int cs_rc = compile_s("w6c", src, cs_s); int ww_rc = compile_s("w6c_ww", src, ww_s); if (cs_rc != 0 || ww_rc != 0) { fprintf(stderr, "FAIL row[%s]: compile rc cs=%d " "ww=%d\n", r->label, cs_rc, ww_rc); - fail++; - runwait(rmcmd); - continue; - } - if (!file_eq(cs_s, ww_s)) { - fprintf(stderr, "FAIL row[%s]: cs != ww .s\n", - r->label); - fail++; - } - if (has_free_sym(cs_s)) { - fprintf(stderr, "FAIL row[%s]: 'free' survives in " - "the .s — lowering is not a no-op\n", r->label); - fail++; - } - int got_cs = run_driver("ww", src, tmpdir, r->label); - if (got_cs != r->want) { - fprintf(stderr, "FAIL row[%s] cstage: want %d " - "got %d\n", r->label, r->want, got_cs); - fail++; - } - char wwdrv[600]; - snprintf(wwdrv, sizeof wwdrv, "%s/ww_ww", g_bin); - if (access(wwdrv, X_OK) == 0) { - int got_ww = run_driver("ww_ww", src, tmpdir, r->label); - if (got_ww != r->want) { - fprintf(stderr, "FAIL row[%s] wwstage: " - "want %d got %d\n", - r->label, r->want, got_ww); - fail++; + fail++; rowfail = 1; + } else { + if (!file_eq(cs_s, ww_s)) { + fprintf(stderr, "FAIL row[%s]: cs != ww .s\n", + r->label); + fail++; rowfail = 1; + } + if (has_free_sym(cs_s)) { + fprintf(stderr, "FAIL row[%s]: 'free' survives in " + "the .s — lowering is not a no-op\n", r->label); + fail++; rowfail = 1; } } - runwait(rmcmd); + /* a silent cleanup failure must fail an otherwise-passing + * row without masking its own diagnostic. */ + if (runwait(rmcmd) != 0) { + fprintf(stderr, "FAIL row[%s]: cleanup %s failed\n", + r->label, tmpdir); + if (!rowfail) + fail++; + } } if (fail) { fprintf(stderr, "free_noop_run: %d/%d rows failed\n", fail, total); return 1; } - printf("free_noop_run: %d rows ok\n", total); + printf("free_noop_run: %d assembly rows ok\n", total); return 0; } diff --git a/test/wcc/940_global_sret_run.c b/test/wcc/940_global_sret_run.c index 970443fe..4bd6124f 100644 --- a/test/wcc/940_global_sret_run.c +++ b/test/wcc/940_global_sret_run.c @@ -19,12 +19,13 @@ * already used (LEAQ off(BP), DI). Local rows pin no regression at the * shared cgassign / cglet sret-receive sites. * - * Each row runs on BOTH cstage (ww) and wwstage (ww_ww), and the - * generated asm is checked byte-identical (cs.s == ww.s, rule 10). + * Runtime behavior is carried by the r940_global_sret_* wwfixtures. This + * wrapper retains the separate-build assembly artifacts and their byte + * identity (cs.s == ww.s, rule 10). */ #include #include -#include +#include #include #include #include @@ -144,49 +145,62 @@ static int byteid(const char *dir, int i) { char cs[256], ws[256], cmd[1024]; + int rc; snprintf(cs, sizeof cs, "%s/bid_cs_%d.s", dir, i); snprintf(ws, sizeof ws, "%s/bid_ww_%d.s", dir, i); + /* an unmatched sepwork glob must be a loud harness error — two + * empty concatenations would otherwise cmp equal (vacuous green). */ snprintf(cmd, sizeof cmd, "cat %s/gsret_c_%d.sepwork/*.s > %s 2>/dev/null", dir, i, cs); - if (system(cmd)) {} + if (runwait(cmd) != 0) { + rc = -1; + goto cleanup; + } snprintf(cmd, sizeof cmd, "cat %s/gsret_w_%d.sepwork/*.s > %s 2>/dev/null", dir, i, ws); - if (system(cmd)) {} + if (runwait(cmd) != 0) { + rc = -1; + goto cleanup; + } snprintf(cmd, sizeof cmd, "cmp -s %s %s", cs, ws); - int rc = runwait(cmd); - unlink(cs); unlink(ws); - return rc == 0 ? 0 : 1; + rc = runwait(cmd) == 0 ? 0 : 1; +cleanup: + if (unlink(cs) != 0 && errno != ENOENT) { perror(cs); if (rc == 0) rc = -1; } + if (unlink(ws) != 0 && errno != ENOENT) { perror(ws); if (rc == 0) rc = -1; } + return rc; } -/* Build `r` with `driver` via `--sep -o /` (per-package asm in - * /.sepwork/), run the binary, return its exit code (or -1 on - * build failure). `cachetag` keys a private WW_PKGCACHE. */ +/* Build `r` with `driver -o /` (per-package asm in + * /.sepwork/), returning zero on build success. */ static int run_driver(const char *driver, const struct row *r, const char *dir, - int i, const char *stem, const char *cachetag) + int i, const char *stem) { - char src[256], cmd[1024]; + char src[256], taggedstem[128], cmd[1024]; snprintf(src, sizeof src, "%s/gsret_%d.ww", dir, i); + snprintf(taggedstem, sizeof taggedstem, "%s_%d", stem, i); FILE *f = fopen(src, "wb"); if (!f) return -1; wwtest_fputs(r->src, f); - fclose(f); + int werr = ferror(f); + if (fclose(f) != 0 || werr) { + fprintf(stderr, "row[%s]: writing %s failed\n", r->label, src); + return -1; + } snprintf(cmd, sizeof cmd, - "cd %s && WW_PKGCACHE=%s/pkgc_%s %s build --sep -o %s/%s %s", - dir, dir, cachetag, driver, dir, stem, src); + "cd %s && %s build -S -o %s/%s %s", + dir, driver, dir, taggedstem, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: build via %s failed\n", r->label, driver); return -1; } - char outbin[256]; - snprintf(outbin, sizeof outbin, "%s/%s", dir, stem); - return runwait(outbin); + return 0; } int @@ -217,26 +231,26 @@ main(void) } int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; + int total = 0, fail = 0, cleanfail = 0; for (int i = 0; i < n; i++) { - /* cstage: --sep build + run → /gsret_c_.sepwork/. */ - int gc = run_driver(cdrv, &rows[i], dir, i, "gsret_c", "c"); + /* cstage separate build → /gsret_c_.sepwork/. */ + int gc = run_driver(cdrv, &rows[i], dir, i, "gsret_c"); total++; if (gc != rows[i].want) { fprintf(stderr, "global_sret_run[cstage][%s]: " - "exit=%d want=%d\n", rows[i].label, gc, + "build=%d want=%d\n", rows[i].label, gc, rows[i].want); fail++; } - /* wwstage: --sep build + run → /gsret_w_.sepwork/. */ + /* wwstage separate build → /gsret_w_.sepwork/. */ if (have_ww) { - int gw = run_driver(wdrv, &rows[i], dir, i, "gsret_w", "w"); + int gw = run_driver(wdrv, &rows[i], dir, i, "gsret_w"); total++; if (gw != rows[i].want) { fprintf(stderr, "global_sret_run[wwstage][%s]: " - "exit=%d want=%d\n", rows[i].label, gw, + "build=%d want=%d\n", rows[i].label, gw, rows[i].want); fail++; } @@ -257,14 +271,25 @@ main(void) } char p[640]; - snprintf(p, sizeof p, "%s/gsret_%d.ww", dir, i); unlink(p); + snprintf(p, sizeof p, "%s/gsret_%d.ww", dir, i); + /* ENOENT = the row failed before writing the source. */ + if (unlink(p) != 0 && errno != ENOENT) { + perror(p); + cleanfail = 1; + } snprintf(p, sizeof p, "rm -rf %s/gsret_c_%d.sepwork " - "%s/gsret_w_%d.sepwork %s/gsret_c_%d %s/gsret_w_%d " - "%s/pkgc_c %s/pkgc_w", dir, i, dir, i, dir, i, dir, i, - dir, dir); - if (system(p)) {} + "%s/gsret_w_%d.sepwork %s/gsret_c_%d %s/gsret_w_%d", + dir, i, dir, i, dir, i, dir, i); + if (runwait(p) != 0) { + fprintf(stderr, "global_sret_run: cleanup row %d " + "failed\n", i); + cleanfail = 1; + } + } + if (rmdir(dir) != 0) { + perror(dir); + cleanfail = 1; } - rmdir(dir); if (!have_ww) fprintf(stderr, "global_sret_run: skip wwstage (no %s)\n", wdrv); @@ -274,6 +299,9 @@ main(void) fail, total); return 1; } + /* cleanup diagnostics already printed; must not stay green. */ + if (cleanfail) + return 1; printf("global_sret_run: %d/%d ok\n", total, total); return 0; } diff --git a/test/wcc/944_alias_accept_run.c b/test/wcc/944_alias_accept_run.c index 4acfbd3b..324407f6 100644 --- a/test/wcc/944_alias_accept_run.c +++ b/test/wcc/944_alias_accept_run.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -49,21 +50,30 @@ static int run_row(const char *bin, const char *label, const char *src, const char *wwerr, int i) { - char dir[96], srcf[160], outbin[160], wwe[160], ss[160], rm[200], cmd[2048]; - snprintf(dir, sizeof dir, "/tmp/aacc_%d_%d", getpid(), i); - mkdir(dir, 0755); + char dir[128], srcf[192], outbin[192], wwe[192], ss[192]; + char scratch[208], cmd[2048]; + snprintf(dir, sizeof dir, "/tmp/aacc_%d_%d_XXXXXX", getpid(), i); + if (mkdtemp(dir) == NULL) { + fprintf(stderr, "row[%s]: temporary directory acquisition failed\n", + label); + return -1; + } snprintf(srcf, sizeof srcf, "%s/c.ww", dir); snprintf(outbin, sizeof outbin, "%s/bin", dir); snprintf(wwe, sizeof wwe, "%s/ww.err", dir); snprintf(ss, sizeof ss, "%s/o.s", dir); - snprintf(rm, sizeof rm, "rm -rf %s", dir); + snprintf(scratch, sizeof scratch, "%s.sepwork", outbin); FILE *f = fopen(srcf, "wb"); - if (!f) { runwait(rm); return -1; } + int ok = 1; + if (!f) { + fprintf(stderr, "row[%s]: source setup failed\n", label); + ok = 0; + goto cleanup; + } fputs(src, f); fclose(f); - int ok = 1; snprintf(cmd, sizeof cmd, "timeout 20 %s/ww build -o %s %s >/dev/null 2>&1", bin, outbin, srcf); if (runwait(cmd) != 0) { @@ -80,7 +90,22 @@ run_row(const char *bin, const char *label, const char *src, label, wwerr, wrc); ok = 0; } - runwait(rm); + +cleanup: + { + int bad = 0; + snprintf(cmd, sizeof cmd, "rm -rf %s", scratch); + if (runwait(cmd) != 0) bad = 1; + if (unlink(ss) != 0 && errno != ENOENT) bad = 1; + if (unlink(wwe) != 0 && errno != ENOENT) bad = 1; + if (unlink(outbin) != 0 && errno != ENOENT) bad = 1; + if (unlink(srcf) != 0 && errno != ENOENT) bad = 1; + if (rmdir(dir) != 0) bad = 1; + if (bad) { + fprintf(stderr, "row[%s]: temporary cleanup failed\n", label); + ok = 0; + } + } return ok ? 0 : 1; } diff --git a/test/wcc/944_alias_cgen_b5_run.c b/test/wcc/944_alias_cgen_b5_run.c index 17f34639..dbc135c4 100644 --- a/test/wcc/944_alias_cgen_b5_run.c +++ b/test/wcc/944_alias_cgen_b5_run.c @@ -14,6 +14,7 @@ */ #include #include +#include #include #include #include @@ -31,19 +32,26 @@ runwait(const char *cmd) static int run_row(const char *bin, const char *label, const char *src, int i) { - char dir[96], srcf[160], ss[160], rm[200], cmd[2048]; - snprintf(dir, sizeof dir, "/tmp/ab5_%d_%d", getpid(), i); - mkdir(dir, 0755); + char dir[128], srcf[192], ss[192], cmd[2048]; + snprintf(dir, sizeof dir, "/tmp/ab5_%d_%d_XXXXXX", getpid(), i); + if (mkdtemp(dir) == NULL) { + fprintf(stderr, "row[%s]: temporary directory acquisition failed\n", + label); + return -1; + } snprintf(srcf, sizeof srcf, "%s/c.ww", dir); snprintf(ss, sizeof ss, "%s/o.s", dir); - snprintf(rm, sizeof rm, "rm -rf %s", dir); FILE *f = fopen(srcf, "wb"); - if (!f) { runwait(rm); return -1; } + int ok = 1; + if (!f) { + fprintf(stderr, "row[%s]: source setup failed\n", label); + ok = 0; + goto cleanup; + } fputs(src, f); fclose(f); - int ok = 1; snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, ss, srcf); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: cstage frontend compile failed\n", label); @@ -54,7 +62,18 @@ run_row(const char *bin, const char *label, const char *src, int i) fprintf(stderr, "row[%s]: wwstage frontend compile failed\n", label); ok = 0; } - runwait(rm); + +cleanup: + { + int bad = 0; + if (unlink(ss) != 0 && errno != ENOENT) bad = 1; + if (unlink(srcf) != 0 && errno != ENOENT) bad = 1; + if (rmdir(dir) != 0) bad = 1; + if (bad) { + fprintf(stderr, "row[%s]: temporary cleanup failed\n", label); + ok = 0; + } + } return ok ? 0 : 1; } diff --git a/test/wcc/944_variant_chain_b95_run.c b/test/wcc/944_variant_chain_b95_run.c index b9189147..9767e6c0 100644 --- a/test/wcc/944_variant_chain_b95_run.c +++ b/test/wcc/944_variant_chain_b95_run.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -73,16 +74,28 @@ static const char *experr = "deferred #277"; static int check(const char *driver, int expect_err, int want) { - char tmpdir[96], srcp[128], outbin[128], errf[128], rmcmd[160], cmd[1024]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/vc277_%d_%d", getpid(), expect_err); - mkdir(tmpdir, 0755); + char tmpdir[128], srcp[192], outbin[192], errf[192]; + char scratch[208], cmd[1024]; + snprintf(tmpdir, sizeof tmpdir, "/tmp/vc277_%d_%d_XXXXXX", + getpid(), expect_err); + if (mkdtemp(tmpdir) == NULL) { + fprintf(stderr, "callret_bound277: %s temporary directory " + "acquisition failed\n", driver); + return 1; + } snprintf(srcp, sizeof srcp, "%s/c.ww", tmpdir); snprintf(outbin, sizeof outbin, "%s/c", tmpdir); snprintf(errf, sizeof errf, "%s/err", tmpdir); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + snprintf(scratch, sizeof scratch, "%s.sepwork", outbin); FILE *f = fopen(srcp, "wb"); - if (!f) { runwait(rmcmd); return 1; } + int rc = 0; + if (!f) { + fprintf(stderr, "callret_bound277: %s source setup failed\n", + driver); + rc = 1; + goto cleanup; + } fputs(src, f); fclose(f); @@ -90,7 +103,6 @@ check(const char *driver, int expect_err, int want) driver, outbin, srcp, errf); int brc = runwait(cmd); - int rc = 0; if (expect_err) { if (brc == 0 || !errlog_has(errf, experr)) { fprintf(stderr, "callret_bound277: %s expected loud " @@ -108,7 +120,22 @@ check(const char *driver, int expect_err, int want) rc = 1; } } - runwait(rmcmd); + +cleanup: + { + int bad = 0; + snprintf(cmd, sizeof cmd, "rm -rf %s", scratch); + if (runwait(cmd) != 0) bad = 1; + if (unlink(errf) != 0 && errno != ENOENT) bad = 1; + if (unlink(outbin) != 0 && errno != ENOENT) bad = 1; + if (unlink(srcp) != 0 && errno != ENOENT) bad = 1; + if (rmdir(tmpdir) != 0) bad = 1; + if (bad) { + fprintf(stderr, "callret_bound277: %s temporary cleanup " + "failed\n", driver); + rc = 1; + } + } return rc; } diff --git a/test/wcc/946_structparam_run.c b/test/wcc/946_structparam_run.c index dbb893e5..3ae36f70 100644 --- a/test/wcc/946_structparam_run.c +++ b/test/wcc/946_structparam_run.c @@ -1,5 +1,5 @@ /* - * 946_structparam_run — runtime + byte-id + asm-pattern net for #165, the + * 946_structparam_run — byte-id + asm-pattern net for #165, the * float-bearing struct-PARAM SysV ABI (the param twin of #171's struct * RETURN, and the struct counterpart of #163's per-element tuple PARAM). * @@ -14,7 +14,7 @@ * NECESSARY-NOT-SUFFICIENT here (cf. 907_f32arg_run, same situation). The * genuine defect is SysV register-CLASS conformance, observable only in * the emitted asm (and at a real ABI boundary). The discriminating - * dimension is therefore the asm pattern (c) below. + * dimension is therefore the asm pattern below. * * THE FIX: per-EIGHTBYTE SysV classification (structs classify per * eightbyte, unlike #163's per-element tuples). Each 8-byte eightbyte that @@ -33,17 +33,17 @@ * f32f32 row asserts BOTH the correct round-tripped value AND the ABSENCE * of an SSE receive, proving the gate caught it. * - * Each row carries THREE dimensions: - * (a) cstage `ww build` + run — exit code (end-to-end round-trip). - * (b) w6c vs w6c_ww `.s` cmp — rule-10 byte-id (both stages identical). - * (c) asm-pattern on the CALLEE function's receive: a lone-f64 eightbyte + * The end-to-end values now live in r946_structparam_* fixtures. This carrier + * retains the two observations unavailable to the fixture grammar: + * (a) w6c vs w6c_ww `.s` cmp — rule-10 byte-id (both stages identical). + * (b) asm-pattern on the CALLEE function's receive: a lone-f64 eightbyte * is received `MOVSD Xn, -off(BP)` (XMM -> slot); a GP eightbyte is * `MOVQ DI, ...`. The marker is scoped to the callee `main.use` text * (main's float-literal init also emits `MOVSD X0, -off(BP)`, so a * whole-file grep would not discriminate). PRESENT for the SSE-routed * rows, ABSENT for the GP / fallback rows. A pre-fix (or GP-regressed) * build receives every struct eightbyte via MOVQ -> marker absent, - * which is exactly what (c) catches. + * which is exactly what the pattern check catches. */ #include #include @@ -64,7 +64,6 @@ runwait(const char *cmd) struct row { const char *label; const char *src; - int want_exit; int want_sse; /* 1 = callee must receive an f64 eightbyte via XMM */ }; @@ -79,7 +78,7 @@ static const struct row rows[] = { "\tlet s: pff = pff { a = 3.0, b = 5.0 };\n" "\tif (use(s) != 8.0) { return 1; };\n" "\treturn 0;\n" - "};\n", 0, 1 }, + "};\n", 1 }, /* struct{f64,i64} — eb0 lone f64 (X0), eb1 pure-INT (DI). Class is * independent of eightbyte position. (s.a:i64)+s.b = 3+5 = 8. */ { "f64i64_arg", @@ -90,7 +89,7 @@ static const struct row rows[] = { "\tlet s: pfi = pfi { a = 3.0, b = 5 };\n" "\tif (use(s) != 8) { return 1; };\n" "\treturn 0;\n" - "};\n", 0, 1 }, + "};\n", 1 }, /* struct{i64,f64} — order-swap: eb0 pure-INT (DI), eb1 lone f64 (X0, * the first float still gets X0). Confirms the float lands in the next * XMM regardless of position. s.a + (s.b:i64) = 3+5 = 8. */ @@ -102,7 +101,7 @@ static const struct row rows[] = { "\tlet s: pif = pif { a = 3, b = 5.0 };\n" "\tif (use(s) != 8) { return 1; };\n" "\treturn 0;\n" - "};\n", 0, 1 }, + "};\n", 1 }, /* GP REGRESSION — struct{i64,i64}: every eightbyte pure-INT, no float * to route, so it STAYS on the GP transport unchanged (the in-tree * byte-id case). Callee receives via MOVQ DI/SI -> no SSE marker. */ @@ -114,7 +113,7 @@ static const struct row rows[] = { "\tlet s: pii = pii { a = 3, b = 5 };\n" "\tif (use(s) != 8) { return 1; };\n" "\treturn 0;\n" - "};\n", 0, 0 }, + "};\n", 0 }, /* FALLBACK GATE — struct{f32,f32}: two f32 pack into ONE SSE eightbyte * (8B struct). The gate rejects f32, so the struct stays GP-routed * (received via MOVQ DI). The value still round-trips (the 8B MOVQ @@ -129,7 +128,7 @@ static const struct row rows[] = { "\tlet s: pf32 = pf32 { a = 3.0f32, b = 5.0f32 };\n" "\tif (use(s) != 8) { return 1; };\n" "\treturn 0;\n" - "};\n", 0, 0 }, + "};\n", 0 }, /* CURSOR INDEPENDENCE — i64 scalar + struct{f64,f64}. n->DI (INTEGER * cursor), s.a->X0, s.b->X1 (SSE cursor), independent counters. * n + s.a + s.b = 2+3+5 = 10. SSE receive present. */ @@ -143,8 +142,8 @@ static const struct row rows[] = { "\tlet s: pff = pff { a = 3.0, b = 5.0 };\n" "\tif (use(2, s) != 10) { return 1; };\n" "\treturn 0;\n" - "};\n", 0, 1 }, - { NULL, NULL, 0, 0 } + "};\n", 1 }, + { NULL, NULL, 0 } }; static int @@ -177,6 +176,12 @@ callee_receives_sse(const char *path) if (!f) return -1; static char buf[1 << 18]; size_t n = fread(buf, 1, sizeof buf - 1, f); + /* a .s larger than the buffer would silently drop the region the + * marker scan needs — refuse instead of scanning a truncated tail. */ + if (n == sizeof buf - 1 && fgetc(f) != EOF) { + fclose(f); + return -1; + } fclose(f); buf[n] = '\0'; @@ -213,18 +218,21 @@ main(void) int n = 0, fail = 0; for (int i = 0; rows[i].src; i++, n++) { - /* src, binary, and both .s files all live under one tmpdir so + /* The source and both .s files live under one tmpdir so * the compiler's path-derived .sepwork scratch stays inside it; * a single `rm -rf` at the end of all phases reclaims it. */ - char tmpdir[64], src[128], outbin[128]; + char tmpdir[64], src[128]; char cs_s[128], ws_s[128], rmcmd[160]; snprintf(tmpdir, sizeof tmpdir, "/tmp/wwstp_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); + if (mkdir(tmpdir, 0755) != 0) { + /* not owned — must not rm a path we failed to create */ + perror(tmpdir); + fail++; + continue; + } snprintf(src, sizeof src, "%s/wwstp_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/wwstp_%d_%d", - tmpdir, getpid(), i); snprintf(cs_s, sizeof cs_s, "%s/wwstp_%d_%d_cs.s", tmpdir, getpid(), i); snprintf(ws_s, sizeof ws_s, "%s/wwstp_%d_%d_ww.s", @@ -232,43 +240,25 @@ main(void) snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); FILE *f = fopen(src, "wb"); - if (f == NULL) { runwait(rmcmd); fail++; continue; } + if (f == NULL) { fail++; goto rowdone; } fputs(rows[i].src, f); fclose(f); char cmd[4096]; - /* (a) cstage build + run. */ - snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s", - bin, outbin, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: cstage build failed\n", - rows[i].label); - fail++; - runwait(rmcmd); - continue; - } - - int got = runwait(outbin); - if (got != rows[i].want_exit) { - fprintf(stderr, "row[%s]: cstage exit %d, want %d\n", - rows[i].label, got, rows[i].want_exit); - fail++; - } - - /* (b) cs==ww byte-id gate: emit .s from both stages, cmp. */ + /* cs==ww byte-id gate: emit .s from both stages, cmp. */ snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, cs_s, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label); - fail++; runwait(rmcmd); continue; + fail++; goto rowdone; } snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c_ww, ws_s, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c_ww failed\n", rows[i].label); - fail++; runwait(rmcmd); continue; + fail++; goto rowdone; } if (slurp_eq(cs_s, ws_s) != 0) { fprintf(stderr, @@ -277,8 +267,7 @@ main(void) fail++; } - /* (c) asm-pattern discriminator on the callee receive. byte-id - * (b) proves ww_s mirrors cs_s, so checking cs_s suffices. */ + /* byte-id proves ww_s mirrors cs_s, so checking cs_s suffices. */ int sse = callee_receives_sse(cs_s); if (sse < 0) { fprintf(stderr, "row[%s]: cannot scan callee .s\n", @@ -292,14 +281,20 @@ main(void) rows[i].want_sse ? "present" : "absent"); fail++; } - runwait(rmcmd); +rowdone: + /* a failed rm must fail the carrier, not leak silently. */ + if (runwait(rmcmd) != 0) { + fprintf(stderr, "row[%s]: cleanup rm failed\n", + rows[i].label); + fail++; + } } if (fail) { fprintf(stderr, "%d/%d struct-param tests failed\n", fail, n); return 1; } - printf("structparam: %d/%d ok (cstage run + cs==ww byte-id + " + printf("structparam: %d/%d ok (cs==ww byte-id + " "SSE-receive asm)\n", n, n); return 0; } diff --git a/test/wcc/946_structret_run.c b/test/wcc/946_structret_run.c index eae7d292..81f50729 100644 --- a/test/wcc/946_structret_run.c +++ b/test/wcc/946_structret_run.c @@ -1,5 +1,5 @@ /* - * 946_structret_run — runtime + byte-id + asm-pattern net for #171a, the + * 946_structret_run — byte-id + asm-pattern net for #171a, the * float-bearing struct-RETURN SysV ABI (the return twin of #165's struct * PARAM, and the struct counterpart of #164's per-element tuple RETURN). * @@ -14,7 +14,7 @@ * value check are therefore NECESSARY-NOT-SUFFICIENT here (cf. * 946_structparam_run, the param twin). The genuine defect is SysV * register-CLASS conformance, observable only in the emitted asm (and at a - * real ABI boundary). The discriminating dimension is the asm pattern (c). + * real ABI boundary). The discriminating dimension is the asm pattern. * * THE FIX: per-EIGHTBYTE SysV classification (reusing struct_float_class / * structfloatclass verbatim from #165). Each 8-byte eightbyte that is a @@ -33,10 +33,10 @@ * asserts BOTH the correct round-tripped value AND the ABSENCE of an SSE * return/receive, proving the gate caught it. * - * Each row carries THREE dimensions: - * (a) cstage `ww build` + run — exit code (end-to-end round-trip). - * (b) w6c vs w6c_ww `.s` cmp — rule-10 byte-id (both stages identical). - * (c) asm-pattern: the producer `main.mk` returns a lone-f64 eightbyte + * The end-to-end values now live in r946_structret_* fixtures. This carrier + * retains the two observations unavailable to the fixture grammar: + * (a) w6c vs w6c_ww `.s` cmp — rule-10 byte-id (both stages identical). + * (b) asm-pattern: the producer `main.mk` returns a lone-f64 eightbyte * as `MOVSD (BP), Xn` (scratch -> XMM; a GP eightbyte is * `MOVQ (BP), AX`), and the consumer `main` receives it as * `MOVSD Xn, -off(BP)` (XMM -> slot). The producer marker is scoped @@ -45,7 +45,7 @@ * `TEXT main,` (a GP recv MOVQ's instead). PRESENT for the SSE-routed * rows, ABSENT for the GP / fallback rows. A pre-fix (or GP-regressed) * build routes every eightbyte via AX/DX/CX -> both markers absent, - * which is exactly what (c) catches. + * which is exactly what the pattern check catches. */ #include #include @@ -66,7 +66,6 @@ runwait(const char *cmd) struct row { const char *label; const char *src; - int want_exit; int want_sse; /* 1 = struct return rides an XMM eightbyte */ }; @@ -82,7 +81,7 @@ static const struct row rows[] = { "\tlet s: pff = mk();\n" "\tif ((s.a: i64) + (s.b: i64) != 8) { return 1; };\n" "\treturn 0;\n" - "};\n", 0, 1 }, + "};\n", 1 }, /* CURSOR INDEPENDENCE — struct{f64,i32}: eb0 lone f64 (X0), eb1 * pure-INT (the i32 rides AX, NOT DX — the GP cursor starts at 0 * regardless of the float ahead of it). (s.a:i64)+(s.b:i64) = 8. */ @@ -94,7 +93,7 @@ static const struct row rows[] = { "\tlet s: pfi = mk();\n" "\tif ((s.a: i64) + (s.b: i64) != 8) { return 1; };\n" "\treturn 0;\n" - "};\n", 0, 1 }, + "};\n", 1 }, /* ORDER SWAP — struct{i64,f64}: eb0 pure-INT (AX), eb1 lone f64 (X0, * the first float still gets X0). Confirms the float lands in the * next XMM regardless of position. s.a + (s.b:i64) = 8. */ @@ -106,7 +105,7 @@ static const struct row rows[] = { "\tlet s: pif = mk();\n" "\tif (s.a + (s.b: i64) != 8) { return 1; };\n" "\treturn 0;\n" - "};\n", 0, 1 }, + "};\n", 1 }, /* GP REGRESSION — struct{i64,i64}: every eightbyte pure-INT, no float * to route, so it STAYS on the AX/DX/CX transport unchanged (the * in-tree byte-id case). No SSE return/receive marker. */ @@ -118,7 +117,7 @@ static const struct row rows[] = { "\tlet s: pii = mk();\n" "\tif (s.a + s.b != 8) { return 1; };\n" "\treturn 0;\n" - "};\n", 0, 0 }, + "};\n", 0 }, /* FALLBACK GATE — struct{f32,f32}: two f32 pack into ONE SSE eightbyte * (8B struct). The gate rejects f32, so the struct stays GP-routed * (returned/received via AX). The value still round-trips (the 8B MOVQ @@ -134,8 +133,8 @@ static const struct row rows[] = { "\tlet s: pf32 = mk();\n" "\tif ((s.a: i64) + (s.b: i64) != 8) { return 1; };\n" "\treturn 0;\n" - "};\n", 0, 0 }, - { NULL, NULL, 0, 0 } + "};\n", 0 }, + { NULL, NULL, 0 } }; static int @@ -186,6 +185,12 @@ ret_rides_sse(const char *path) if (!f) return -1; static char buf[1 << 18]; size_t n = fread(buf, 1, sizeof buf - 1, f); + /* a .s larger than the buffer would silently drop the region the + * marker scan needs — refuse instead of scanning a truncated tail. */ + if (n == sizeof buf - 1 && fgetc(f) != EOF) { + fclose(f); + return -1; + } fclose(f); buf[n] = '\0'; @@ -224,19 +229,22 @@ main(void) int n = 0, fail = 0; for (int i = 0; rows[i].src; i++, n++) { - /* src + outbin + cs.s + ww.s all live under one tmpdir so the + /* The source and both .s files live under one tmpdir so the * compiler's .sepwork scratch (derived from the src path) lands * inside it; a single rm -rf at the end clears every phase. */ char tmpdir[64]; snprintf(tmpdir, sizeof tmpdir, "/tmp/wwsrt_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); + if (mkdir(tmpdir, 0755) != 0) { + /* not owned — must not rm a path we failed to create */ + perror(tmpdir); + fail++; + continue; + } - char src[128], outbin[128], cs_s[128], ws_s[128], rmcmd[160]; + char src[128], cs_s[128], ws_s[128], rmcmd[160]; snprintf(src, sizeof src, "%s/wwsrt_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/wwsrt_%d_%d", - tmpdir, getpid(), i); snprintf(cs_s, sizeof cs_s, "%s/wwsrt_%d_%d_cs.s", tmpdir, getpid(), i); snprintf(ws_s, sizeof ws_s, "%s/wwsrt_%d_%d_ww.s", @@ -244,43 +252,25 @@ main(void) snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); FILE *f = fopen(src, "wb"); - if (f == NULL) { runwait(rmcmd); fail++; continue; } + if (f == NULL) { fail++; goto rowdone; } fputs(rows[i].src, f); fclose(f); char cmd[4096]; - /* (a) cstage build + run. */ - snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s", - bin, outbin, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: cstage build failed\n", - rows[i].label); - fail++; - runwait(rmcmd); - continue; - } - - int got = runwait(outbin); - if (got != rows[i].want_exit) { - fprintf(stderr, "row[%s]: cstage exit %d, want %d\n", - rows[i].label, got, rows[i].want_exit); - fail++; - } - - /* (b) cs==ww byte-id gate: emit .s from both stages, cmp. */ + /* cs==ww byte-id gate: emit .s from both stages, cmp. */ snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, cs_s, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label); - fail++; runwait(rmcmd); continue; + fail++; goto rowdone; } snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c_ww, ws_s, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c_ww failed\n", rows[i].label); - fail++; runwait(rmcmd); continue; + fail++; goto rowdone; } if (slurp_eq(cs_s, ws_s) != 0) { fprintf(stderr, @@ -289,8 +279,7 @@ main(void) fail++; } - /* (c) asm-pattern discriminator on the return/receive. byte-id - * (b) proves ww_s mirrors cs_s, so checking cs_s suffices. */ + /* byte-id proves ww_s mirrors cs_s, so checking cs_s suffices. */ int sse = ret_rides_sse(cs_s); if (sse < 0) { fprintf(stderr, "row[%s]: cannot scan .s (or SEND/RECV " @@ -304,14 +293,20 @@ main(void) rows[i].want_sse ? "present" : "absent"); fail++; } - runwait(rmcmd); +rowdone: + /* a failed rm must fail the carrier, not leak silently. */ + if (runwait(rmcmd) != 0) { + fprintf(stderr, "row[%s]: cleanup rm failed\n", + rows[i].label); + fail++; + } } if (fail) { fprintf(stderr, "%d/%d struct-return tests failed\n", fail, n); return 1; } - printf("structret: %d/%d ok (cstage run + cs==ww byte-id + " + printf("structret: %d/%d ok (cs==ww byte-id + " "SSE-return asm)\n", n, n); return 0; } diff --git a/test/wcc/949_driver_flagargs.c b/test/wcc/949_driver_flagargs.c index 5d9495cd..06ee6963 100644 --- a/test/wcc/949_driver_flagargs.c +++ b/test/wcc/949_driver_flagargs.c @@ -7,16 +7,17 @@ * silently-swallowed positional. * test — -I/-c/-o carry meaning; -l/-L and any unknown flag are * rejected with "ww test: unknown flag" (rc 2); a lone -I/-o - * is "ww test: -X needs an argument" (rc 2); -c/-o without a - * single test file is "ww test: -c/-o need a single test file" - * (rc 2, #17); a 2nd positional (fnmatch name-filter pattern) + * is "ww test: -X needs an argument" (rc 2); -o without a + * single test file is "ww test: -c/-S/-o need a single test + * file" (rc 2, #17); a missing -run argument is rc 2; a 2nd positional * in directory mode is "ww test: pattern needs a single test * file" (rc 2, #17). * Each row asserts the expected rc + stderr substring AND that the two * drivers are byte-identical (rule 10): the cstage parse_build_flags / * do_test must match the wwstage main.ww dobuild/dorun/dotest verbatim. + * This carrier also owns the `-V` output and C/WW-driver version parity. * - * All rows error during flag parsing, before any compile, so there are no + * All flag rows error during parsing, before any compile, so there are no * build intermediates to redirect (rule 14: light driver test, any NNN). * Sibling of 949_missingpkg (driver enforcement) and 993_ww_ww (twin parity). */ @@ -25,6 +26,7 @@ #include #include #include +#include "ww.h" struct row { const char *args; /* argv tail after the driver path */ @@ -37,47 +39,85 @@ static const struct row rows[] = { { "build -I", 2, "ww build: -I needs an argument" }, { "build -L", 2, "ww build: -L needs an argument" }, { "build -l", 2, "ww build: -l needs an argument" }, + { "build -zz", 2, "ww build: unknown flag" }, { "run -o", 2, "ww run: -o needs an argument" }, { "run -l", 2, "ww run: -l needs an argument" }, + { "run -zz", 2, "ww run: unknown flag" }, { "test -l", 2, "ww test: unknown flag" }, { "test -zz", 2, "ww test: unknown flag" }, { "test -I", 2, "ww test: -I needs an argument" }, - /* #17: -c (compile-only) + -o are now meaningful for `test`; a lone -o - * needs an arg, and -c/-o require a single test file (not the default - * "." directory enumeration). */ + /* Package `test -c` is now valid and covered by native package tests. A + * lone -o needs an argument, while -o still has no directory contract. */ { "test -o", 2, "ww test: -o needs an argument" }, - { "test -o x", 2, "ww test: -c/-o need a single test file" }, - { "test -c", 2, "ww test: -c/-o need a single test file" }, + { "test -o x", 2, "ww test: -c/-S/-o need a single test file" }, + { "test -run", 2, "ww test: -run needs an argument" }, /* #17: a 2nd positional is a fnmatch name-filter pattern, valid only * for a single test file/module; in directory mode (target ".") it * has no single binary to route to and is rejected (rc 2). */ { "test . zzz", 2, "ww test: pattern needs a single test file" }, }; -/* Run "/ " capturing rc + stderr text into err (NUL- - * terminated). Returns the child's exit code (or -1 on spawn failure). */ +static void +read_text(const char *path, char *buf, size_t bufsz) +{ + buf[0] = '\0'; + FILE *f = fopen(path, "r"); + if (f == NULL) + return; + size_t n = fread(buf, 1, bufsz - 1, f); + buf[n] = '\0'; + fclose(f); +} + +/* Run "/ " and capture both output streams. */ static int run_drv(const char *bin, const char *drv, const char *args, - char *err, size_t errsz) + char *out, size_t outsz, char *err, size_t errsz) { int pid = getpid(); - char errf[64], cmd[2048]; + char outf[64], errf[64], cmd[2048]; + snprintf(outf, sizeof outf, "/tmp/dfa949_%d.out", pid); snprintf(errf, sizeof errf, "/tmp/dfa949_%d.err", pid); - snprintf(cmd, sizeof cmd, "%s/%s %s >/dev/null 2>%s", bin, drv, args, errf); + snprintf(cmd, sizeof cmd, "%s/%s %s >%s 2>%s", + bin, drv, args, outf, errf); int rc = system(cmd); - if (rc == -1) { unlink(errf); return -1; } - rc = WIFEXITED(rc) ? WEXITSTATUS(rc) : 1; - err[0] = '\0'; - FILE *f = fopen(errf, "r"); - if (f) { - size_t got = fread(err, 1, errsz - 1, f); - err[got] = '\0'; - fclose(f); + if (rc == -1) { + unlink(outf); + unlink(errf); + return -1; } + rc = WIFEXITED(rc) ? WEXITSTATUS(rc) : 1; + read_text(outf, out, outsz); + read_text(errf, err, errsz); + unlink(outf); unlink(errf); return rc; } +static int +test_version(const char *bin) +{ + char cout[128], cerr[128], wout[128], werr[128], want[128]; + int crc = run_drv(bin, "ww", "-V", cout, sizeof cout, + cerr, sizeof cerr); + int wrc = run_drv(bin, "ww_ww", "-V", wout, sizeof wout, + werr, sizeof werr); + int fail = 0; + snprintf(want, sizeof want, "ww %s\n", WW_VERSION); + if (crc != 0 || strcmp(cout, want) != 0 || cerr[0] != '\0') { + fprintf(stderr, "949 FAIL: ww -V rc=%d stdout='%s' stderr='%s'\n", + crc, cout, cerr); + fail = 1; + } + if (wrc != crc || strcmp(wout, cout) != 0 || strcmp(werr, cerr) != 0) { + fprintf(stderr, "949 FAIL: ww_ww -V rc=%d stdout='%s' stderr='%s' " + "!= ww rc=%d stdout='%s' stderr='%s'\n", + wrc, wout, werr, crc, cout, cerr); + fail = 1; + } + return fail; +} + int main(void) { @@ -91,12 +131,14 @@ main(void) bin = absbin; } - int fail = 0; + int fail = test_version(bin); size_t n = sizeof rows / sizeof rows[0]; for (size_t i = 0; i < n; i++) { - char cerr[4096], werr[4096]; - int crc = run_drv(bin, "ww", rows[i].args, cerr, sizeof cerr); - int wrc = run_drv(bin, "ww_ww", rows[i].args, werr, sizeof werr); + char cout[256], cerr[4096], wout[256], werr[4096]; + int crc = run_drv(bin, "ww", rows[i].args, + cout, sizeof cout, cerr, sizeof cerr); + int wrc = run_drv(bin, "ww_ww", rows[i].args, + wout, sizeof wout, werr, sizeof werr); if (crc != rows[i].rc) { fprintf(stderr, "949 FAIL: ww %s rc=%d want=%d\n", @@ -119,9 +161,15 @@ main(void) rows[i].args, werr, cerr); fail = 1; } + if (strcmp(wout, cout) != 0) { + fprintf(stderr, "949 FAIL: %s ww_ww stdout '%s' != ww '%s'\n", + rows[i].args, wout, cout); + fail = 1; + } } if (fail) return 1; - printf("driver flag-argument error branches: %zu rows, ww==ww_ww pinned\n", n); + printf("driver version + flag-argument errors: %zu rows, ww==ww_ww pinned\n", + n); return 0; } diff --git a/test/wcc/949_missingpkg.c b/test/wcc/949_missingpkg.c index a3edd782..76e80af8 100644 --- a/test/wcc/949_missingpkg.c +++ b/test/wcc/949_missingpkg.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -35,24 +36,28 @@ runwait(const char *cmd) static int run_miss(const char *bin) { - int pid = getpid(); - char tmpdir[64], src[128], errf[128], outb[128], cmd[2048]; - char rmcmd[160], line[4096]; - /* source + binary + intermediates + .sepwork all under one tmpdir so - * the compiler's output-derived .sepwork scratch lands here (not bare - * /tmp where unlink/rmdir would never name it) and the single rm -rf - * reclaims it on every exit path. */ - snprintf(tmpdir, sizeof tmpdir, "/tmp/mp949_miss_%d_d", pid); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/mp949_miss_%d.ww", tmpdir, pid); - snprintf(errf, sizeof errf, "%s/mp949_miss_%d.err", tmpdir, pid); - /* -o a writable stem inside tmpdir (not /dev/null): post-T3 the - * intermediate .combined.ww/.s/.o follow -o, so /dev/null would yield - * an uncreatable /dev/null.combined.ww and mask the missing-pkg fatal. */ - snprintf(outb, sizeof outb, "%s/mp949_miss_%d.out", tmpdir, pid); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + char tmpdir[] = "/tmp/mp949_miss_XXXXXX"; + char src[128], errf[128], outb[128], scratch[144], cmd[2048]; + char line[4096]; + if (mkdtemp(tmpdir) == NULL) { + fprintf(stderr, "949 FAIL: acquire miss workspace\n"); + return 1; + } + /* Source, capture, output, and output-derived .sepwork all live under + * this invocation-owned directory. */ + snprintf(src, sizeof src, "%s/miss.ww", tmpdir); + snprintf(errf, sizeof errf, "%s/miss.err", tmpdir); + /* -o needs a writable stem inside tmpdir (not /dev/null): the + * output-derived .sepwork follows -o, so /dev/null would yield an + * uncreatable /dev/null.sepwork and mask the missing-package fatal. */ + snprintf(outb, sizeof outb, "%s/miss.out", tmpdir); + snprintf(scratch, sizeof scratch, "%s.sepwork", outb); + int result = 1; FILE *f = fopen(src, "w"); - if (!f) { fprintf(stderr, "949 FAIL: stage miss\n"); runwait(rmcmd); return 1; } + if (!f) { + fprintf(stderr, "949 FAIL: stage miss\n"); + goto cleanup; + } fputs("package main;\n" "import nosuchpkg;\n" "export fn main() i32 = { return 0; };\n", f); @@ -63,8 +68,7 @@ run_miss(const char *bin) int rc = runwait(cmd); if (rc == 0) { fprintf(stderr, "949 FAIL: ww accepted a missing import\n"); - runwait(rmcmd); - return 1; + goto cleanup; } int found = 0; f = fopen(errf, "r"); @@ -75,13 +79,28 @@ run_miss(const char *bin) } fclose(f); } - runwait(rmcmd); if (!found) { fprintf(stderr, "949 FAIL: no 'cannot find package nosuchpkg' " "on stderr\n"); - return 1; + goto cleanup; } - return 0; + result = 0; + +cleanup: + { + int bad = 0; + snprintf(cmd, sizeof cmd, "rm -rf %s", scratch); + if (runwait(cmd) != 0) bad = 1; + if (unlink(outb) != 0 && errno != ENOENT) bad = 1; + if (unlink(errf) != 0 && errno != ENOENT) bad = 1; + if (unlink(src) != 0 && errno != ENOENT) bad = 1; + if (rmdir(tmpdir) != 0) bad = 1; + if (bad) { + fprintf(stderr, "949 FAIL: cleanup miss workspace\n"); + result = 1; + } + } + return result; } /* inline branch: a single-file multi-package unit whose `import aa` is @@ -90,18 +109,23 @@ run_miss(const char *bin) static int run_inline(const char *bin) { - int pid = getpid(); - char tmpdir[64], src[128], outb[128], cmd[2048], rmcmd[160]; - /* source + binary + .sepwork all under one tmpdir so the compiler's - * source-derived .sepwork scratch lands here (not bare /tmp) and the - * single rm -rf reclaims it. */ - snprintf(tmpdir, sizeof tmpdir, "/tmp/mp949_inl_%d_d", pid); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/mp949_inl_%d.ww", tmpdir, pid); - snprintf(outb, sizeof outb, "%s/mp949_inl_%d", tmpdir, pid); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + char tmpdir[] = "/tmp/mp949_inl_XXXXXX"; + char src[128], outb[128], scratch[144], cmd[2048]; + if (mkdtemp(tmpdir) == NULL) { + fprintf(stderr, "949 FAIL: acquire inline workspace\n"); + return 1; + } + /* Source, output, and output-derived .sepwork all live under this + * invocation-owned directory. */ + snprintf(src, sizeof src, "%s/inline.ww", tmpdir); + snprintf(outb, sizeof outb, "%s/inline", tmpdir); + snprintf(scratch, sizeof scratch, "%s.sepwork", outb); + int result = 1; FILE *f = fopen(src, "w"); - if (!f) { fprintf(stderr, "949 FAIL: stage inline\n"); runwait(rmcmd); return 1; } + if (!f) { + fprintf(stderr, "949 FAIL: stage inline\n"); + goto cleanup; + } fputs("package aa;\n" "export fn getv() i32 = { return 7; };\n" "package main;\n" @@ -115,16 +139,29 @@ run_inline(const char *bin) if (rc != 0) { fprintf(stderr, "949 FAIL: inline-package build failed " "(inline-skip branch regressed)\n"); - runwait(rmcmd); - return 1; + goto cleanup; } int got = runwait(outb); - runwait(rmcmd); if (got != 7) { fprintf(stderr, "949 FAIL: inline-package exit=%d want=7\n", got); - return 1; + goto cleanup; } - return 0; + result = 0; + +cleanup: + { + int bad = 0; + snprintf(cmd, sizeof cmd, "rm -rf %s", scratch); + if (runwait(cmd) != 0) bad = 1; + if (unlink(outb) != 0 && errno != ENOENT) bad = 1; + if (unlink(src) != 0 && errno != ENOENT) bad = 1; + if (rmdir(tmpdir) != 0) bad = 1; + if (bad) { + fprintf(stderr, "949 FAIL: cleanup inline workspace\n"); + result = 1; + } + } + return result; } int diff --git a/test/wcc/949_xmod_fnptr_const_run.c b/test/wcc/949_xmod_fnptr_const_run.c index e2fc3c8c..d040ac25 100644 --- a/test/wcc/949_xmod_fnptr_const_run.c +++ b/test/wcc/949_xmod_fnptr_const_run.c @@ -43,14 +43,15 @@ * | — the #117 over-accept hazard) | | * * Cross-module via OWN 2-module fixtures in a PRIVATE mktemp dir (784 - * model) — NOT the real lib/ascii: the driver writes intermediates next - * to traversed sources, so importing lib/ would race; a synthetic module + * model) — NOT the real lib/ascii: ordinary build retains its caller-owned + * .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 @@ -164,19 +165,19 @@ static const struct scenario scenarios[] = { { "nonfn_dotted_table", nonfn_dotted_files, K_BUILDERR, 0 }, }; -/* Build `driver build --sep -o /` in the fixture dir; returns +/* 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 per-package asm lands in /.sepwork/ - * (cachetag keys a private WW_PKGCACHE so out/.pkgcache is untouched). */ + * exit to want_exit. #94: the caller-visible per-package asm is retained in + * /.sepwork/ until this carrier finishes observing it. */ static int build_run(const char *dir, const char *driver, const struct scenario *sc, - const char *stem, const char *cachetag) + const char *stem) { char cmd[4096], path[1024]; snprintf(cmd, sizeof cmd, - "cd %s && WW_PKGCACHE=%s/pkgc_%s %s build --sep -I %s -o %s/%s " + "cd %s && %s build -I %s -o %s/%s " "%s/main.ww >/dev/null 2>%s/err", - dir, dir, cachetag, driver, dir, dir, stem, dir, dir); + dir, driver, dir, dir, stem, dir, dir); int brc = runwait(cmd); if (sc->kind == K_BUILDERR) { if (brc == 0) { @@ -217,19 +218,25 @@ run_scenario(const char *bin, const struct scenario *sc) FILE *f = fopen(path, "wb"); if (!f) { fprintf(stderr, "949[%s]: write %s\n", sc->label, sc->files[i].name); rc = -1; goto done; } - fputs(sc->files[i].src, f); - fclose(f); + int writefail = fputs(sc->files[i].src, f) == EOF; + if (fclose(f) != 0) writefail = 1; + if (writefail) { + fprintf(stderr, "949[%s]: write %s\n", sc->label, + sc->files[i].name); + rc = -1; + goto done; + } } - /* cstage driver --sep build (+run for K_RUN) → /main_c.sepwork/. */ + /* 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", "c") != 0) { rc = -1; goto done; } + if (build_run(dir, csdrv, sc, "main_c") != 0) { rc = -1; goto done; } - /* wwstage driver --sep build (+run for K_RUN) — race-free in the + /* wwstage driver build (+run for K_RUN) — race-free in the * private mktemp dir. Direct proof the wwstage emits a runnable * binary. Distinct stem so its sepwork doesn't clobber cstage's. */ snprintf(wwdrv, sizeof wwdrv, "%s/ww_ww", bin); - if (build_run(dir, wwdrv, sc, "main_w", "w") != 0) { rc = -1; goto done; } + if (build_run(dir, wwdrv, sc, "main_w") != 0) { rc = -1; goto done; } if (sc->kind == K_BUILDERR) goto done; @@ -242,10 +249,16 @@ run_scenario(const char *bin, const struct scenario *sc) snprintf(cs_s, sizeof cs_s, "%s/all_cs.s", dir); snprintf(ws_s, sizeof ws_s, "%s/all_ww.s", dir); snprintf(cmd, sizeof cmd, "cat %s/main_c.sepwork/*.s > %s 2>/dev/null", - dir, cs_s); if (system(cmd)) {} + dir, cs_s); + int cs_cat = runwait(cmd); snprintf(cmd, sizeof cmd, "cat %s/main_w.sepwork/*.s > %s 2>/dev/null", - dir, ws_s); if (system(cmd)) {} - if (slurp_eq(cs_s, ws_s) != 0) { + dir, ws_s); + int ws_cat = runwait(cmd); + if (cs_cat != 0 || ws_cat != 0) { + fprintf(stderr, "949[%s]: could not aggregate retained " + "sepwork asm\n", sc->label); + rc = -1; + } else if (slurp_eq(cs_s, ws_s) != 0) { fprintf(stderr, "949[%s]: cs.s/ww.s DIFFER (rule-10 " "byte-id violation)\n", sc->label); rc = -1; @@ -253,8 +266,30 @@ run_scenario(const char *bin, const struct scenario *sc) } done: - snprintf(cmd, sizeof cmd, "rm -rf %s", dir); - (void)runwait(cmd); + { + int cleanfail = 0; + snprintf(cmd, sizeof cmd, "rm -rf %s/main_c.sepwork", dir); + if (runwait(cmd) != 0) cleanfail = 1; + snprintf(cmd, sizeof cmd, "rm -rf %s/main_w.sepwork", dir); + if (runwait(cmd) != 0) cleanfail = 1; + for (int i = 0; sc->files[i].name; i++) { + snprintf(path, sizeof path, "%s/%s", dir, sc->files[i].name); + if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1; + } + const char *children[] = { + "main_c", "main_w", "err", "all_cs.s", "all_ww.s", NULL + }; + for (int i = 0; children[i]; i++) { + snprintf(path, sizeof path, "%s/%s", dir, children[i]); + if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1; + } + if (rmdir(dir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "949[%s]: temporary cleanup failed\n", + sc->label); + rc = -1; + } + } return rc; } diff --git a/test/wcc/951_defdim_struct_run.c b/test/wcc/951_defdim_struct_run.c index 396a0ee7..cf7ae705 100644 --- a/test/wcc/951_defdim_struct_run.c +++ b/test/wcc/951_defdim_struct_run.c @@ -1,5 +1,5 @@ /* - * 951_defdim_struct_run — runtime + byte-id net for #141: a struct + * 951_defdim_struct_run — byte-id net for #141: a struct * field whose array dimension is a `def` (`[MAX]u8`, MAX a same-module * def), not an integer literal. BOTH stages were wrong, oppositely: * @@ -21,8 +21,8 @@ * ww read a corrupted `end`; post-fix it round-trips and cs==ww byte-id. * Closes #13's def-dim half (the slice-repeat clause stays open). * - * cstage `ww build` + run for exit code; w6c vs w6c_ww `.s` cmp for the - * rule-10 byte-id gate. The byte-id leg compiles the `ww build`-produced + * Runtime values live in r951_defdim_* fixtures. This carrier compares the + * `ww build`-produced * .combined.ww, not the raw src: w6c does no import resolution, so the * cross-module row (C1, `import os`) only folds once concatenated, and a * same-module row combines to itself. The last row is the ACTUAL @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -46,7 +47,7 @@ runwait(const char *cmd) return -1; } -struct row { const char *label; const char *src; int want_exit; }; +struct row { const char *label; const char *src; }; static const struct row rows[] = { /* TEETH: def-dim array FOLLOWED by a scalar; write both, read the @@ -61,7 +62,7 @@ static const struct row rows[] = { " s.b[3] = 9u8;\n" " s.end = 42;\n" " return s.end;\n" - "};\n", 42 }, + "};\n" }, /* the array element itself must survive too — read the last byte * (pre-fix the overlapping `end` write would clobber it). */ { "m4_belem", @@ -73,7 +74,7 @@ static const struct row rows[] = { " s.b[3] = 200u8;\n" " s.end = 7;\n" " return s.b[3]: i32;\n" - "};\n", 200 }, + "};\n" }, /* the ~4KB path::buffer shape — def MAX=4095, read the trailing * scalar after a 4095-byte array. */ { "m4095_end", @@ -85,7 +86,7 @@ static const struct row rows[] = { " s.b[4094] = 5u8;\n" " s.end = 99;\n" " return s.end;\n" - "};\n", 99 }, + "};\n" }, /* two scalars after the def-dim array — pins the cumulative offset * (a+c read their written values: 11+22=33). */ { "twoafter", @@ -97,7 +98,7 @@ static const struct row rows[] = { " s.a = 11;\n" " s.c = 22;\n" " return s.a + s.c;\n" - "};\n", 33 }, + "};\n" }, /* C1 (rob-mandatory): the ACTUAL path::buffer shape — a CROSS- * MODULE def dimension. `def MAX = os.PATH_MAX - 1` folds an N_DOT * (os.PATH_MAX) const-ref through eval_def_const's cross-module arm, @@ -115,8 +116,8 @@ static const struct row rows[] = { " s.b[4094] = 7u8;\n" " s.end = 77;\n" " return s.end;\n" - "};\n", 77 }, - { NULL, NULL, 0 } + "};\n" }, + { NULL, NULL } }; static int @@ -162,14 +163,21 @@ main(void) char src[64]; snprintf(src, sizeof src, "/tmp/wwdds_%d_%d.ww", getpid(), i); FILE *f = fopen(src, "wb"); - if (f == NULL) { fail++; continue; } + if (f == NULL) { perror(src); fail++; continue; } fputs(rows[i].src, f); fclose(f); char tmpdir[64]; snprintf(tmpdir, sizeof tmpdir, "/tmp/wwdds_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); + if (mkdir(tmpdir, 0755) != 0) { + /* not owned — must not rm a path we failed to create */ + perror(tmpdir); + fail++; + if (unlink(src) != 0 && errno != ENOENT) + perror(src); + continue; + } char base[64]; const char *b = strrchr(src, '/'); @@ -178,36 +186,16 @@ main(void) char *dot = strrchr(base, '.'); if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; - /* #94 sep layout: each driver's --sep build emits the runnable + /* #94 sep layout: each driver's build emits the runnable * binary at + per-package asm under .sepwork/. * Distinct stems so the wwstage sepwork doesn't clobber cstage's; - * pin WW_PKGCACHE under tmpdir so out/.pkgcache is untouched. A + * pin all outputs stay under tmpdir. A * cross-module row's `import os` resolves per-package, a same- * module row is just __root — both compare via the sorted glob. */ char cmd[2048], stem_c[160], stem_w[160]; snprintf(stem_c, sizeof stem_c, "%s/%s_c", tmpdir, base); snprintf(stem_w, sizeof stem_w, "%s/%s_w", tmpdir, base); - snprintf(cmd, sizeof cmd, - "cd %s && WW_PKGCACHE=%s/pkgc_c %s/ww build --sep -o %s %s", - tmpdir, tmpdir, bin, stem_c, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: cstage build failed\n", - rows[i].label); - fail++; - unlink(src); - snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir); - if (system(cmd)) {} - continue; - } - - int got = runwait(stem_c); - if (got != rows[i].want_exit) { - fprintf(stderr, "row[%s]: cstage exit %d, want %d\n", - rows[i].label, got, rows[i].want_exit); - fail++; - } - char cs_s[64], ws_s[64]; snprintf(cs_s, sizeof cs_s, "/tmp/wwdds_%d_%d_cs.s", getpid(), i); @@ -215,20 +203,38 @@ main(void) getpid(), i); snprintf(cmd, sizeof cmd, - "cd %s && WW_PKGCACHE=%s/pkgc_w %s/ww_ww build --sep -o %s %s " - ">/dev/null 2>&1", tmpdir, tmpdir, bin, stem_w, src); + "cd %s && %s/ww build -S -o %s %s", + tmpdir, bin, stem_c, src); + if (runwait(cmd) != 0) { + fprintf(stderr, "row[%s]: cstage build failed\n", + rows[i].label); + fail++; + goto rowdone; + } + + snprintf(cmd, sizeof cmd, + "cd %s && %s/ww_ww build -S -o %s %s " + ">/dev/null 2>&1", tmpdir, bin, stem_w, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: ww_ww build failed\n", rows[i].label); fail++; } else { + /* a failed cat (e.g. an empty sepwork glob) must fail + * the row — two absent trees would compare equal-empty. */ snprintf(cmd, sizeof cmd, "cat %s.sepwork/*.s > %s 2>/dev/null", stem_c, cs_s); - if (system(cmd)) {} + int catc = runwait(cmd); snprintf(cmd, sizeof cmd, "cat %s.sepwork/*.s > %s 2>/dev/null", stem_w, ws_s); - if (system(cmd)) {} - if (slurp_eq(cs_s, ws_s) != 0) { + int catw = runwait(cmd); + if (catc != 0 || catw != 0) { + fprintf(stderr, + "row[%s]: sepwork .s concat failed " + "(cstage=%d wwstage=%d)\n", + rows[i].label, catc, catw); + fail++; + } else if (slurp_eq(cs_s, ws_s) != 0) { fprintf(stderr, "row[%s]: cstage/wwstage .s DIFFER " "(rule-10 byte-id violation)\n", @@ -237,9 +243,29 @@ main(void) } } +rowdone: + /* a failed cleanup must fail the carrier, not leak silently; + * ENOENT is fine — an early leg may exit before creating the + * concat files. */ snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir); - if (system(cmd)) {} - unlink(src); unlink(cs_s); unlink(ws_s); + int cleanfail = runwait(cmd) != 0; + if (unlink(src) != 0 && errno != ENOENT) { + perror(src); + cleanfail = 1; + } + if (unlink(cs_s) != 0 && errno != ENOENT) { + perror(cs_s); + cleanfail = 1; + } + if (unlink(ws_s) != 0 && errno != ENOENT) { + perror(ws_s); + cleanfail = 1; + } + if (cleanfail) { + fprintf(stderr, "row[%s]: cleanup failed\n", + rows[i].label); + fail++; + } } if (fail) { @@ -247,7 +273,7 @@ main(void) fail, n); return 1; } - printf("defdim_struct: %d/%d ok (cstage run + cs==ww byte-id)\n", + printf("defdim_struct: %d/%d ok (cs==ww byte-id)\n", n, n); return 0; } diff --git a/test/wcc/952_floats_run.c b/test/wcc/952_floats_run.c index d37d2957..78886bba 100644 --- a/test/wcc/952_floats_run.c +++ b/test/wcc/952_floats_run.c @@ -33,6 +33,7 @@ */ #include #include +#include #include #include #include @@ -372,42 +373,61 @@ main(void) int n = 0, fail = 0; for (int i = 0; rows[i].src; i++, n++) { - char tmpdir[64]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/wwflt_%d_%d", getpid(), i); - mkdir(tmpdir, 0755); - - char rmcmd[128]; - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); - - char src[128]; - snprintf(src, sizeof src, "%s/wwflt_%d_%d.ww", tmpdir, getpid(), i); - - FILE *f = fopen(src, "wb"); - if (f == NULL) { fail++; runwait(rmcmd); continue; } - fputs(rows[i].src, f); - fclose(f); - - char outbin[128]; + char tmpdir[] = "/tmp/wwflt_XXXXXX"; + if (mkdtemp(tmpdir) == NULL) { + fprintf(stderr, "row %d: mkdtemp failed\n", i); + fail++; + continue; + } + int rowfail = 0; + char src[128], outbin[128], cmd[2048]; + snprintf(src, sizeof src, "%s/case.ww", tmpdir); snprintf(outbin, sizeof outbin, "%s/out", tmpdir); - char cmd[2048]; + FILE *f = fopen(src, "wb"); + if (f == NULL) { + fprintf(stderr, "row %d: source open failed\n", i); + rowfail = 1; + goto cleanup; + } + int writefail = fputs(rows[i].src, f) == EOF; + if (fclose(f) != 0) writefail = 1; + if (writefail) { + fprintf(stderr, "row %d: source write failed\n", i); + rowfail = 1; + goto cleanup; + } + snprintf(cmd, sizeof cmd, "%s/ww build -I %s/lib -o %s %s", bin, cwd, outbin, src); if (runwait(cmd) != 0) { fprintf(stderr, "row %d: build failed\n src: %s\n", i, rows[i].src); - fail++; - runwait(rmcmd); - continue; + rowfail = 1; + goto cleanup; } int got = runwait(outbin); if (got != rows[i].want_exit) { fprintf(stderr, "row %d: exit %d, want %d\n src: %s\n", i, got, rows[i].want_exit, rows[i].src); - fail++; + rowfail = 1; } - runwait(rmcmd); + +cleanup: + { + int cleanfail = 0; + snprintf(cmd, sizeof cmd, "rm -rf %s/out.sepwork", tmpdir); + if (runwait(cmd) != 0) cleanfail = 1; + if (unlink(outbin) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(tmpdir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "row %d: temporary cleanup failed\n", i); + rowfail = 1; + } + } + if (rowfail) fail++; } if (fail) { fprintf(stderr, "%d/%d floats tests failed\n", fail, n); diff --git a/test/wcc/954_tuprecv_run.c b/test/wcc/954_tuprecv_run.c index 3e94ed81..b84d85a6 100644 --- a/test/wcc/954_tuprecv_run.c +++ b/test/wcc/954_tuprecv_run.c @@ -18,6 +18,7 @@ */ #include #include +#include #include #include #include @@ -61,19 +62,29 @@ main(void) return 1; } - char tmpdir[64], rmcmd[160], srcp[128], errf[128], cmd[2048]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/wwtuprecv_%d", getpid()); - mkdir(tmpdir, 0755); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + char tmpdir[] = "/tmp/wwtuprecv_XXXXXX"; + if (mkdtemp(tmpdir) == NULL) { + fprintf(stderr, "tuprecv: mkdtemp failed\n"); + return 1; + } + char srcp[128], errf[128], cmd[2048]; snprintf(srcp, sizeof srcp, "%s/ctl_destr.ww", tmpdir); snprintf(errf, sizeof errf, "%s/err.txt", tmpdir); - FILE *f = fopen(srcp, "wb"); - if (f == NULL) { runwait(rmcmd); return 1; } - fputs(src, f); - fclose(f); - int fail = 0; + FILE *f = fopen(srcp, "wb"); + if (f == NULL) { + fprintf(stderr, "tuprecv: source open failed\n"); + fail++; + goto cleanup; + } + int writefail = fputs(src, f) == EOF; + if (fclose(f) != 0) writefail = 1; + if (writefail) { + fprintf(stderr, "tuprecv: source write failed\n"); + fail++; + goto cleanup; + } /* w6c_ww must compile the construct cleanly AND emit no asserttyped. */ snprintf(cmd, sizeof cmd, "%s -o /dev/null %s 2>%s", w6c_ww, srcp, errf); @@ -89,7 +100,17 @@ main(void) fail++; } - runwait(rmcmd); +cleanup: + { + int cleanfail = 0; + if (unlink(errf) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(srcp) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(tmpdir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "tuprecv: temporary cleanup failed\n"); + fail++; + } + } if (fail) return 1; printf("tuprecv: asserttyped-absence pin ok (#121)\n"); return 0; diff --git a/test/wcc/956_modqualdestr_run.c b/test/wcc/956_modqualdestr_run.c index f43649a3..bc15ed03 100644 --- a/test/wcc/956_modqualdestr_run.c +++ b/test/wcc/956_modqualdestr_run.c @@ -1,5 +1,5 @@ /* - * 956_modqualdestr_run — runtime + byte-id + stamp regression net for + * 956_modqualdestr_run — byte-id + stamp regression net for * #6a-A: module-qualified destructure binding types. * * THE GAP: the wwstage N_MLET handler (check.ww) backfilled destructure @@ -23,17 +23,16 @@ * Sibling of 956_tuprecv_f64_run (same-module float destructure, bare * N_IDENT callee) and 953_f64crossmod_run (single-call module-qualified * f64). Single-file multi-package form (like 953) so w6c/w6c_ww see the - * cross-module call without -I plumbing. Each row carries three - * dimensions: - * (a) cstage `ww build` + run, asserting the exit code (cstage is the - * reference — correct pre- and post-fix). - * (b) w6c vs w6c_ww `.s` cmp — rule-10 byte-id; pre-fix the float row + * cross-module call without -I plumbing. Runtime values now live in + * r956_modqual_* fixtures. This carrier retains two dimensions: + * (a) w6c vs w6c_ww `.s` cmp — rule-10 byte-id; pre-fix the float row * diverges (w6c_ww loud-aborts, emits no .s). - * (c) #6a-A stamp gate: w6c_ww must emit no `asserttyped:` diagnostic + * (b) #6a-A stamp gate: w6c_ww must emit no `asserttyped:` diagnostic * on the destructure binding idents. Pre-fix fires on every use. */ #include #include +#include #include #include #include @@ -50,7 +49,6 @@ runwait(const char *cmd) struct row { const char *label; const char *src; - int want_exit; }; static const struct row rows[] = { @@ -73,7 +71,7 @@ static const struct row rows[] = { "\tif (g != 9.0) { return 1; };\n" "\tif (exp != 1i64) { return 2; };\n" "\treturn 0;\n" - "};\n", 0 }, + "};\n" }, /* DOMINANT CLASS-A — module-qualified INTEGER destructure (the * checked.addXX shape: `let (res, ov) = checked.addi64(a, b)`). cgen's * structural classifier already routed the integer codegen, so this @@ -92,7 +90,7 @@ static const struct row rows[] = { "\tif (res != 15i64) { return 1; };\n" "\tif (ov) { return 2; };\n" "\treturn (res: i32);\n" - "};\n", 15 }, + "};\n" }, /* MIXED — module-qualified (f64, str) destructure: the float rides * the SSE cursor (X0), the str rides the integer cursor (AX,DX,CX). * Confirms the stamp distributes correctly onto a wide-header binding @@ -111,7 +109,7 @@ static const struct row rows[] = { "\tif (g != 4.0) { return 1; };\n" "\tif (s.len != 5) { return 2; };\n" "\treturn (f: i32);\n" - "};\n", 4 }, + "};\n" }, /* CLASS-CLOSURE — NON-destructure cross-module same-leaf shadow. The * #6a-A fix lives at the exprtype N_CALL root, so it closes the whole * class, not just the destructure surface (same coverage-trap as the @@ -135,8 +133,8 @@ static const struct row rows[] = { "\treturn (r.0: i32) + (r.1: i32);\n" "};\n" "package alpha;\n" - "export fn dup() (i64, str) = { return (9i64, \"wrong\"); };\n", 10 }, - { NULL, NULL, 0 } + "export fn dup() (i64, str) = { return (9i64, \"wrong\"); };\n" }, + { NULL, NULL } }; static int @@ -180,66 +178,52 @@ main(void) int n = 0, fail = 0; for (int i = 0; rows[i].src; i++, n++) { - /* src, outbin, cs.s, ww.s, err all live under one tmpdir so - * the compiler's path-derived .sepwork scratch stays inside it - * and a single rm -rf reclaims everything (V3). */ - char tmpdir[64], rmcmd[160]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/wwmqd_%d_d_%d", - getpid(), i); - mkdir(tmpdir, 0755); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); - - char src[128]; - snprintf(src, sizeof src, "%s/wwmqd_%d_%d.ww", - tmpdir, getpid(), i); - FILE *f = fopen(src, "wb"); - if (f == NULL) { fail++; runwait(rmcmd); continue; } - fputs(rows[i].src, f); - fclose(f); - - char cmd[2048]; - - /* (a) cstage build + run. */ - char outbin[128]; - snprintf(outbin, sizeof outbin, "%s/wwmqd_%d_%d", - tmpdir, getpid(), i); - - snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s", - bin, outbin, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: cstage build failed\n", - rows[i].label); + /* Every compiler input, output, and capture lives under one + * collision-safe directory owned by this row. */ + char tmpdir[] = "/tmp/wwmqd_XXXXXX"; + if (mkdtemp(tmpdir) == NULL) { + fprintf(stderr, "row[%s]: mkdtemp failed\n", rows[i].label); fail++; - runwait(rmcmd); continue; } - int got = runwait(outbin); - if (got != rows[i].want_exit) { - fprintf(stderr, "row[%s]: cstage exit %d, want %d\n", - rows[i].label, got, rows[i].want_exit); + char src[128]; + snprintf(src, sizeof src, "%s/case.ww", tmpdir); + FILE *f = fopen(src, "wb"); + if (f == NULL) { + fprintf(stderr, "row[%s]: source open failed\n", rows[i].label); fail++; + goto row_cleanup; + } + int writefail = fputs(rows[i].src, f) == EOF; + if (fclose(f) != 0) writefail = 1; + if (writefail) { + fprintf(stderr, "row[%s]: source write failed\n", rows[i].label); + fail++; + goto row_cleanup; } - /* (b) cs==ww byte-id gate: emit .s from both stages, cmp. */ + char cmd[2048]; + + /* cs==ww byte-id gate: emit .s from both stages, cmp. */ char cs_s[128], ws_s[128]; - snprintf(cs_s, sizeof cs_s, "%s/wwmqd_%d_%d_cs.s", - tmpdir, getpid(), i); - snprintf(ws_s, sizeof ws_s, "%s/wwmqd_%d_%d_ww.s", - tmpdir, getpid(), i); + snprintf(cs_s, sizeof cs_s, "%s/cs.s", tmpdir); + snprintf(ws_s, sizeof ws_s, "%s/ww.s", tmpdir); snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, cs_s, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label); - fail++; runwait(rmcmd); continue; + fail++; + goto row_cleanup; } snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c_ww, ws_s, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c_ww failed\n", rows[i].label); - fail++; runwait(rmcmd); continue; + fail++; + goto row_cleanup; } if (slurp_eq(cs_s, ws_s) != 0) { fprintf(stderr, @@ -248,24 +232,45 @@ main(void) fail++; } - /* (c) #6a-A stamp gate: the module-qualified destructure + /* The module-qualified destructure * binding must carry a checker type stamp, so w6c_ww emits no * `asserttyped:` diagnostic on its idents. Non-vacuous — * pre-fix (HEAD) w6c_ww fires asserttyped on every binding use * (and the float row loud-aborts in cgen above). */ char errf[128]; - snprintf(errf, sizeof errf, - "%s/wwmqd_%d_%d_err.txt", tmpdir, getpid(), i); + snprintf(errf, sizeof errf, "%s/err.txt", tmpdir); snprintf(cmd, sizeof cmd, "%s -o /dev/null %s 2>%s", w6c_ww, src, errf); - runwait(cmd); + if (runwait(cmd) != 0) { + fprintf(stderr, "row[%s]: w6c_ww stamp probe failed\n", + rows[i].label); + fail++; + } snprintf(cmd, sizeof cmd, "grep -q asserttyped %s", errf); if (runwait(cmd) == 0) { fprintf(stderr, "row[%s]: w6c_ww emitted asserttyped " "(destructure binding unstamped)\n", rows[i].label); fail++; } - runwait(rmcmd); + +row_cleanup: + { + int cleanfail = 0; + char path[128]; + const char *children[] = { + "case.ww", "cs.s", "ww.s", "err.txt", NULL + }; + for (int j = 0; children[j]; j++) { + snprintf(path, sizeof path, "%s/%s", tmpdir, children[j]); + if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1; + } + if (rmdir(tmpdir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "row[%s]: temporary cleanup failed\n", + rows[i].label); + fail++; + } + } } if (fail) { @@ -273,7 +278,7 @@ main(void) fail, n); return 1; } - printf("modqualdestr: %d/%d ok (cstage run + cs==ww byte-id + " + printf("modqualdestr: %d/%d ok (cs==ww byte-id + " "stamp)\n", n, n); return 0; } diff --git a/test/wcc/956_tuprecv_f64_run.c b/test/wcc/956_tuprecv_f64_run.c index 8b9dc4c3..1243dab6 100644 --- a/test/wcc/956_tuprecv_f64_run.c +++ b/test/wcc/956_tuprecv_f64_run.c @@ -20,6 +20,7 @@ */ #include #include +#include #include #include #include @@ -146,40 +147,74 @@ main(void) /* NON-VACUITY self-check: the grep must FIRE on a literal asserttyped * line. A grep that never matches would pass every row vacuously. */ { - char probe[80], cmd[160]; - snprintf(probe, sizeof probe, "/tmp/wwtupf_probe_%d.txt", getpid()); - FILE *p = fopen(probe, "wb"); - if (!p) { fprintf(stderr, "tuprecv_f64: probe open failed\n"); return 1; } - fputs("error: asserttyped: e.type_ nil\n", p); - fclose(p); - snprintf(cmd, sizeof cmd, "grep -q asserttyped %s", probe); - if (runwait(cmd) != 0) { - fprintf(stderr, "tuprecv_f64: grep self-check FAILED — the " - "asserttyped gate is vacuous\n"); - unlink(probe); + char probedir[] = "/tmp/wwtupf_probe_XXXXXX"; + if (mkdtemp(probedir) == NULL) { + fprintf(stderr, "tuprecv_f64: probe mkdtemp failed\n"); return 1; } - unlink(probe); + char probe[128], cmd[256]; + snprintf(probe, sizeof probe, "%s/probe.txt", probedir); + int probe_fail = 0; + FILE *p = fopen(probe, "wb"); + if (!p) { + fprintf(stderr, "tuprecv_f64: probe open failed\n"); + probe_fail = 1; + } else { + int writefail = + fputs("error: asserttyped: e.type_ nil\n", p) == EOF; + if (fclose(p) != 0) writefail = 1; + if (writefail) { + fprintf(stderr, "tuprecv_f64: probe write failed\n"); + probe_fail = 1; + } else { + snprintf(cmd, sizeof cmd, "grep -q asserttyped %s", probe); + if (runwait(cmd) != 0) { + fprintf(stderr, "tuprecv_f64: grep self-check FAILED — the " + "asserttyped gate is vacuous\n"); + probe_fail = 1; + } + } + } + int cleanfail = 0; + if (unlink(probe) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(probedir) != 0) cleanfail = 1; + if (cleanfail) + fprintf(stderr, "tuprecv_f64: probe cleanup failed\n"); + if (probe_fail || cleanfail) return 1; } for (int i = 0; rows[i].src; i++, n++) { - char tmpdir[64], src[128], errf[128], rmcmd[160], cmd[2048]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/wwtupf_%d_d_%d", - getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/wwtupf_%d_%d.ww", - tmpdir, getpid(), i); + char tmpdir[] = "/tmp/wwtupf_XXXXXX"; + if (mkdtemp(tmpdir) == NULL) { + fprintf(stderr, "row[%s]: mkdtemp failed\n", rows[i].label); + fail++; + continue; + } + char src[128], errf[128], cmd[2048]; + snprintf(src, sizeof src, "%s/case.ww", tmpdir); snprintf(errf, sizeof errf, "%s/err", tmpdir); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); FILE *f = fopen(src, "wb"); - if (f == NULL) { runwait(rmcmd); fail++; continue; } - fputs(rows[i].src, f); - fclose(f); + if (f == NULL) { + fprintf(stderr, "row[%s]: source open failed\n", rows[i].label); + fail++; + goto row_cleanup; + } + int writefail = fputs(rows[i].src, f) == EOF; + if (fclose(f) != 0) writefail = 1; + if (writefail) { + fprintf(stderr, "row[%s]: source write failed\n", rows[i].label); + fail++; + goto row_cleanup; + } snprintf(cmd, sizeof cmd, "%s -o /dev/null %s 2>%s", w6c_ww, src, errf); - runwait(cmd); + if (runwait(cmd) != 0) { + fprintf(stderr, "row[%s]: w6c_ww stamp probe failed\n", + rows[i].label); + fail++; + } snprintf(cmd, sizeof cmd, "grep -q asserttyped %s", errf); if (runwait(cmd) == 0) { fprintf(stderr, "row[%s]: w6c_ww emitted asserttyped " @@ -187,7 +222,19 @@ main(void) rows[i].label); fail++; } - runwait(rmcmd); + +row_cleanup: + { + int cleanfail = 0; + if (unlink(errf) != 0 && errno != ENOENT) cleanfail = 1; + if (unlink(src) != 0 && errno != ENOENT) cleanfail = 1; + if (rmdir(tmpdir) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "row[%s]: temporary cleanup failed\n", + rows[i].label); + fail++; + } + } } if (fail) { diff --git a/test/wcc/957_assert_builtin_run.c b/test/wcc/957_assert_builtin_run.c index 96b9f741..c4e8a56e 100644 --- a/test/wcc/957_assert_builtin_run.c +++ b/test/wcc/957_assert_builtin_run.c @@ -1,5 +1,5 @@ /* - * 957_assert_builtin_run — runtime + byte-id + reject net for #58: the + * 957_assert_builtin_run — stderr + byte-id net for #58: the * `assert(cond[, msg])` / `abort([msg])` builtins (the harec * EXPR_ASSERT family) in BOTH stages. * @@ -12,12 +12,9 @@ * cmd/w6c/cgen.c:6618-6663). The fix mirrors that tag-then-lower pair * into check.ww exprtype N_CALL + cgenexpr.ww cgcall. * - * THIS TEST MUST CATCH A WWSTAGE-ONLY DIVERGENCE, so every positive - * row carries both dimensions (modelled on 953/954): - * (a) cstage `ww build` + run, asserting the exit code AND the - * runtime stderr (rt_abort writes msg verbatim; a NULL want_msg - * pins the (NULL, 0) no-msg shape as EMPTY stderr). - * (b) w6c vs w6c_ww `.s` cmp — FAILS if the stages diverge. On + * Shared runtime and reject contracts live in r957_* fixtures. This carrier + * retains runtime stderr for aborting rows and w6c vs w6c_ww `.s` identity. + * The latter fails if the stages diverge. On * pre-fix master every assert/abort row diverges (wwstage emits * the bogus CALL). * @@ -26,18 +23,15 @@ * and routes the regular call path in BOTH stages. The cross-module * row pins the CURRENT flat-scope semantics — if the #45 root (filed * as task #14: cross-module bare resolution of a foreign decl) is - * later ruled a reject, that row flips to expect_fail with it. + * later ruled a reject, its fixture should change polarity with it. * - * Reject rows pin the checker mirror ON CONTENT: non-bool cond / - * non-str msg / arity overflows fail the build in BOTH stages with the - * shared diagnostic substring (cstage prefixes pos, wwstage's cerr is - * bare — strstr covers both). The alias-cond row pins the rule-10 - * down-alignment: cstage compares ty_bool by identity (check.c:1560), - * so wwstage must not alias-peel either (widening = task #5 alias arc). + * The migrated reject fixtures retain the checker diagnostics and the + * alias-cond rule-10 down-alignment. */ #include #include #include +#include #include #include #include @@ -68,8 +62,7 @@ struct row { const char *src; int want_exit; const char *want_msg; /* run stderr must contain this; NULL = must be empty */ - int expect_fail; /* 1 = BOTH stages must reject; want_exit/want_msg ignored */ - const char *diag; /* reject rows: both stages' stderr must contain this */ + int observe_output; }; static const struct row rows[] = { @@ -80,35 +73,35 @@ static const struct row rows[] = { "\tassert(1 + 1 == 2);\n" "\tassert(2 > 1, \"never fires\");\n" "\treturn 42;\n" - "};\n", 42, NULL, 0, NULL }, + "};\n", 42, NULL, 0 }, /* Failing assert without msg: rt_abort(NULL, 0), exit 1, silent. */ { "fail_nomsg", "package main;\n" "export fn main() i32 = {\n" "\tassert(1 == 2);\n" "\treturn 0;\n" - "};\n", 1, NULL, 0, NULL }, + "};\n", 1, NULL, 1 }, /* Failing assert with msg: rt_abort(ptr, len) writes it to stderr. */ { "fail_msg", "package main;\n" "export fn main() i32 = {\n" "\tassert(false, \"assert fired\\n\");\n" "\treturn 0;\n" - "};\n", 1, "assert fired", 0, NULL }, + "};\n", 1, "assert fired", 1 }, /* abort with msg: unconditional rt_abort, exit 1, msg on stderr. */ { "abort_msg", "package main;\n" "export fn main() i32 = {\n" "\tabort(\"boom\\n\");\n" "\treturn 0;\n" - "};\n", 1, "boom", 0, NULL }, + "};\n", 1, "boom", 1 }, /* bare abort(): rt_abort(NULL, 0), exit 1, silent. */ { "abort_bare", "package main;\n" "export fn main() i32 = {\n" "\tabort();\n" "\treturn 0;\n" - "};\n", 1, NULL, 0, NULL }, + "};\n", 1, NULL, 1 }, /* assert inside an IMPORTED module's fn (single-file multi-package * form, like 953) — the regex fold-5 consumer shape. */ { "imported_mod", @@ -119,7 +112,7 @@ static const struct row rows[] = { "};\n" "package main;\n" "import m;\n" - "export fn main() i32 = { return m.f(); };\n", 42, NULL, 0, NULL }, + "export fn main() i32 = { return m.f(); };\n", 42, NULL, 0 }, /* SHADOW control, same-module: a user fn `assert` suppresses the * builtin; the call routes the regular path (no-op fn), so the * false cond does NOT abort. Exit 7 proves the user fn ran. */ @@ -130,7 +123,7 @@ static const struct row rows[] = { "export fn main() i32 = {\n" "\tassert(false);\n" "\treturn g;\n" - "};\n", 7, NULL, 0, NULL }, + "};\n", 7, NULL, 0 }, /* SHADOW control, cross-module (#45 shape, task #14): a foreign * exported fn `assert` in the flat scope also suppresses the * builtin; bare `assert(false, ...)` binds it and does NOT abort. */ @@ -142,44 +135,8 @@ static const struct row rows[] = { "export fn main() i32 = {\n" "\tassert(false, \"routed to m.assert\");\n" "\treturn 42;\n" - "};\n", 42, NULL, 0, NULL }, - /* Checker rejects, mirrored both stages — pinned on diagnostic - * CONTENT, not just a non-zero exit. */ - { "reject_nonbool", - "package main;\n" - "export fn main() i32 = {\n" - "\tassert(\"not a bool\");\n" - "\treturn 0;\n" - "};\n", 0, NULL, 1, "assert: cond must be bool" }, - /* Alias-of-bool cond: ty_bool identity in cstage (check.c:1560) - * → reject; wwstage aligned down (no resolvealias peel). */ - { "reject_alias_cond", - "package main;\n" - "type myb = bool;\n" - "export fn main() i32 = {\n" - "\tlet b: myb = true;\n" - "\tassert(b);\n" - "\treturn 0;\n" - "};\n", 0, NULL, 1, "assert: cond must be bool" }, - { "reject_assert_arity", - "package main;\n" - "export fn main() i32 = {\n" - "\tassert(true, \"m\", \"extra\");\n" - "\treturn 0;\n" - "};\n", 0, NULL, 1, "assert: at most two args" }, - { "reject_abort_nonstr", - "package main;\n" - "export fn main() i32 = {\n" - "\tabort(42);\n" - "\treturn 0;\n" - "};\n", 0, NULL, 1, "abort: message must be str" }, - { "reject_abort_arity", - "package main;\n" - "export fn main() i32 = {\n" - "\tabort(\"a\", \"b\");\n" - "\treturn 0;\n" - "};\n", 0, NULL, 1, "abort: at most one arg" }, - { NULL, NULL, 0, NULL, 0, NULL } + "};\n", 42, NULL, 0 }, + { NULL, NULL, 0, NULL, 0 } }; static int @@ -224,105 +181,80 @@ main(void) char errbuf[4096]; int n = 0, fail = 0; for (int i = 0; rows[i].src; i++, n++) { - char tmpdir[64], src[128], errf[128], rmcmd[160]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/wwasrt_%d_d_%d", - getpid(), i); - mkdir(tmpdir, 0755); + char tmpdir[64] = "/tmp/wwasrt_XXXXXX"; + if (mkdtemp(tmpdir) == NULL) { + perror("assert_builtin: mkdtemp"); + fail++; + continue; + } + char src[128], errf[128], outbin[128], sepwork[160]; + char cs_s[128], ws_s[128], rmcmd[192]; snprintf(src, sizeof src, "%s/wwasrt_%d_%d.ww", tmpdir, getpid(), i); snprintf(errf, sizeof errf, "%s/wwasrt_%d_%d.err", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); - FILE *f = fopen(src, "wb"); - if (f == NULL) { runwait(rmcmd); fail++; continue; } - fputs(rows[i].src, f); - fclose(f); - - if (rows[i].expect_fail) { - char cmd[2048]; - const char *stage[2] = { w6c, w6c_ww }; - const char *sname[2] = { "cstage", "wwstage" }; - for (int s = 0; s < 2; s++) { - snprintf(cmd, sizeof cmd, - "%s -o /dev/null %s 2>%s", - stage[s], src, errf); - if (runwait(cmd) == 0) { - fprintf(stderr, "row[%s]: %s accepted, " - "expected reject\n", - rows[i].label, sname[s]); - fail++; - continue; - } - if (slurp(errf, errbuf, sizeof errbuf) < 0 || - strstr(errbuf, rows[i].diag) == NULL) { - fprintf(stderr, "row[%s]: %s rejected " - "without \"%s\" on stderr\n", - rows[i].label, sname[s], - rows[i].diag); - fail++; - } - } - runwait(rmcmd); - continue; - } - - /* (a) cstage build + run in a scratch dir. */ - char outbin[128]; snprintf(outbin, sizeof outbin, "%s/wwasrt_%d_%d", tmpdir, getpid(), i); - - char cmd[2048]; - snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s " - ">/dev/null 2>&1", bin, outbin, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: cstage build failed\n", - rows[i].label); - fail++; - runwait(rmcmd); - continue; - } - - snprintf(cmd, sizeof cmd, "%s >/dev/null 2>%s", outbin, errf); - int got = runwait(cmd); - if (got != rows[i].want_exit) { - fprintf(stderr, "row[%s]: cstage exit %d, want %d\n", - rows[i].label, got, rows[i].want_exit); - fail++; - } - long elen = slurp(errf, errbuf, sizeof errbuf); - if (rows[i].want_msg) { - if (elen < 0 || strstr(errbuf, rows[i].want_msg) == NULL) { - fprintf(stderr, "row[%s]: run stderr missing " - "\"%s\"\n", rows[i].label, - rows[i].want_msg); - fail++; - } - } else if (elen != 0) { - fprintf(stderr, "row[%s]: run stderr not empty " - "(rt_abort no-msg must be (NULL, 0))\n", - rows[i].label); - fail++; - } - - /* (b) cs==ww byte-id gate. */ - char cs_s[128], ws_s[128]; + snprintf(sepwork, sizeof sepwork, "%s.sepwork", outbin); snprintf(cs_s, sizeof cs_s, "%s/wwasrt_%d_%d_cs.s", tmpdir, getpid(), i); snprintf(ws_s, sizeof ws_s, "%s/wwasrt_%d_%d_ww.s", tmpdir, getpid(), i); + snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepwork); + FILE *f = fopen(src, "wb"); + if (f == NULL) { fail++; goto cleanup; } + fputs(rows[i].src, f); + fclose(f); + char cmd[2048]; + if (rows[i].observe_output) { + snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s " + ">/dev/null 2>&1", bin, outbin, src); + if (runwait(cmd) != 0) { + fprintf(stderr, "row[%s]: cstage build failed\n", + rows[i].label); + fail++; + goto cleanup; + } + + snprintf(cmd, sizeof cmd, "%s >/dev/null 2>%s", outbin, errf); + int got = runwait(cmd); + if (got != rows[i].want_exit) { + fprintf(stderr, "row[%s]: cstage exit %d, want %d\n", + rows[i].label, got, rows[i].want_exit); + fail++; + } + long elen = slurp(errf, errbuf, sizeof errbuf); + if (rows[i].want_msg) { + if (elen < 0 || strstr(errbuf, rows[i].want_msg) == NULL) { + fprintf(stderr, "row[%s]: run stderr missing " + "\"%s\"\n", rows[i].label, + rows[i].want_msg); + fail++; + } + } else if (elen != 0) { + fprintf(stderr, "row[%s]: run stderr not empty " + "(rt_abort no-msg must be (NULL, 0))\n", + rows[i].label); + fail++; + } + } + + /* cs==ww byte-id gate. */ snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, cs_s, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label); - fail++; runwait(rmcmd); continue; + fail++; + goto cleanup; } snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c_ww, ws_s, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c_ww failed\n", rows[i].label); - fail++; runwait(rmcmd); continue; + fail++; + goto cleanup; } if (slurp_eq(cs_s, ws_s) != 0) { fprintf(stderr, @@ -330,7 +262,22 @@ main(void) "byte-id violation)\n", rows[i].label); fail++; } - runwait(rmcmd); + +cleanup: { + int cleanbad = 0; + if (unlink(src) != 0 && errno != ENOENT) cleanbad = 1; + if (unlink(errf) != 0 && errno != ENOENT) cleanbad = 1; + if (unlink(outbin) != 0 && errno != ENOENT) cleanbad = 1; + if (unlink(cs_s) != 0 && errno != ENOENT) cleanbad = 1; + if (unlink(ws_s) != 0 && errno != ENOENT) cleanbad = 1; + if (runwait(rmcmd) != 0) cleanbad = 1; + if (rmdir(tmpdir) != 0) cleanbad = 1; + if (cleanbad) { + fprintf(stderr, "row[%s]: temporary cleanup failed\n", + rows[i].label); + fail++; + } + } } if (fail) { @@ -338,7 +285,7 @@ main(void) fail, n); return 1; } - printf("assert_builtin: %d/%d ok (cstage run+stderr + cs==ww byte-id + reject diags)\n", + printf("assert_builtin: %d/%d ok (stderr + cs==ww byte-id)\n", n, n); return 0; } diff --git a/test/wcc/958_types_sizelim_run.c b/test/wcc/958_types_sizelim_run.c index b62ac14a..4f286205 100644 --- a/test/wcc/958_types_sizelim_run.c +++ b/test/wcc/958_types_sizelim_run.c @@ -22,6 +22,7 @@ */ #include #include +#include #include #include #include @@ -98,23 +99,24 @@ main(void) int n = 0, fail = 0; for (int i = 0; rows[i].src; i++, n++) { - char tmpdir[64]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/wwslim_%d_%d", getpid(), i); - mkdir(tmpdir, 0755); - char rmcmd[128]; - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + char tmpdir[64] = "/tmp/wwslim_XXXXXX"; + if (mkdtemp(tmpdir) == NULL) { + perror("types_sizelim: mkdtemp"); + fail++; + continue; + } - char src[128]; + char src[128], outbin[128], sepwork[160], rmcmd[192]; snprintf(src, sizeof src, "%s/wwslim_%d_%d.ww", tmpdir, getpid(), i); + snprintf(outbin, sizeof outbin, "%s/out", tmpdir); + snprintf(sepwork, sizeof sepwork, "%s.sepwork", outbin); + snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepwork); FILE *f = fopen(src, "wb"); - if (f == NULL) { fail++; runwait(rmcmd); continue; } + if (f == NULL) { fail++; goto cleanup; } fputs(rows[i].src, f); fclose(f); - char outbin[128]; - snprintf(outbin, sizeof outbin, "%s/out", tmpdir); - char cmd[2048]; snprintf(cmd, sizeof cmd, "%s/ww build -I %s/lib -o %s %s", bin, cwd, outbin, src); @@ -122,8 +124,7 @@ main(void) fprintf(stderr, "row %d: build failed\n src: %s\n", i, rows[i].src); fail++; - runwait(rmcmd); - continue; + goto cleanup; } int got = runwait(outbin); @@ -132,7 +133,18 @@ main(void) i, got, rows[i].want_exit, rows[i].src); fail++; } - runwait(rmcmd); + +cleanup: { + int cleanbad = 0; + if (unlink(src) != 0 && errno != ENOENT) cleanbad = 1; + if (unlink(outbin) != 0 && errno != ENOENT) cleanbad = 1; + if (runwait(rmcmd) != 0) cleanbad = 1; + if (rmdir(tmpdir) != 0) cleanbad = 1; + if (cleanbad) { + fprintf(stderr, "row %d: temporary cleanup failed\n", i); + fail++; + } + } } if (fail) { fprintf(stderr, "%d/%d types_sizelim tests failed\n", fail, n); diff --git a/test/wcc/959_types_intlim_run.c b/test/wcc/959_types_intlim_run.c index 3f940f4c..4d4d53af 100644 --- a/test/wcc/959_types_intlim_run.c +++ b/test/wcc/959_types_intlim_run.c @@ -19,6 +19,7 @@ */ #include #include +#include #include #include #include @@ -97,24 +98,24 @@ main(void) int n = 0, fail = 0; for (int i = 0; rows[i].src; i++, n++) { - char tmpdir[64]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/wwilim_%d_%d", getpid(), i); - mkdir(tmpdir, 0755); + char tmpdir[64] = "/tmp/wwilim_XXXXXX"; + if (mkdtemp(tmpdir) == NULL) { + perror("types_intlim: mkdtemp"); + fail++; + continue; + } - char rmcmd[128]; - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); - - char src[128]; + char src[128], outbin[128], sepwork[160], rmcmd[192]; snprintf(src, sizeof src, "%s/wwilim_%d_%d.ww", tmpdir, getpid(), i); + snprintf(outbin, sizeof outbin, "%s/out", tmpdir); + snprintf(sepwork, sizeof sepwork, "%s.sepwork", outbin); + snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepwork); FILE *f = fopen(src, "wb"); - if (f == NULL) { fail++; runwait(rmcmd); continue; } + if (f == NULL) { fail++; goto cleanup; } fputs(rows[i].src, f); fclose(f); - char outbin[128]; - snprintf(outbin, sizeof outbin, "%s/out", tmpdir); - char cmd[2048]; snprintf(cmd, sizeof cmd, "%s/ww build -I %s/lib -o %s %s", bin, cwd, outbin, src); @@ -122,8 +123,7 @@ main(void) fprintf(stderr, "row %d: build failed\n src: %s\n", i, rows[i].src); fail++; - runwait(rmcmd); - continue; + goto cleanup; } int got = runwait(outbin); @@ -132,7 +132,18 @@ main(void) i, got, rows[i].want_exit, rows[i].src); fail++; } - runwait(rmcmd); + +cleanup: { + int cleanbad = 0; + if (unlink(src) != 0 && errno != ENOENT) cleanbad = 1; + if (unlink(outbin) != 0 && errno != ENOENT) cleanbad = 1; + if (runwait(rmcmd) != 0) cleanbad = 1; + if (rmdir(tmpdir) != 0) cleanbad = 1; + if (cleanbad) { + fprintf(stderr, "row %d: temporary cleanup failed\n", i); + fail++; + } + } } if (fail) { fprintf(stderr, "%d/%d types_intlim tests failed\n", fail, n); diff --git a/test/wcc/961_opaque_guards.c b/test/wcc/961_opaque_guards.c index 442bd9df..c7b278e3 100644 --- a/test/wcc/961_opaque_guards.c +++ b/test/wcc/961_opaque_guards.c @@ -1,75 +1,20 @@ /* - * 961_opaque_guards — #108 sub-fold (b): the opaque USE-GUARDS. + * 961_opaque_guards — C-stage-only guards for the unsized opaque type. * - * #108(a) made `opaque` an abstract, UNSIZED type (size = align = - * SIZE_UNDEFINED = (u64)-1), legal only behind indirection (`*opaque` - * 8B, `[]opaque` 24B header). That opened a footgun: a bare use of - * opaque where a concrete byte size is needed would fabricate a - * (u64)-1-byte slot — a silent miscompile (rule 7). This fold makes - * every such use a LOUD compile error. + * opaque is legal behind indirection (*opaque is 8 bytes and []opaque is a + * 24-byte header), but it cannot appear by value or be indexed through a + * slice. The WW frontend currently accepts the eight programs below because + * it does not validate those construction and binding sites. That polarity + * cannot be represented by the both-stage fixture protocol, so this carrier + * requires each program to fail with the C-stage driver. * - * opaque is illegal by-value in FOUR aggregate positions (array elem, - * struct field, tuple member, tagged-union variant) + as a bare value, - * under size/align, and as a []opaque element-index. The guards (cstage - * cmd/wcc/check.c), each a build-fails row: - * - * guard | misuse | message - * ------+--------------------------------+--------------------------------- - * 1 | bare local `let x: opaque` | unsized type 'opaque' cannot be a - * | param `fn f(x: opaque)` | variable / a parameter / - * | return-by-value `fn f() opaque` | a return type - * 2 | struct field `{ x: opaque }` | ... cannot be a struct field - * 3 | array elem `[N]opaque` | ... cannot be an array element - * 3t | tuple member `(opaque, i32)` | ... cannot be a tuple member - * 3u | tagged variant `(opaque|i32)` | ... cannot be a tagged union member - * 4 | size(opaque) / align(opaque) | cannot take size/align of unsized - * | | type 'opaque' - * 5 | indexing `s[i]`, s: []opaque | cannot index []opaque: element - * | | type 'opaque' has undefined size - * - * Nested aggregates (size([4]opaque), size(struct{x:opaque}), - * size((opaque,i32))) are caught transitively: the cstage rejects the - * inner aggregate at its own construction, and the wwstage size/align - * fold detects them via a RECURSIVE astunsized (an aggregate is unsized - * iff any member is). The tuple/tagged size() rows are the exact - * gate-blind miscompile the first #108(b) attempt (c9e98ca) left open — - * size((opaque,i32)) built and folded to 3. - * - * Detection is via the SIZE_UNDEFINED sentinel (the size/align the guard - * consults), so the legal sized forms `*opaque` (8B) and `[]opaque` (24B - * header) pass untouched — positive rows below + the 960 probe pin that. - * Mirrors harec's scattered `size == SIZE_UNDEFINED` guards - * (ref/harec/src/check.c:1524 binding, :3931 return-by-value, :2720 - * size-of, :384 slice-index; ref/harec/src/type_store.c:1147 tuple - * member / :449 tagged variant; field/array at the type-construction - * sites). - * - * Stage placement (rule 10, per-guard — see also the worker report): - * - Guards 1/2/3/3t/3u/5 are CSTAGE-ONLY. The wwstage check.ww is an - * AST-level approximation: it has no binding-size computation (g1), - * no type-decl field/element/member validation walk (g2/g3/3t/3u), - * and its N_INDEX `indexresult` returns the element type without - * consulting its size and is documented to defer invalid-index - * rejection to the cstage (g5). Adding twins there would mean - * building check-sites the leaner stage doesn't have — same - * cstage-only precedent as 712_redecl / 708_param_shadow_mod. - * - Guard 4 is BOTH-STAGES. The wwstage HAS the size()/align() fold - * (exprtype + astsize/astalign), which would otherwise fold opaque - * (and any opaque-containing aggregate) to a bogus 0 (a silent - * miscompile); the twin is a RECURSIVE astunsized + deffolderr, - * since the wwstage lacks the cstage's per-construction guards and - * so its fold alone must detect tuple/tagged/nested opaque uses. - * Verified by hand on w6c_ww / wwdump_ww (this cstage-driver test - * does not exercise the wwstage, like 712/960). - * - * opaque is unused by the bootstrap, so every guard is inert on the - * selfhost corpus — 990-997 stay byte-identical. - * - * Driven like 712_redecl: kind==0 rows must FAIL to build; kind==1 rows - * must build AND exit with `want`. + * Both-stage size/align rejections, including nested aggregates, are owned by + * the six r961_neg_* fixtures. The sized pointer and slice controls are + * owned by r961_pos_ptr_opaque and r961_pos_slice_opaque. */ #include #include +#include #include #include #include @@ -83,196 +28,112 @@ runwait(const char *cmd) return -1; } -/* - * kind == 0: negative — build must fail (any nonzero exit). - * kind == 1: positive — build must succeed AND binary exits with `want`. - */ -struct row { const char *label; int kind; const char *src; int want; }; +struct row { const char *label; const char *src; }; static const struct row rows[] = { - /* guard 1 — bare local. */ - { "neg_bare_local", 0, + { "neg_bare_local", "package main;\n" "export fn main() i32 = {\n" - " let x: opaque;\n" - " return 0;\n" - "};\n", - 0 }, + "\tlet x: opaque;\n" + "\treturn 0;\n" + "};\n" }, - /* guard 1 — by-value parameter. */ - { "neg_param", 0, + { "neg_param", "package main;\n" "fn f(x: opaque) i32 = { return 0; };\n" - "export fn main() i32 = { return 0; };\n", - 0 }, + "export fn main() i32 = { return 0; };\n" }, - /* guard 1 — return-by-value. */ - { "neg_return", 0, + /* + * A declaration isolates the return-type guard. A function body returning + * 0 would add an unrelated untyped_int-to-opaque rejection. + */ + { "neg_return", "package main;\n" - "fn f() opaque = { return 0; };\n" - "export fn main() i32 = { return 0; };\n", - 0 }, + "fn f() opaque;\n" + "export fn main() i32 = { return 0; };\n" }, - /* guard 2 — opaque struct field. */ - { "neg_struct_field", 0, + { "neg_struct_field", "package main;\n" "type S = struct { x: opaque };\n" - "export fn main() i32 = { return 0; };\n", - 0 }, + "export fn main() i32 = { return 0; };\n" }, - /* guard 3 — [N]opaque array element. */ - { "neg_array_elem", 0, + { "neg_array_elem", "package main;\n" "export fn main() i32 = {\n" - " let a: [4]opaque;\n" - " return 0;\n" - "};\n", - 0 }, + "\tlet a: [4]opaque;\n" + "\treturn 0;\n" + "};\n" }, - /* guard 4 — size(opaque). */ - { "neg_size_of", 0, - "package main;\n" - "export fn main() i32 = { return size(opaque): i32; };\n", - 0 }, - - /* guard 4 — align(opaque). */ - { "neg_align_of", 0, - "package main;\n" - "export fn main() i32 = { return align(opaque): i32; };\n", - 0 }, - - /* guard 5 — indexing a []opaque (legal sized header, unsized elem). - * The result is cast to i32 so this trips ONLY the index guard, not - * the bare-local guard. */ - { "neg_slice_index", 0, + /* + * The cast keeps this row on the slice-index guard rather than creating + * a bare opaque local. + */ + { "neg_slice_index", "package main;\n" "export fn main() i32 = {\n" - " let s: []opaque;\n" - " let v: i32 = s[0]: i32;\n" - " return v;\n" - "};\n", - 0 }, + "\tlet s: []opaque;\n" + "\tlet v: i32 = s[0]: i32;\n" + "\treturn v;\n" + "};\n" }, - /* guard tuple — opaque as a tuple member. The tuple type is rejected - * at construction (cstage type_store.c:1147); the wwstage twin folds - * it via the recursive astunsized. */ - { "neg_tuple_member", 0, + { "neg_tuple_member", "package main;\n" "export fn main() i32 = {\n" - " let t: (opaque, i32);\n" - " return 0;\n" - "};\n", - 0 }, + "\tlet t: (opaque, i32);\n" + "\treturn 0;\n" + "};\n" }, - /* guard tuple — size((opaque, i32)). This is the exact gate-blind - * miscompile c9e98ca left: it built + folded to 3. Now rejected. */ - { "neg_size_tuple", 0, - "package main;\n" - "export fn main() i32 = { return size((opaque, i32)): i32; };\n", - 0 }, - - /* guard tagged — opaque as a tagged-union variant (an unsized variant - * has no payload slot; cstage type_store.c:449). */ - { "neg_tagged_variant", 0, + { "neg_tagged_variant", "package main;\n" "export fn main() i32 = {\n" - " let x: (opaque | i32);\n" - " return 0;\n" - "};\n", - 0 }, - - /* guard tagged — size((opaque | i32)). */ - { "neg_size_tagged", 0, - "package main;\n" - "export fn main() i32 = { return size((opaque | i32)): i32; };\n", - 0 }, - - /* nested — size([4]opaque): the unsized leaf is one level down. Proves - * the wwstage's astunsized descends (cstage rejects at array constr). */ - { "neg_size_nested_array", 0, - "package main;\n" - "export fn main() i32 = { return size([4]opaque): i32; };\n", - 0 }, - - /* nested — size(struct { x: opaque }): an unsized field one level down. */ - { "neg_size_nested_struct", 0, - "package main;\n" - "export fn main() i32 = { return size(struct { x: opaque }): i32; };\n", - 0 }, - - /* pos control — `*opaque` is sized (8B): a local, a param, a return, - * and size()/align() of it all compile. Round-trips a real *i32. */ - { "pos_ptr_opaque", 1, - "package main;\n" - "fn id(p: *opaque) *opaque = { return p; };\n" - "export fn main() i32 = {\n" - " let n: i32 = 42;\n" - " let po: *opaque = (&n): *opaque;\n" - " let back: *i32 = id(po): *i32;\n" - " let w: i32 = size(*opaque): i32;\n" - " if (w != 8) { return 1; };\n" - " return *back;\n" - "};\n", - 42 }, - - /* pos control — `[]opaque` is sized (24B header): a local + size() - * of it compile. size([]opaque) == 24. */ - { "pos_slice_opaque", 1, - "package main;\n" - "export fn main() i32 = {\n" - " let s: []opaque;\n" - " let h: i32 = size([]opaque): i32;\n" - " if (h != 24) { return 1; };\n" - " return s.len: i32 + 7;\n" - "};\n", - 7 }, + "\tlet x: (opaque | i32);\n" + "\treturn 0;\n" + "};\n" }, }; static int run_row(const char *driver, const struct row *r, int i) { - char src[128], tmpdir[128], outbin[128], rmcmd[160], cmd[2048]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/wcopg_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); + char tmpdir[64] = "/tmp/wcopg_XXXXXX"; + if (mkdtemp(tmpdir) == NULL) { + perror("opaque-guard: mkdtemp"); + return -1; + } + char src[128], outbin[128], sepwork[160], rmcmd[192], cmd[2048]; snprintf(src, sizeof src, "%s/wcopg_%d_%d.ww", tmpdir, getpid(), i); snprintf(outbin, sizeof outbin, "%s/wcopg_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + snprintf(sepwork, sizeof sepwork, "%s.sepwork", outbin); + snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepwork); + int result = -1; FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return -1; } + if (!f) goto cleanup; fputs(r->src, f); fclose(f); snprintf(cmd, sizeof cmd, "%s build -o %s %s >/dev/null 2>&1", driver, outbin, src); int rc = runwait(cmd); + int bad = (rc == 0); + if (bad) { + fprintf(stderr, "opaque-guard[%s]: build unexpectedly succeeded\n", + r->label); + } + result = bad ? -1 : 0; - if (r->kind == 0) { - /* Negative — build must fail. */ - int bad = (rc == 0); - if (bad) { - fprintf(stderr, - "opaque-guard[%s]: build unexpectedly succeeded\n", - r->label); - } - runwait(rmcmd); - return bad ? -1 : 0; +cleanup: { + int cleanbad = 0; + if (unlink(src) != 0 && errno != ENOENT) cleanbad = 1; + if (unlink(outbin) != 0 && errno != ENOENT) cleanbad = 1; + if (runwait(rmcmd) != 0) cleanbad = 1; + if (rmdir(tmpdir) != 0) cleanbad = 1; + if (cleanbad) { + fprintf(stderr, "opaque-guard[%s]: temporary cleanup failed\n", + r->label); + result = -1; } - - /* Positive — build then run. */ - if (rc != 0) { - fprintf(stderr, "opaque-guard[%s]: build failed\n", r->label); - runwait(rmcmd); - return -1; } - int got = runwait(outbin); - runwait(rmcmd); - if (got != r->want) { - fprintf(stderr, "opaque-guard[%s]: exit=%d want=%d\n", - r->label, got, r->want); - return -1; - } - return 0; + return result; } int diff --git a/test/wcc/963_sort_run.c b/test/wcc/963_sort_run.c index 54bdbc94..1712f78a 100644 --- a/test/wcc/963_sort_run.c +++ b/test/wcc/963_sort_run.c @@ -27,6 +27,7 @@ */ #include #include +#include #include #include #include @@ -142,32 +143,31 @@ main(void) int n = 0, fail = 0; for (int i = 0; rows[i].src; i++, n++) { - char tmpdir[64]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/wwsort_%d_%d", getpid(), i); - mkdir(tmpdir, 0755); + char tmpdir[64] = "/tmp/wwsort_XXXXXX"; + if (mkdtemp(tmpdir) == NULL) { + perror("sort: mkdtemp"); + fail++; + continue; + } - char rmcmd[128]; - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); - - char src[128]; + char src[128], outbin[128], sepwork[160], rmcmd[192]; snprintf(src, sizeof src, "%s/wwsort_%d_%d.ww", tmpdir, getpid(), i); + snprintf(outbin, sizeof outbin, "%s/out", tmpdir); + snprintf(sepwork, sizeof sepwork, "%s.sepwork", outbin); + snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepwork); FILE *f = fopen(src, "wb"); - if (f == NULL) { fail++; runwait(rmcmd); continue; } + if (f == NULL) { fail++; goto cleanup; } fputs(rows[i].src, f); fclose(f); - char outbin[128]; - snprintf(outbin, sizeof outbin, "%s/out", tmpdir); - char cmd[2048]; snprintf(cmd, sizeof cmd, "%s/ww build -I %s/lib -o %s %s", bin, cwd, outbin, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: build failed\n", rows[i].label); fail++; - runwait(rmcmd); - continue; + goto cleanup; } int got = runwait(outbin); @@ -176,7 +176,19 @@ main(void) rows[i].label, got, rows[i].want_exit); fail++; } - runwait(rmcmd); + +cleanup: { + int cleanbad = 0; + if (unlink(src) != 0 && errno != ENOENT) cleanbad = 1; + if (unlink(outbin) != 0 && errno != ENOENT) cleanbad = 1; + if (runwait(rmcmd) != 0) cleanbad = 1; + if (rmdir(tmpdir) != 0) cleanbad = 1; + if (cleanbad) { + fprintf(stderr, "row[%s]: temporary cleanup failed\n", + rows[i].label); + fail++; + } + } } if (fail) { fprintf(stderr, "%d/%d sort tests failed\n", fail, n); diff --git a/test/wcc/975_dirs_run.c b/test/wcc/975_dirs_run.c index c0b11ed1..8ee91ae1 100644 --- a/test/wcc/975_dirs_run.c +++ b/test/wcc/975_dirs_run.c @@ -1,24 +1,23 @@ /* * 975_dirs_run — execute the lib/dirs @test fixture under the C-side - * `ww run` driver and assert exit 0. + * `ww test` driver and assert exit 0. * * Workflow: * 1. mkdtemp /tmp/wwdirs-XXXXXX — fresh per run, no cross-run * stale state, no parallel-test conflicts. * 2. setenv the four cohort env states (see lib/dirs/dirstest.ww * file header for the contract). - * 3. exec `ww run lib/dirs/dirstest.ww`. The fixture reads + * 3. exec `ww test lib/dirs/dirstest.ww`. The fixture reads * WW_TEST_TMPDIR + the XDG_/HOME envs and asserts. - * 4. system("rm -rf ") to reclaim the tmpdir + the - * auto-created child dirs (dirs.* mkdir-recursive on the way - * down, so the directory tree is non-empty by test exit). + * 4. Remove the exact auto-created child dirs bottom-up, then the + * mkdtemp-owned root. * - * Same wrapper shape as 970-974; cleanup-on-failure is rm-rf'd so a - * test failure never leaves residue in /tmp. + * Same wrapper shape as 970-974; cleanup runs on success and failure. */ #include #include #include +#include #include #include @@ -54,7 +53,7 @@ main(void) return 1; } - /* Pre-arrange env. The child `ww run` inherits these. */ + /* Pre-arrange env. The child `ww test` inherits these. */ char cfg[128]; snprintf(cfg, sizeof cfg, "%s/cfg", tmpl); setenv("HOME", tmpl, 1); @@ -70,15 +69,39 @@ main(void) snprintf(cmd, sizeof cmd, "%s/ww test %s", bin, path); int rc = runwait(cmd); - /* Cleanup regardless of pass/fail — no residue under /tmp. */ - char rmcmd[256]; - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpl); - (void)system(rmcmd); - + int fail = 0; if (rc != 0) { fprintf(stderr, "dirs_run FAIL: %s exited %d\n", src, rc); - return 1; + fail = 1; } + + /* dirs.* recursively creates these exact directory chains. */ + char p[256]; + int cleanbad = 0; + snprintf(p, sizeof p, "%s/cfg/myapp", tmpl); + if (rmdir(p) != 0 && errno != ENOENT) cleanbad = 1; + snprintf(p, sizeof p, "%s/cfg", tmpl); + if (rmdir(p) != 0 && errno != ENOENT) cleanbad = 1; + snprintf(p, sizeof p, "%s/.cache/myapp", tmpl); + if (rmdir(p) != 0 && errno != ENOENT) cleanbad = 1; + snprintf(p, sizeof p, "%s/.cache", tmpl); + if (rmdir(p) != 0 && errno != ENOENT) cleanbad = 1; + snprintf(p, sizeof p, "%s/.local/share/myapp", tmpl); + if (rmdir(p) != 0 && errno != ENOENT) cleanbad = 1; + snprintf(p, sizeof p, "%s/.local/share", tmpl); + if (rmdir(p) != 0 && errno != ENOENT) cleanbad = 1; + snprintf(p, sizeof p, "%s/.local/state/myapp", tmpl); + if (rmdir(p) != 0 && errno != ENOENT) cleanbad = 1; + snprintf(p, sizeof p, "%s/.local/state", tmpl); + if (rmdir(p) != 0 && errno != ENOENT) cleanbad = 1; + snprintf(p, sizeof p, "%s/.local", tmpl); + if (rmdir(p) != 0 && errno != ENOENT) cleanbad = 1; + if (rmdir(tmpl) != 0) cleanbad = 1; + if (cleanbad) { + fprintf(stderr, "dirs_run FAIL: temporary cleanup failed\n"); + fail = 1; + } + if (fail) return 1; printf("dirs_run: %s ok\n", src); return 0; } diff --git a/test/wcc/975_dirs_toolong_run.c b/test/wcc/975_dirs_toolong_run.c index 0eb5b99b..69947fdc 100644 --- a/test/wcc/975_dirs_toolong_run.c +++ b/test/wcc/975_dirs_toolong_run.c @@ -15,6 +15,7 @@ */ #include #include +#include #include #include #include @@ -43,15 +44,24 @@ static const char *PROG = static int run_stage(const char *name, const char *driver) { - char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[2048]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/dtl_%d_d", getpid()); - mkdir(tmpdir, 0755); + char tmpdir[64] = "/tmp/dtl_XXXXXX"; + if (mkdtemp(tmpdir) == NULL) { + perror("dirs_toolong: mkdtemp"); + return 1; + } + char src[128], outbin[128], sepwork[160], rmcmd[192], cmd[2048]; + char shorthome[128], shortcfg[160], shortprog[192]; snprintf(src, sizeof src, "%s/dtl_%d.ww", tmpdir, getpid()); snprintf(outbin, sizeof outbin, "%s/dtl_%d", tmpdir, getpid()); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + snprintf(sepwork, sizeof sepwork, "%s.sepwork", outbin); + snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepwork); + snprintf(shorthome, sizeof shorthome, "%s/home", tmpdir); + snprintf(shortcfg, sizeof shortcfg, "%s/.config", shorthome); + snprintf(shortprog, sizeof shortprog, "%s/prog", shortcfg); + int fail = 0; FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return 1; } + if (!f) { fail = 1; goto cleanup; } fputs(PROG, f); fclose(f); @@ -59,16 +69,14 @@ run_stage(const char *name, const char *driver) driver, outbin, src); if (runwait(cmd) != 0) { fprintf(stderr, "dirs_toolong[%s]: build failed\n", name); - runwait(rmcmd); - return 1; + fail = 1; + goto cleanup; } - int fail = 0; - /* short HOME — the normal path, must still exit 0. */ snprintf(cmd, sizeof cmd, - "env -u XDG_CONFIG_HOME HOME=/tmp/dtl_%d_home %s >/dev/null 2>&1", - getpid(), outbin); + "env -u XDG_CONFIG_HOME HOME=%s %s >/dev/null 2>&1", + shorthome, outbin); if (runwait(cmd) != 0) { fprintf(stderr, "dirs_toolong[%s]: short HOME did not exit 0\n", name); @@ -77,7 +85,7 @@ run_stage(const char *name, const char *driver) /* ~260-byte HOME — must abort loudly (non-zero) and create no dir. */ char longhome[512]; - int p = snprintf(longhome, sizeof longhome, "/tmp/dtl_%d_long_", getpid()); + int p = snprintf(longhome, sizeof longhome, "%s/long_", tmpdir); for (int i = 0; i < 260 && p < (int)sizeof longhome - 1; i++) longhome[p++] = 'a'; longhome[p] = '\0'; @@ -96,13 +104,23 @@ run_stage(const char *name, const char *driver) fprintf(stderr, "dirs_toolong[%s]: stray directory created\n", name); fail = 1; - snprintf(cmd, sizeof cmd, "rm -rf %s", longhome); - (void)system(cmd); } - runwait(rmcmd); - snprintf(cmd, sizeof cmd, "rm -rf /tmp/dtl_%d_home", getpid()); - (void)system(cmd); +cleanup: { + int cleanbad = 0; + if (unlink(src) != 0 && errno != ENOENT) cleanbad = 1; + if (unlink(outbin) != 0 && errno != ENOENT) cleanbad = 1; + if (runwait(rmcmd) != 0) cleanbad = 1; + if (rmdir(shortprog) != 0 && errno != ENOENT) cleanbad = 1; + if (rmdir(shortcfg) != 0 && errno != ENOENT) cleanbad = 1; + if (rmdir(shorthome) != 0 && errno != ENOENT) cleanbad = 1; + if (rmdir(tmpdir) != 0) cleanbad = 1; + if (cleanbad) { + fprintf(stderr, "dirs_toolong[%s]: temporary cleanup failed\n", + name); + fail = 1; + } +} return fail; } diff --git a/test/wcc/976_stat_run.c b/test/wcc/976_stat_run.c index f97bc012..4d330223 100644 --- a/test/wcc/976_stat_run.c +++ b/test/wcc/976_stat_run.c @@ -1,8 +1,8 @@ /* * 976_stat_run — execute the lib/os stat fixture under the C-side - * `ww run` driver and assert exit 0. + * `ww test` driver and assert exit 0. * - * Workflow (mirrors 975_dirs_run's mkdtemp + setenv + rm-rf shape): + * Workflow (mirrors 975_dirs_run's mkdtemp + exact-cleanup shape): * 1. mkdtemp /tmp/wwstat-XXXXXX — fresh per run, no parallel-test * contention. * 2. Populate the scratch tree: @@ -12,15 +12,15 @@ * 3. setenv WW_TEST_STAT_REGFILE / SYMLINK / SUBDIR / NOENT with * the absolute paths; lib/os/stattest.ww reads them via * os.getenv and exercises stat/lstat/fstat/exists. - * 4. exec `ww run lib/os/stattest.ww`. - * 5. system("rm -rf ") regardless of pass/fail. + * 4. exec `ww test lib/os/stattest.ww`. + * 5. Remove the exact children and then the mkdtemp-owned root. * - * Same wrapper shape as 970-975; cleanup-on-failure is rm-rf'd so a - * regression never leaves residue under /tmp. + * Same wrapper shape as 970-975; cleanup runs on success and failure. */ #include #include #include +#include #include #include #include @@ -62,46 +62,51 @@ main(void) snprintf(symlink_path, sizeof symlink_path, "%s/symlink", tmpl); snprintf(subdir, sizeof subdir, "%s/subdir", tmpl); snprintf(noent, sizeof noent, "%s/does-not-exist", tmpl); + const char *src = "lib/os/stattest.ww"; + int fail = 0, subdir_owned = 0; /* Populate. */ int fd = open(regfile, O_WRONLY | O_CREAT | O_EXCL, 0644); - if (fd < 0) { perror("open regfile"); goto cleanup_fail; } + if (fd < 0) { perror("open regfile"); fail = 1; goto cleanup; } if (write(fd, "hello world", 11) != 11) { - perror("write regfile"); close(fd); goto cleanup_fail; + perror("write regfile"); close(fd); fail = 1; goto cleanup; } close(fd); if (symlink("./regfile", symlink_path) != 0) { - perror("symlink"); goto cleanup_fail; + perror("symlink"); fail = 1; goto cleanup; } if (mkdir(subdir, 0700) != 0) { - perror("mkdir subdir"); goto cleanup_fail; + perror("mkdir subdir"); fail = 1; goto cleanup; } + subdir_owned = 1; setenv("WW_TEST_STAT_REGFILE", regfile, 1); setenv("WW_TEST_STAT_SYMLINK", symlink_path, 1); setenv("WW_TEST_STAT_SUBDIR", subdir, 1); setenv("WW_TEST_STAT_NOENT", noent, 1); - const char *src = "lib/os/stattest.ww"; char path[1024], cmd[2048]; snprintf(path, sizeof path, "%s/%s", cwd, src); snprintf(cmd, sizeof cmd, "%s/ww test %s", bin, path); int rc = runwait(cmd); - char rmcmd[256]; - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpl); - (void)system(rmcmd); - if (rc != 0) { fprintf(stderr, "stat_run FAIL: %s exited %d\n", src, rc); - return 1; + fail = 1; } + +cleanup: { + int cleanbad = 0; + if (unlink(symlink_path) != 0 && errno != ENOENT) cleanbad = 1; + if (unlink(regfile) != 0 && errno != ENOENT) cleanbad = 1; + if (subdir_owned && rmdir(subdir) != 0) cleanbad = 1; + if (rmdir(tmpl) != 0) cleanbad = 1; + if (cleanbad) { + fprintf(stderr, "stat_run FAIL: temporary cleanup failed\n"); + fail = 1; + } +} + if (fail) return 1; printf("stat_run: %s ok\n", src); return 0; - -cleanup_fail: { - char rmcmd[256]; - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpl); - (void)system(rmcmd); - return 1; -} } +} diff --git a/test/wcc/989_barefn_collide_run.c b/test/wcc/989_barefn_collide_run.c index d2b35fe6..48a12023 100644 --- a/test/wcc/989_barefn_collide_run.c +++ b/test/wcc/989_barefn_collide_run.c @@ -21,11 +21,11 @@ * (mod_collect / mod_lookup_for_fn) stays dormant-but-load-bearing for * genuinely package-less `//ww:module-reset` deps until #24b/#26. * - * Asserts (combined `ww build`, BOTH driver stages; `--sep` reproduces - * identically): + * Asserts (default separate compilation, both driver stages): * 1. Build + run, BOTH stages → exit 9 (the user's `main.run`, return 9), * never aa.run (return 5). - * 2. cs==ww (rule 10): the combined `.s` is byte-identical between the + * 2. cs==ww (rule 10): the concatenated per-package `.s` is byte-identical + * between the * two driver stages. * 3. DISTINCTNESS: the `.s` has EXACTLY ONE `TEXT main.run` (the user's) * AND EXACTLY ONE `TEXT aa.run` (the import) — distinct symbols, no dup. @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -149,24 +150,38 @@ main(void) const char *bin = absbin(); if (!bin) { fprintf(stderr, "84 FAIL: getcwd\n"); return 1; } - char td[64], cmd[8192]; - int fail = 0; - snprintf(td, sizeof td, "/tmp/wwbare84_%d", getpid()); - snprintf(cmd, sizeof cmd, "rm -rf %s", td); - runwait(cmd); - mkdir(td, 0755); + char td[] = "/tmp/wwbare84_XXXXXX"; + char cmd[8192]; + int fail = 0, aaowned = 0, aafile = 0, rootfile = 0; + int stage_started[2] = { 0, 0 }; + if (mkdtemp(td) == NULL) { + perror("wwbare84: mkdtemp"); + return 1; + } /* All paths derive from `td` (a small fixed 64-byte buffer) so the * snprintfs are provably non-truncating (warning-clean). */ char aadir[1024], aaww[1024], rootww[1024]; snprintf(aadir, sizeof aadir, "%s/aa", td); - mkdir(aadir, 0755); snprintf(aaww, sizeof aaww, "%s/aa/aa.ww", td); snprintf(rootww, sizeof rootww, "%s/root.ww", td); - if (write_file(aaww, aa_src) || write_file(rootww, root_src)) { + if (mkdir(aadir, 0755) != 0) { + perror(aadir); + fail++; + goto out; + } + aaowned = 1; + if (write_file(aaww, aa_src) != 0) { fprintf(stderr, "84 FAIL: write fixture\n"); fail++; goto out; } + aafile = 1; + if (write_file(rootww, root_src) != 0) { + fprintf(stderr, "84 FAIL: write fixture\n"); + fail++; + goto out; + } + rootfile = 1; struct { const char *drv, *tag; char prog[1024], sfile[1024]; } stg[] = { { "ww", "cs", {0}, {0} }, { "ww_ww", "ww", {0}, {0} } }; @@ -174,18 +189,20 @@ main(void) for (int s = 0; s < 2; s++) { snprintf(stg[s].prog, sizeof stg[s].prog, "%s/prog.%s", td, stg[s].tag); snprintf(stg[s].sfile, sizeof stg[s].sfile, "%s/prog.%s.s", td, stg[s].tag); - /* #93 sep layout: `--sep -o ` splits the asm across + /* #93 sep layout: `-o ` splits the asm across * .sepwork/.s — the bare user `TEXT run,` lands in - * __root.s and the imported `TEXT aa.run,` in aa.s. Concat all - * the per-unit .s (sorted glob order is deterministic) into the + * __root.s and the imported `TEXT aa.run,` in aa.s. Concat those + * exact per-unit .s files in a fixed order into the * flat .s the label-count + byte-id checks consume. The - * `-I %s` source path is preserved; WW_PKGCACHE is pinned under - * the tmpdir so the shared out/.pkgcache stays untouched. */ + * `-I %s` source path is preserved, and all outputs stay under + * the tmpdir. */ snprintf(cmd, sizeof cmd, - "WW_PKGCACHE=%s/pkgc timeout 240 %s/%s build --sep -o %s " - "-I %s %s >/dev/null 2>&1 && cat %s.sepwork/*.s > %s", - td, bin, stg[s].drv, stg[s].prog, td, rootww, - stg[s].prog, stg[s].sfile); + "timeout 240 %s/%s build -o %s " + "-I %s %s >/dev/null 2>&1 && " + "cat %s.sepwork/__root.s %s.sepwork/aa.s > %s", + bin, stg[s].drv, stg[s].prog, td, rootww, + stg[s].prog, stg[s].prog, stg[s].sfile); + stage_started[s] = 1; if (runwait(cmd) != 0) { fprintf(stderr, "84 FAIL: %s build\n", stg[s].drv); fail++; @@ -210,15 +227,36 @@ main(void) } } - /* (2) cs==ww (rule 10): the combined .s is byte-identical. */ + /* (2) cs==ww (rule 10): concatenated per-package .s is byte-identical. */ if (files_eq(stg[0].sfile, stg[1].sfile) != 0) { fprintf(stderr, "84 FAIL: cs .s != ww .s (rule 10)\n"); fail++; } out: - snprintf(cmd, sizeof cmd, "rm -rf %s", td); - runwait(cmd); + { + int cleanfail = 0; + char path[1024]; + const char *tags[] = { "cs", "ww" }; + for (int i = 0; i < 2; i++) { + if (!stage_started[i]) continue; + snprintf(path, sizeof path, "%s/prog.%s.sepwork", td, tags[i]); + snprintf(cmd, sizeof cmd, "rm -rf %s", path); + if (runwait(cmd) != 0) cleanfail = 1; + snprintf(path, sizeof path, "%s/prog.%s", td, tags[i]); + if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1; + snprintf(path, sizeof path, "%s/prog.%s.s", td, tags[i]); + if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1; + } + if (rootfile && unlink(rootww) != 0 && errno != ENOENT) cleanfail = 1; + if (aafile && unlink(aaww) != 0 && errno != ENOENT) cleanfail = 1; + if (aaowned && rmdir(aadir) != 0) cleanfail = 1; + if (rmdir(td) != 0) cleanfail = 1; + if (cleanfail) { + fprintf(stderr, "84 FAIL: temporary cleanup failed\n"); + fail++; + } + } if (fail) { fprintf(stderr, "84: %d check(s) failed\n", fail); return 1; diff --git a/test/wcc/989_c6soak_run.c b/test/wcc/989_c6soak_run.c index ca2a2f2b..a505147b 100644 --- a/test/wcc/989_c6soak_run.c +++ b/test/wcc/989_c6soak_run.c @@ -1,32 +1,27 @@ /* - * 989_c6soak_run — M3-tail commit-6 dual-path soak gate, phase-1 leg + * 989_c6soak_run — M3-tail commit-6 separate-compilation soak gate * (#46 c6, rob-c6-spec.md Tier-A/B + §3 collision). * - * The STANDING regression-gate for M4: separate-compilation must be - * behaviorally equivalent to the combined (embed-everything) build on - * real, multi-package code. The oracle is OBSERVABLE OUTPUT, not binary - * identity — a sep-linked binary and a combined-linked binary differ in - * symbol order/layout (rob-c6-spec §2 ★), so we run both and compare - * exit codes, never `cmp` the executables. + * The standing real-code regression gate for separate compilation. The + * oracle is observable output plus byte-identical per-package compiler + * artifacts across the C and WW driver stages. * - * Targets (real lib chains, cheap → phase-1): + * Targets (real library chains): * - utf8 : encoding.utf8 (DOTTED-path #57) + strings → decode + count * runes ("héllo" = 5). Exercises the multi-component import - * the combined build resolves by leaf and sep must resolve - * by the full dotted path. + * resolution by the full dotted path. * - collide: strings.contains AND bytes.contains — the SAME leaf `contains` * exported by two packages (rob-c6-spec §3, the #48/#49 class - * on real code). sep path-qualifies them to DISTINCT symbols; - * the program prints both correct results; the §3 non-vacuity + * on real code). The driver path-qualifies them to DISTINCT + * symbols; + * the program computes both correct results; the §3 non-vacuity * check below greps the per-pkg .s to prove the two labels are * genuinely distinct (disambiguation is real, not luck). * - * Asserts per target (all COLD — fresh per-stage WW_PKGCACHE, scratch - * wiped each run): - * 1. DUAL-PATH OUTPUT EQUIVALENCE (the teeth): combined-build run-exit == - * sep-build run-exit == expected, for BOTH driver stages. This is the - * M4 behavioral proof: sep emits a program that behaves identically to - * the combined one. + * Asserts per target (direct per-stage builds use fresh output stems under + * an invocation-owned root; retained scratch is inspected, then removed by + * final carrier cleanup): + * 1. RUN-EXIT: both driver stages build and run to the expected result. * 2. cs==ww (rule 10): the cstage `ww` and wwstage `ww_ww` sep-drivers emit * byte-identical per-package .s and .wwi (the .wwi enters the compare * set — rob-c6-spec). Both also link to a binary that runs to `expect`. @@ -39,14 +34,14 @@ * equivalence) is FOLDED into the 990-997 bootstrap oracle (994_w6c_ww), * not bolted on here (rob-c6-spec §2 / amendment-B). * - * Light wwstage-driver test (CLAUDE.md rule 14): all intermediates are - * `-o`-redirected to /tmp, so it is phase-1 parallel-safe. Models + * All intermediates are `-o`-redirected to /tmp for isolation. Models * 989_sepbuild_run.c / 989_sepdotpath_run.c conventions; 989 prefix per * the sep-gate precedent. */ #include #include #include +#include #include #include #include @@ -220,35 +215,25 @@ main(void) snprintf(inc, sizeof inc, "-I %s/lib -I %s/lib/ww -I %s/selfhost/cmd/wcc", cwd, cwd, cwd); - char td[64], cmd[8192]; + char td[] = "/tmp/wwc6soak_XXXXXX"; + char cmd[8192]; int fail = 0; - snprintf(td, sizeof td, "/tmp/wwc6soak_%d", getpid()); - snprintf(cmd, sizeof cmd, "rm -rf %s", td); - runwait(cmd); - mkdir(td, 0755); + int source_created[2] = { 0, 0 }; + int stage_started[2][2] = { { 0, 0 }, { 0, 0 } }; + if (mkdtemp(td) == NULL) { + perror("wwc6soak: mkdtemp"); + return 1; + } for (int i = 0; roots[i].label; i++) { struct root *r = &roots[i]; char rootww[1024]; snprintf(rootww, sizeof rootww, "%s/%s.ww", td, r->label); if (write_file(rootww, r->src)) { fail++; continue; } + source_created[i] = 1; - /* 1. combined build (cstage driver) → run. */ - char comb[1024]; - snprintf(comb, sizeof comb, "%s/%s.comb", td, r->label); - snprintf(cmd, sizeof cmd, - "timeout 240 %s/ww build %s -o %s %s >/dev/null 2>&1", - bin, inc, comb, rootww); - if (runwait(cmd) != 0) { - fprintf(stderr, "c6soak FAIL: %s combined build\n", r->label); - fail++; - continue; - } - int comb_rc = runwait(comb); - - /* sep build both stages, fresh per-stage WW_PKGCACHE so every - * package compiles COLD (a warm shared cache would skip the - * per-pkg .s/.wwi this gate inspects — mirrors 989_pkgcache). */ + /* Both stages produce every per-package + * .s/.wwi artifact this gate inspects. */ struct { const char *drv, *tag; char prog[1024]; int rc; } stg[] = { { "ww", "cs", {0}, -1 }, { "ww_ww", "ww", {0}, -1 } }; int built = 1; @@ -257,16 +242,14 @@ main(void) * `