test/804: reject-row diagnostic-text checks + delete-in-a-loop row (#35 review)
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.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user