diff --git a/test/wcc/926_tagged_sret_run.c b/test/wcc/926_tagged_sret_run.c index 37d25d06..b1069e53 100644 --- a/test/wcc/926_tagged_sret_run.c +++ b/test/wcc/926_tagged_sret_run.c @@ -356,18 +356,96 @@ static const struct row rows[] = { " return 0;\n" "};\n", 0, 0, 1 }, + /* Discard statement: rides the generic @sretscr slot; the frame + * canary pins no clobber across two discarded wide results. */ + { "wide_discard_stmt", + WIDE_TYPES + "fn mk(b: []u8) (wide | oops | nomem) = {\n" + " return wide { xs = b, ys = b, n = 1 };\n" + "};\n" + "export fn main() i32 = {\n" + " let canary: i64 = 777;\n" + " let buf: [4]u8 = [1u8, 2u8, 3u8, 4u8];\n" + " mk(buf[0:4]);\n" + " mk(buf[0:4]);\n" + " if (canary != 777) { return 1; };\n" + " return 0;\n" + "};\n", + 0, 1, 0 }, + /* LOUD-STOP: `?` consuming an sret-class call result (cstage + * gates at the N_LET TRYUNW/TRYPROP shape, wwstage at cgtryprop + * — same acceptance, rule 10). */ + { "fail_tryprop_wide", + WIDE_TYPES + "fn mk(b: []u8) (wide | oops | nomem) = {\n" + " return wide { xs = b, ys = b, n = 1 };\n" + "};\n" + "fn go(b: []u8) (i64 | oops | nomem) = {\n" + " let w: wide = mk(b)?;\n" + " return w.n;\n" + "};\n" + "export fn main() i32 = {\n" + " let buf: [4]u8 = [1u8, 2u8, 3u8, 4u8];\n" + " match (go(buf[0:4])) {\n" + " case let v: i64 => { if (v != 1) { return 1; }; };\n" + WIDE_MAIN_CHECK, + 0, 0, 1 }, + /* LOUD-STOP: `is` on an sret-class call result (AX = dest + * pointer, not the tag). */ + { "fail_typetest_wide", + WIDE_TYPES + "fn mk(b: []u8) (wide | oops | nomem) = {\n" + " return wide { xs = b, ys = b, n = 1 };\n" + "};\n" + "export fn main() i32 = {\n" + " let buf: [4]u8 = [1u8, 2u8, 3u8, 4u8];\n" + " if (mk(buf[0:4]) is wide) { return 0; };\n" + " return 1;\n" + "};\n", + 0, 0, 1 }, + /* LOUD-STOP: sret receive into a tagged GLOBAL lvalue. */ + { "fail_global_receive", + WIDE_TYPES + "let g: (wide | oops | nomem);\n" + "fn mk(b: []u8) (wide | oops | nomem) = {\n" + " return wide { xs = b, ys = b, n = 1 };\n" + "};\n" + "export fn main() i32 = {\n" + " let buf: [4]u8 = [1u8, 2u8, 3u8, 4u8];\n" + " g = mk(buf[0:4]);\n" + " match (g) {\n" + " case let w: wide => { if (w.n != 1) { return 1; }; };\n" + WIDE_MAIN_CHECK, + 0, 0, 1 }, + /* LOUD-STOP: struct-literal FIELD of tagged type from an + * sret-class call — the widener's cursor-spill #40 gate. */ + { "fail_structfield_widen", + WIDE_TYPES + "type holder = struct { r: (wide | oops | nomem), k: i64 };\n" + "fn mk(b: []u8) (wide | oops | nomem) = {\n" + " return wide { xs = b, ys = b, n = 1 };\n" + "};\n" + "export fn main() i32 = {\n" + " let buf: [4]u8 = [1u8, 2u8, 3u8, 4u8];\n" + " let h: holder = holder { r = mk(buf[0:4]), k = 5 };\n" + " match (h.r) {\n" + " case let w: wide => { if (w.n != 1) { return 1; }; };\n" + WIDE_MAIN_CHECK, + 0, 0, 1 }, }; static const char *g_bin; -/* compile one row with `tool` (w6c or w6c_ww) into outpath; returns - * the tool's exit code. */ +/* compile one row with `tool` (w6c or w6c_ww) into outpath; stderr + * goes to errpath so buildfail rows can pin the loud-stop marker. + * Returns the tool's exit code. */ static int -compile_s(const char *tool, const char *src, const char *outpath) +compile_s(const char *tool, const char *src, const char *outpath, + const char *errpath) { char cmd[1024]; - snprintf(cmd, sizeof cmd, "%s/%s %s > %s 2>/dev/null", - g_bin, tool, src, outpath); + snprintf(cmd, sizeof cmd, "%s/%s %s > %s 2> %s", + g_bin, tool, src, outpath, errpath); return runwait(cmd); } @@ -435,20 +513,25 @@ main(void) 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); FILE *f = fopen(src, "wb"); if (!f) return 1; fputs("package main;\n\n", f); fputs(r->src, f); fclose(f); - int cs_rc = compile_s("w6c", src, cs_s); - int ww_rc = compile_s("w6c_ww", src, ww_s); + 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) { @@ -456,8 +539,18 @@ main(void) "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++; @@ -466,6 +559,7 @@ main(void) "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; } if (!file_eq(cs_s, ww_s)) { @@ -505,6 +599,7 @@ main(void) } } unlink(src); unlink(cs_s); unlink(ww_s); + unlink(cs_e); unlink(ww_e); } if (fail) { fprintf(stderr, "tagged_sret_run: %d/%d rows failed\n",