From 912b9acef6dc35ebc6815a593fb3680db7b572b5 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Thu, 4 Jun 2026 06:50:16 +0900 Subject: [PATCH] test/804: reject-row diagnostic-text checks + delete-in-a-loop row (#35 review) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A BUILD_FAIL row now asserts the expected stderr substring on both stages (the #35 cite on the range form included) — without it a row passes vacuously on any unrelated build failure. The new delete_in_loop row drains [1,2,3,4] to empty via repeated delete(xs[0]) with base-4 positional accumulation, pinning the len bookkeeping under iteration. 38 -> 41 fixtures. --- test/wcc/804_delete_elem.c | 113 ++++++++++++++++++++++++++++--------- 1 file changed, 87 insertions(+), 26 deletions(-) diff --git a/test/wcc/804_delete_elem.c b/test/wcc/804_delete_elem.c index 27e1e306..5cf87432 100644 --- a/test/wcc/804_delete_elem.c +++ b/test/wcc/804_delete_elem.c @@ -40,12 +40,20 @@ * regex_shape | delete((*threads)[i]) behind *[]thread | * | — the fold-2b delete_thread shape | 24 * | (deref-of-local base, struct element) | + * delete_in_loop | drain [1,2,3,4] to empty via repeated | 112 + * | delete(xs[0]) — len bookkeeping under | + * | iteration, base-4 order accumulation | * reject_array | delete(t[0]) on [3]i64 — "delete must | BUILD_FAIL * | operate on a slice" | * reject_nonindex | delete(xs) — operand must be xs[i] | BUILD_FAIL - * reject_range | delete(xs[0:2]) — range form deferred | BUILD_FAIL + * reject_range | delete(xs[0:2]) — range form deferred, | BUILD_FAIL + * | message must cite task #35 | * reject_arity | delete(xs[0], xs[1]) | BUILD_FAIL * + * BUILD_FAIL rows also assert the diagnostic TEXT (stderr substring, + * both stages) — a build that fails for any other reason (parse error, + * crash) is a vacuous reject and fails the row. + * * Every non-BUILD_FAIL row also asserts cstage/wwstage asm byte-id, * which subsumes the frame canary (TEXT main,$N) and the del_l/del_e * label-counter symmetry. @@ -66,12 +74,19 @@ runwait(const char *cmd) return -1; } -/* want == BUILD_FAIL: the row must FAIL to build on both stages (the - * checker reject set is part of the contract — rule 7, never a silent - * acceptance). */ +/* want == BUILD_FAIL: the row must FAIL to build on both stages AND + * emit expect_err on stderr (the checker reject set — message text + * included, esp. the #35 cite on the deferred range form — is part of + * the contract: rule 7, never a silent acceptance; without the message + * check a row would pass vacuously on any unrelated build failure). */ #define BUILD_FAIL (-2147483647 - 1) -struct row { const char *label; const char *src; int want; }; +struct row { + const char *label; + const char *src; + int want; + const char *expect_err; /* BUILD_FAIL rows: required stderr substring */ +}; static const struct row rows[] = { { "i64_first", @@ -84,7 +99,7 @@ static const struct row rows[] = { "\tdelete(xs[0]);\n" "\treturn (xs[0] + xs[1]): i32 + len(xs)*10;\n" "};\n", - 44 }, + 44, NULL }, { "i64_middle", "package main;\n" @@ -96,7 +111,7 @@ static const struct row rows[] = { "\tdelete(xs[1]);\n" "\treturn (xs[0] + xs[1]): i32 + len(xs)*10;\n" "};\n", - 40 }, + 40, NULL }, { "i64_last", "package main;\n" @@ -108,7 +123,7 @@ static const struct row rows[] = { "\tdelete(xs[2]);\n" "\treturn (xs[0] + xs[1]): i32 + len(xs)*10;\n" "};\n", - 38 }, + 38, NULL }, { "i32_narrow", "package main;\n" @@ -120,7 +135,7 @@ static const struct row rows[] = { "\tdelete(xs[0]);\n" "\treturn xs[0] + xs[1]*10 + len(xs)*100;\n" "};\n", - 229 }, + 229, NULL }, { "u8_narrow", "package main;\n" @@ -132,7 +147,7 @@ static const struct row rows[] = { "\tdelete(xs[1]);\n" "\treturn (xs[0] + xs[1]): i32 + len(xs)*10;\n" "};\n", - 28 }, + 28, NULL }, { "cap_unchanged", "package main;\n" @@ -142,7 +157,7 @@ static const struct row rows[] = { "\tdelete(xs[1]);\n" "\treturn (xs.cap*10 + xs.len): i32 + xs[1]: i32;\n" "};\n", - 91 }, + 91, NULL }, { "delete_to_empty", "package main;\n" @@ -152,7 +167,7 @@ static const struct row rows[] = { "\tdelete(xs[0]);\n" "\treturn len(xs) + 7;\n" "};\n", - 7 }, + 7, NULL }, { "str_elem", "package main;\n" @@ -167,7 +182,7 @@ static const struct row rows[] = { "\tdelete(xs[1]);\n" "\treturn (xs[0].len*10 + xs[1].len): i32 + len(xs)*100;\n" "};\n", - 234 }, + 234, NULL }, /* 56B element: tag qword + 48B struct payload — seven whole-qword * moves; tag AND payload must both survive the raw move (a tagged @@ -200,7 +215,7 @@ static const struct row rows[] = { "\tr += bp[104]: i32;\n" "\treturn r + len(xs)*100;\n" "};\n", - 231 }, + 231, NULL }, /* The fold-2b consumer shape: regex.ha:547-551 delete_thread takes * threads: *[]thread and deletes through the pointer. ww spells the @@ -220,7 +235,29 @@ static const struct row rows[] = { "\tlet r: i32 = (xs[0].pc + xs[1].pc): i32;\n" "\treturn r + len(xs)*10;\n" "};\n", - 24 }, + 24, NULL }, + + /* Pins the len bookkeeping under iteration: each pass reads the + * new head then deletes it; base-4 positional accumulation makes + * any wrong order, double-shift, or stale len visible (a stuck + * len would never terminate; the row would time out as a wrong + * exit via the harness). 1,2,3,4 -> ((1*4+2)*4+3)*4+4 = 112. */ + { "delete_in_loop", + "package main;\n" + "export fn main() i32 = {\n" + "\tlet xs: []i64 = [];\n" + "\tappend(xs, 1);\n" + "\tappend(xs, 2);\n" + "\tappend(xs, 3);\n" + "\tappend(xs, 4);\n" + "\tlet acc: i64 = 0;\n" + "\tfor (len(xs) > 0) {\n" + "\t\tacc = acc*4 + xs[0];\n" + "\t\tdelete(xs[0]);\n" + "\t};\n" + "\treturn acc: i32 + len(xs)*1000;\n" + "};\n", + 112, NULL }, { "reject_array", "package main;\n" @@ -229,7 +266,7 @@ static const struct row rows[] = { "\tdelete(t[0]);\n" "\treturn 0;\n" "};\n", - BUILD_FAIL }, + BUILD_FAIL, "delete must operate on a slice" }, { "reject_nonindex", "package main;\n" @@ -239,7 +276,7 @@ static const struct row rows[] = { "\tdelete(xs);\n" "\treturn 0;\n" "};\n", - BUILD_FAIL }, + BUILD_FAIL, "delete: operand must be an indexing expression" }, { "reject_range", "package main;\n" @@ -250,7 +287,7 @@ static const struct row rows[] = { "\tdelete(xs[0:2]);\n" "\treturn 0;\n" "};\n", - BUILD_FAIL }, + BUILD_FAIL, "range form delete(xs[i..j]) unimplemented (task #35)" }, { "reject_arity", "package main;\n" @@ -261,15 +298,31 @@ static const struct row rows[] = { "\tdelete(xs[0], xs[1]);\n" "\treturn 0;\n" "};\n", - BUILD_FAIL }, + BUILD_FAIL, "delete: takes exactly one argument" }, }; +/* errlog_has — the build-failure stderr must carry the row's expected + * diagnostic; any other failure (parse error, crash) is a vacuous + * reject and must not pass. */ +static int +errlog_has(const char *path, const char *needle) +{ + FILE *f = fopen(path, "rb"); + if (!f) return 0; + char buf[8192]; + size_t got = fread(buf, 1, sizeof buf - 1, f); + fclose(f); + buf[got] = '\0'; + return strstr(buf, needle) != NULL; +} + static int run_driver(const char *driver, const struct row *r, int i) { - char src[64], tmpdir[64], cmd[1024]; + char src[64], tmpdir[64], errlog[80], cmd[1200]; snprintf(src, sizeof src, "/tmp/dele_%d_%d.ww", getpid(), i); snprintf(tmpdir, sizeof tmpdir, "/tmp/dele_%d_d_%d", getpid(), i); + snprintf(errlog, sizeof errlog, "%s.err", src); FILE *f = fopen(src, "wb"); if (!f) return -1; @@ -277,14 +330,22 @@ run_driver(const char *driver, const struct row *r, int i) fclose(f); mkdir(tmpdir, 0755); - snprintf(cmd, sizeof cmd, "cd %s && %s build %s 2>/dev/null", - tmpdir, driver, src); + snprintf(cmd, sizeof cmd, "cd %s && %s build %s 2>%s", + tmpdir, driver, src, errlog); if (runwait(cmd) != 0) { - if (r->want != BUILD_FAIL) + int rc = -1; + if (r->want != BUILD_FAIL) { fprintf(stderr, "row[%s]: build via %s failed\n", r->label, driver); - unlink(src); rmdir(tmpdir); - return -1; + } else if (r->expect_err && + !errlog_has(errlog, r->expect_err)) { + 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 */ + } + unlink(src); unlink(errlog); rmdir(tmpdir); + return rc; } const char *base = strrchr(src, '/'); @@ -295,7 +356,7 @@ run_driver(const char *driver, const struct row *r, int i) if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; int got = runwait(outbin); - unlink(src); unlink(outbin); rmdir(tmpdir); + unlink(src); unlink(errlog); unlink(outbin); rmdir(tmpdir); return got; }