checkarrlitfits' nested recursion keyed on the raw elemtn kind; a named-alias element type ([2]row, row=[2]int) arrives as N_TNAME, so the inner overlong literal skipped the count+range checks and the module static-DATA route emitted silently TRUNCATED data (ken's m7c_global: DATAW 1,2,4,5 — exit-masked once the #60 read fix removed the segv; cstage loud-rejects every spelling via its typed-literal assignability net). #105: the W1 fill gate never runs on this route, severity raised post-#60. Fix: chase elemtn through resolvealias (transitive) at the recursion gate — alias spellings of any depth take the same checks as the direct shape at all four contexts funneling through the choke point (module let / local let / def / struct-field). A direct N_TARRAY passes through resolvealias unchanged, so accepted shapes are byte-identical base→tip (m7c_global_ok + exact-fit alias field/def/2lvl probed ASM-ID vs a base scratch build). The m7/m7b local overlong rows stay loud, now via the earlier count-naming checker diagnostic instead of the cgen #270-1c fatal. The out-of-range narrow inner element louds "array element out of range" exactly as the direct spelling already did on wwstage. 808_arrlit_overlong: 37 -> 50 checks (+1 accept control alias_exact_module = ken's m7c_global_ok with a byte-id cell, +4 loud flips alias_nested_{module,2lvl,def,field} pinning per-stage texts, +1 REVIEW AMENDMENT alias_nested_local pinning the m7/m7b text move — pre-fix ww was loud via the late cgen #270-1c fatal; the row reds if the diag regresses off the checker count text). 989 ratchet zero flips — no lib module-level literal trips the gate. Filed sibling, not folded: OUTER alias-of-array overlong (let g: arr = [5 elems], arr=[4]int) still ww-silent-truncates at the alias-blind call-site N_TARRAY gates; cs louds with the count text.
520 lines
16 KiB
C
520 lines
16 KiB
C
/*
|
|
* 808_arrlit_overlong — an array literal with MORE elements than the
|
|
* declared [N] must be a loud checker reject in BOTH stages (#71).
|
|
*
|
|
* The bug: `let a: [2]u8 = [1u8, 2u8, 3u8];` was silently accepted by
|
|
* both checkers — type_assignable fails on the length mismatch, but
|
|
* arrlit_init_fits (#130 accept-if-fits) only range-checked the
|
|
* elements and never compared the literal's count against the declared
|
|
* length. cgen then stored every element at its natural offset, writing
|
|
* past the slot: stack-frame smash for locals (the `[1,2,3...]`-into-
|
|
* `[2]int` repeat form clobbered the saved BP outright), silent
|
|
* neighbour corruption for module-level DATA. The repeat-marker form
|
|
* was worse on cstage: clet's has_arr_repeat bypass skipped ALL checks
|
|
* for any `...` literal, so `[2]u8 = [999...]` also dodged the #130
|
|
* range check that wwstage already enforced.
|
|
*
|
|
* The fix (BOTH stages, one choke point each): the shared accept-if-
|
|
* fits helper (cmd/wcc/check.c arrlit_init_fits; selfhost/cmd/wcc/
|
|
* check.ww checkarrlitfits) pre-counts the literal's elements (skipping
|
|
* the `...` marker) and rejects count > N naming both counts. All four
|
|
* declaration contexts (local let / module let / def / struct-field
|
|
* literal) funnel through that helper. cstage clet's repeat bypass is
|
|
* narrowed to non-array targets so repeat literals into arrays run the
|
|
* same checks wwstage always ran.
|
|
*
|
|
* #105 (the W3 fold): a NAMED-ALIAS element type ([2]row, row=[2]int)
|
|
* dodged wwstage's nested recursion — the choke point's inner gate
|
|
* keyed on the raw elemtn kind (N_TNAME, not N_TARRAY), so the module
|
|
* static-DATA emitter silently TRUNCATED the overlong inner literal
|
|
* (exit-masked once #60 removed the read-side segv; cstage loud-
|
|
* rejects every spelling via its typed-literal assignability net).
|
|
* Fixed by chasing elemtn through resolvealias (transitive) before
|
|
* the recursion gate — alias spellings of any depth now take the same
|
|
* count + range checks as the direct shape, at all four declaration
|
|
* contexts. The alias_nested_* rows pin the flip; alias_exact_module
|
|
* is the 0/0 byte-id control (ken's m7c_global_ok).
|
|
*
|
|
* Out of scope, probed + filed separately: UNDER-long literals (no
|
|
* `...`) stay accepted in both stages (Hare rejects); `[0]`/`[_]`
|
|
* alen==0 sentinel conflation; wwstage assign/call-arg overlong
|
|
* acceptance (cstage already rejects those positions); OUTER alias-
|
|
* of-array overlong (`let g: arr = [5 elems]`, arr=[4]int) — the
|
|
* call-site N_TARRAY gates are alias-blind, ww still truncates
|
|
* (#105-sibling, filed); cstage accepting the out-of-range NESTED
|
|
* narrow element ww rejects (range net asymmetry, pre-existing on
|
|
* the direct spelling).
|
|
*
|
|
* accept rows | want
|
|
* ----------------------------------+------
|
|
* exact_local [2]int = [1,2] | runs, 0
|
|
* exact_module module-level [2] | runs, 0
|
|
* exact_def def [3] = [1,2,3] | runs, 0
|
|
* exact_field struct f=[2 elems] | runs, 0
|
|
* repeat_fill [4]int = [9...] | runs, 0
|
|
* repeat_partial [4]int = [1,2...] | runs, 0
|
|
* infer_len [_]int = [1,2,3] | runs, 0
|
|
* alias_exact_module [2]row exact | runs, 0 (#105 control)
|
|
*
|
|
* reject rows (build must FAIL on both drivers, and stderr must name
|
|
* both counts — per-stage expected substring: cstage catches the
|
|
* NESTED rows through its typed-literal assignability net instead of
|
|
* the choke-point diag, so those carry a different cstage substring)
|
|
* ----------------------------------------------
|
|
* overlong local / module / def(5-vs-3) / struct-field
|
|
* overlong repeat `[2]int = [1,2,3...]`
|
|
* overlong narrow `[2]u8 = [1u8,2u8,3u8]`
|
|
* nested module `[2][2]int = [[1,2,3],[4,5]]` (inner overlong —
|
|
* pre-fix wwstage emitted corrupted DATA: 1,2,4,5)
|
|
* nested field struct{f:[2][2]int} inner overlong
|
|
* alias nested module / 2lvl / def / field — the #105 flips: same
|
|
* inner-overlong shapes spelled through `type row = [2]int` (and
|
|
* `row2 = row`); pre-fix wwstage silently truncated (module DATA)
|
|
* or smashed frames (cstage was already loud on every row)
|
|
* alias nested local — ww pre-fix loud via the LATE cgen #270-1c
|
|
* fatal; pins the text move to the checker count diag
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/wait.h>
|
|
|
|
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; };
|
|
|
|
static const struct row rows[] = {
|
|
{ "exact_local",
|
|
"package main;\n"
|
|
"export fn main() i32 = {\n"
|
|
"\tlet a: [2]int = [1, 2];\n"
|
|
"\treturn a[1]: i32 - 2;\n"
|
|
"};\n",
|
|
0 },
|
|
|
|
{ "exact_module",
|
|
"package main;\n"
|
|
"let g: [2]int = [4, 5];\n"
|
|
"export fn main() i32 = {\n"
|
|
"\treturn g[1]: i32 - 5;\n"
|
|
"};\n",
|
|
0 },
|
|
|
|
{ "exact_def",
|
|
"package main;\n"
|
|
"def TAB: [3]int = [1, 2, 3];\n"
|
|
"export fn main() i32 = {\n"
|
|
"\treturn TAB[2]: i32 - 3;\n"
|
|
"};\n",
|
|
0 },
|
|
|
|
{ "exact_field",
|
|
"package main;\n"
|
|
"type s = struct { f: [2]int, g: int };\n"
|
|
"export fn main() i32 = {\n"
|
|
"\tlet v: s = s{ f = [6, 7], g = 8 };\n"
|
|
"\treturn v.f[1]: i32 + v.g: i32 - 15;\n"
|
|
"};\n",
|
|
0 },
|
|
|
|
{ "repeat_fill",
|
|
"package main;\n"
|
|
"export fn main() i32 = {\n"
|
|
"\tlet a: [4]int = [9...];\n"
|
|
"\treturn a[3]: i32 - 9;\n"
|
|
"};\n",
|
|
0 },
|
|
|
|
{ "repeat_partial",
|
|
"package main;\n"
|
|
"export fn main() i32 = {\n"
|
|
"\tlet a: [4]int = [1, 2...];\n"
|
|
"\treturn a[3]: i32 - 2;\n"
|
|
"};\n",
|
|
0 },
|
|
|
|
{ "infer_len",
|
|
"package main;\n"
|
|
"export fn main() i32 = {\n"
|
|
"\tlet a: [_]int = [10, 20, 30];\n"
|
|
"\treturn a.len: i32 - 3;\n"
|
|
"};\n",
|
|
0 },
|
|
|
|
/* #105 control (ken's m7c_global_ok): alias-elem module global,
|
|
* exact fit — must keep building 0/0 byte-id through the chased
|
|
* recursion gate. `N: int` cast spelling: the bare-int nested
|
|
* accept is cstage-rejected (#17), out of this row's scope. */
|
|
{ "alias_exact_module",
|
|
"package main;\n"
|
|
"type row = [2]int;\n"
|
|
"let g: [2]row = [[1: int, 2: int], [4: int, 5: int]];\n"
|
|
"export fn main() i32 = {\n"
|
|
"\tif (g[0][0] != 1) { return 1; };\n"
|
|
"\tif (g[1][1] != 5) { return 2; };\n"
|
|
"\treturn 0;\n"
|
|
"};\n",
|
|
0 },
|
|
};
|
|
|
|
/* Overlong literals — both stages must FAIL the build (the old accept
|
|
* stored every element at its natural offset: frame smash) AND the
|
|
* diagnostic must carry the expected substring (the choke-point reject
|
|
* names BOTH counts; the nested rows reach cstage's pre-existing
|
|
* assignability net instead, hence per-stage substrings). */
|
|
struct negrow {
|
|
const char *label;
|
|
const char *src;
|
|
const char *diag_c; /* expected stderr substring, cstage */
|
|
const char *diag_w; /* expected stderr substring, wwstage */
|
|
};
|
|
|
|
#define OVERLONG_3V2 "array literal has 3 elements but declared array holds 2"
|
|
|
|
static const struct negrow neg[] = {
|
|
{ "overlong_local",
|
|
"package main;\n"
|
|
"export fn main() i32 = {\n"
|
|
"\tlet a: [2]int = [1, 2, 3];\n"
|
|
"\treturn 0;\n"
|
|
"};\n",
|
|
OVERLONG_3V2, OVERLONG_3V2 },
|
|
|
|
{ "overlong_module",
|
|
"package main;\n"
|
|
"let g: [2]int = [1, 2, 3];\n"
|
|
"export fn main() i32 = { return 0; };\n",
|
|
OVERLONG_3V2, OVERLONG_3V2 },
|
|
|
|
/* counts deliberately differ from the other rows so a diag that
|
|
* hardcodes 3/2 instead of naming the real counts trips here */
|
|
{ "overlong_def",
|
|
"package main;\n"
|
|
"def TAB: [3]int = [1, 2, 3, 4, 5];\n"
|
|
"export fn main() i32 = { return 0; };\n",
|
|
"array literal has 5 elements but declared array holds 3",
|
|
"array literal has 5 elements but declared array holds 3" },
|
|
|
|
{ "overlong_field",
|
|
"package main;\n"
|
|
"type s = struct { f: [2]int };\n"
|
|
"export fn main() i32 = {\n"
|
|
"\tlet v: s = s{ f = [1, 2, 3] };\n"
|
|
"\treturn 0;\n"
|
|
"};\n",
|
|
OVERLONG_3V2, OVERLONG_3V2 },
|
|
|
|
/* repeat marker with too many explicit elements — the worst
|
|
* pre-fix case (wrote at the saved BP) */
|
|
{ "overlong_repeat",
|
|
"package main;\n"
|
|
"export fn main() i32 = {\n"
|
|
"\tlet a: [2]int = [1, 2, 3...];\n"
|
|
"\treturn 0;\n"
|
|
"};\n",
|
|
OVERLONG_3V2, OVERLONG_3V2 },
|
|
|
|
/* narrow element width (sub-8B store path) */
|
|
{ "overlong_narrow",
|
|
"package main;\n"
|
|
"export fn main() i32 = {\n"
|
|
"\tlet a: [2]u8 = [1u8, 2u8, 3u8];\n"
|
|
"\treturn 0;\n"
|
|
"};\n",
|
|
OVERLONG_3V2, OVERLONG_3V2 },
|
|
|
|
/* nested inner overlong, module DATA — pre-fix wwstage silently
|
|
* emitted 1,2,4,5; cstage rejects via assignability, wwstage via
|
|
* the recursive choke point */
|
|
{ "nested_module",
|
|
"package main;\n"
|
|
"let g: [2][2]int = [[1, 2, 3], [4, 5]];\n"
|
|
"export fn main() i32 = { return 0; };\n",
|
|
"not assignable", OVERLONG_3V2 },
|
|
|
|
{ "nested_field",
|
|
"package main;\n"
|
|
"type s = struct { f: [2][2]int };\n"
|
|
"export fn main() i32 = {\n"
|
|
"\tlet v: s = s{ f = [[1, 2, 3], [4, 5]] };\n"
|
|
"\treturn 0;\n"
|
|
"};\n",
|
|
"not assignable", OVERLONG_3V2 },
|
|
|
|
/* #105 flips: alias-elem inner overlong, the four declaration
|
|
* contexts through the one (now chased) choke point. Pre-fix
|
|
* wwstage built all four silently — the module row emitted
|
|
* truncated DATA (1,2,4,5; ken's m7c_global, exit-masked since
|
|
* #60). cstage was already loud on each via assignability. */
|
|
{ "alias_nested_module",
|
|
"package main;\n"
|
|
"type row = [2]int;\n"
|
|
"let g: [2]row = [[1: int, 2: int, 3: int], [4: int, 5: int]];\n"
|
|
"export fn main() i32 = { return 0; };\n",
|
|
"not assignable", OVERLONG_3V2 },
|
|
|
|
/* 2-level alias — pins the transitive chase */
|
|
{ "alias_nested_2lvl",
|
|
"package main;\n"
|
|
"type row = [2]int;\n"
|
|
"type row2 = row;\n"
|
|
"let g: [2]row2 = [[1: int, 2: int, 3: int], [4: int, 5: int]];\n"
|
|
"export fn main() i32 = { return 0; };\n",
|
|
"not assignable", OVERLONG_3V2 },
|
|
|
|
{ "alias_nested_def",
|
|
"package main;\n"
|
|
"type row = [2]int;\n"
|
|
"def TAB: [2]row = [[1: int, 2: int, 3: int], [4: int, 5: int]];\n"
|
|
"export fn main() i32 = { return 0; };\n",
|
|
"not assignable", OVERLONG_3V2 },
|
|
|
|
/* local let — ww was ALREADY loud pre-fix, but via the cgen
|
|
* #270-1c aggregate-element fatal; the chase moves it to the
|
|
* checker count diag (ken's m7/m7b text move). This row pins the
|
|
* new text so a regression back to the late generic fatal reds. */
|
|
{ "alias_nested_local",
|
|
"package main;\n"
|
|
"type row = [2]int;\n"
|
|
"export fn main() i32 = {\n"
|
|
"\tlet g: [2]row = [[1: int, 2: int, 3: int], [4: int, 5: int]];\n"
|
|
"\treturn 0;\n"
|
|
"};\n",
|
|
"not assignable", OVERLONG_3V2 },
|
|
|
|
{ "alias_nested_field",
|
|
"package main;\n"
|
|
"type row = [2]int;\n"
|
|
"type s = struct { f: [2]row };\n"
|
|
"export fn main() i32 = {\n"
|
|
"\tlet v: s = s{ f = [[1: int, 2: int, 3: int], [4: int, 5: int]] };\n"
|
|
"\treturn 0;\n"
|
|
"};\n",
|
|
"not assignable", OVERLONG_3V2 },
|
|
};
|
|
|
|
static int
|
|
run_driver(const char *driver, const struct row *r, int i)
|
|
{
|
|
char src[64], tmpdir[64], cmd[1024];
|
|
snprintf(src, sizeof src, "/tmp/aol_%d_%d.ww", getpid(), i);
|
|
snprintf(tmpdir, sizeof tmpdir, "/tmp/aol_%d_d_%d", getpid(), i);
|
|
|
|
FILE *f = fopen(src, "wb");
|
|
if (!f) return -1;
|
|
fputs(r->src, f);
|
|
fclose(f);
|
|
|
|
mkdir(tmpdir, 0755);
|
|
snprintf(cmd, sizeof cmd, "cd %s && %s build %s 2>/dev/null",
|
|
tmpdir, driver, src);
|
|
if (runwait(cmd) != 0) {
|
|
fprintf(stderr, "row[%s]: build via %s failed\n",
|
|
r->label, driver);
|
|
unlink(src); rmdir(tmpdir);
|
|
return -1;
|
|
}
|
|
|
|
const char *base = strrchr(src, '/');
|
|
base = base ? base + 1 : src;
|
|
char outbin[128];
|
|
snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base);
|
|
char *dot = strrchr(outbin, '.');
|
|
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
|
|
int got = runwait(outbin);
|
|
|
|
unlink(src); unlink(outbin); rmdir(tmpdir);
|
|
return got;
|
|
}
|
|
|
|
/* build_should_fail — an overlong literal must error on `driver` AND
|
|
* the diagnostic must contain `diag`; returns 0 when the build
|
|
* correctly FAILS with the expected text, non-zero otherwise. */
|
|
static int
|
|
build_should_fail(const char *dname, const char *driver,
|
|
const struct negrow *r, const char *diag, int i)
|
|
{
|
|
char s[64], tmpdir[64], errf[64], cmd[1024];
|
|
snprintf(s, sizeof s, "/tmp/aoln_%d_%d.ww", getpid(), i);
|
|
snprintf(tmpdir, sizeof tmpdir, "/tmp/aoln_%d_d_%d", getpid(), i);
|
|
snprintf(errf, sizeof errf, "/tmp/aoln_%d_%d.err", getpid(), i);
|
|
|
|
FILE *f = fopen(s, "wb");
|
|
if (!f) return -1;
|
|
fputs(r->src, f);
|
|
fclose(f);
|
|
|
|
mkdir(tmpdir, 0755);
|
|
snprintf(cmd, sizeof cmd, "cd %s && %s build %s 2>%s",
|
|
tmpdir, driver, s, errf);
|
|
int rc = runwait(cmd);
|
|
unlink(s);
|
|
/* clean any emitted binary */
|
|
const char *base = strrchr(s, '/');
|
|
base = base ? base + 1 : s;
|
|
char outbin[128];
|
|
snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base);
|
|
char *dot = strrchr(outbin, '.');
|
|
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
|
|
unlink(outbin);
|
|
rmdir(tmpdir);
|
|
|
|
int bad = 0;
|
|
if (rc == 0) {
|
|
fprintf(stderr, "arrlit_overlong[%s][%s]: built ok, "
|
|
"expected a loud error\n", dname, r->label);
|
|
bad = 1;
|
|
} else {
|
|
char ebuf[4096];
|
|
size_t n = 0;
|
|
FILE *ef = fopen(errf, "rb");
|
|
if (ef) {
|
|
n = fread(ebuf, 1, sizeof ebuf - 1, ef);
|
|
fclose(ef);
|
|
}
|
|
ebuf[n] = '\0';
|
|
if (strstr(ebuf, diag) == NULL) {
|
|
fprintf(stderr, "arrlit_overlong[%s][%s]: rejected "
|
|
"but diagnostic lacks \"%s\"; got: %s\n",
|
|
dname, r->label, diag, ebuf);
|
|
bad = 1;
|
|
}
|
|
}
|
|
unlink(errf);
|
|
return bad;
|
|
}
|
|
|
|
/* 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/aol_asm_%d_%d.ww", getpid(), i);
|
|
snprintf(cs, sizeof cs, "/tmp/aol_asm_%d_%d_c.s", getpid(), i);
|
|
snprintf(ws, sizeof ws, "/tmp/aol_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);
|
|
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);
|
|
if (runwait(cmd) != 0) {
|
|
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
|
|
unlink(src); unlink(cs);
|
|
return -1;
|
|
}
|
|
|
|
FILE *fc = fopen(cs, "rb");
|
|
FILE *fw = fopen(ws, "rb");
|
|
int rc = 0;
|
|
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, "row[%s]: cstage vs wwstage asm differs\n",
|
|
r->label);
|
|
unlink(src); unlink(cs); unlink(ws);
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
main(void)
|
|
{
|
|
const char *bin = getenv("BIN");
|
|
if (!bin) bin = "out/bin";
|
|
char absbin[1024];
|
|
if (bin[0] != '/') {
|
|
char cwd[1024];
|
|
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
|
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 },
|
|
};
|
|
|
|
int n = (int)(sizeof rows / sizeof rows[0]);
|
|
int nn = (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, "arrlit_overlong: 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,
|
|
"arrlit_overlong[%s][%s]: exit=%d want=%d\n",
|
|
drivers[d].name, rows[i].label,
|
|
got, rows[i].want);
|
|
fail++;
|
|
}
|
|
}
|
|
for (int i = 0; i < nn; i++) {
|
|
total++;
|
|
const char *diag =
|
|
strcmp(drivers[d].name, "cstage") == 0
|
|
? neg[i].diag_c : neg[i].diag_w;
|
|
if (build_should_fail(drivers[d].name,
|
|
drivers[d].path, &neg[i], diag, 100 + i) != 0)
|
|
fail++;
|
|
}
|
|
}
|
|
|
|
if (access(wdrv, X_OK) == 0) {
|
|
for (int i = 0; i < n; i++) {
|
|
total++;
|
|
if (asm_byte_identical(bin, &rows[i], i) != 0)
|
|
fail++;
|
|
}
|
|
}
|
|
|
|
if (fail) {
|
|
fprintf(stderr,
|
|
"arrlit_overlong: %d/%d fixtures failed\n", fail, total);
|
|
return 1;
|
|
}
|
|
printf("arrlit_overlong: %d/%d ok\n", total, total);
|
|
return 0;
|
|
}
|