test/926: pin #38b/#40 loud-stop markers + 5 more consumption-shape rows (#38 review)

Buildfail rows previously accepted ANY non-zero compiler exit — an
unrelated checker error would masquerade as loud-stop coverage; now
the stderr must carry the #38b/#40 marker on BOTH stages. New rows:
wide_discard_stmt (generic @sretscr discard, frame canary) and four
loud-stop shapes verified by probe during review — `?` consumption,
`is` consumption, tagged-GLOBAL receive, struct-literal tagged field
from an sret-class call (the widener #40 gate).
This commit is contained in:
2026-06-04 04:01:16 +09:00
parent 147a8a26b9
commit 8999b59ab0

View File

@@ -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",