wcc/cgen: zero-init sub-8-byte bare lets, both stages; bytes test honest (#16-team)

A bare 'let x: T;' with 1 <= size(T) <= 7 matched no zero-fill arm in
either stage (8B and >8B were already zeroed) - 'let c: [3]u8;' read
stack garbage. User-ruled zero-value semantics: cstage gate sz>8 ->
sz>0; wwstage zsz==8 arm hoisted above the fill-run arm (required -
8B would otherwise route into the run and diverge) and run gate
zsz>0. New 840 pin: dirty-frame probe rows, dual-dim (run + cs/ww
byte-id); discriminators fail exit-154 on pre-fix binaries.

Fused with the lib/bytes test conversion (rule 11): either half alone
turns 967 red. The old exit(signalled+10) wrapped a real 1782-count
ltrim failure to exit 0 - green depended on the garbage. Converted to
assert form (completes the 35/35 @test conversion); ltrim rows keep
the bare 'let c: [3]u8;' as the consumer proof of the fix.
This commit is contained in:
2026-06-10 20:08:58 +09:00
parent 3e2bf0612e
commit 5b212e51cf
7 changed files with 527 additions and 276 deletions

View File

@@ -440,6 +440,7 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \
$(BIN)/test_litstr_pseudo_run \ $(BIN)/test_litstr_pseudo_run \
$(BIN)/test_lenidx_run \ $(BIN)/test_lenidx_run \
$(BIN)/test_globalidx_run \ $(BIN)/test_globalidx_run \
$(BIN)/test_zeroinit_run \
$(BIN)/test_tuple_sret_callee \ $(BIN)/test_tuple_sret_callee \
$(BIN)/test_tuple_sret_receive_run \ $(BIN)/test_tuple_sret_receive_run \
$(BIN)/test_append_wide_elem \ $(BIN)/test_append_wide_elem \
@@ -1207,6 +1208,16 @@ $(BIN)/test_globalidx_run: test/wcc/803_globalidx_run.c \
$(LIB)/libwwrt.a | $(BIN) $(LIB)/libwwrt.a | $(BIN)
$(CC) $(CFLAGS) -o $@ $< $(CC) $(CFLAGS) -o $@ $<
# #16: a bare `let x: T;` (no rhs) must zero-fill its slot. cgen emitted
# the zero-fill only for 8B-primitive and >8B-composite slots — a SUB-8
# aggregate (`let c: [3]u8;`) fell through to NOTHING and read stack
# garbage (cstage == wwstage, gate-blind; ken's bytes-967 root cause).
# Runtime (cstage dirty()→probe()) + cs==ww byte-id, both dimensions/row.
$(BIN)/test_zeroinit_run: test/wcc/840_zeroinit_run.c \
$(BIN)/ww $(BIN)/w6c $(BIN)/w6c_ww $(BIN)/w6a $(BIN)/w6l \
$(LIB)/libwwrt.a | $(BIN)
$(CC) $(CFLAGS) -o $@ $<
# #10 Fold A (wide tuple-return / sret, CALLEE side): an over-cap tuple # #10 Fold A (wide tuple-return / sret, CALLEE side): an over-cap tuple
# return (> 4 GP or > 2 SSE eightbytes) now compiles via sret instead of # return (> 4 GP or > 2 SSE eightbytes) now compiles via sret instead of
# loud-stopping at the SEND. Compile + cs==ww byte-id only — the receive # loud-stopping at the SEND. Compile + cs==ww byte-id only — the receive

View File

@@ -12896,11 +12896,11 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
} }
} else if (sz == 8) { } else if (sz == 8) {
ins2(c, A_MOVQ, aimm(0), amem(D_BP, off)); ins2(c, A_MOVQ, aimm(0), amem(D_BP, off));
} else if (!n->rhs && sz > 8) { } else if (!n->rhs && sz > 0) {
/* `let x: T;` with no rhs for a multi-word composite /* `let x: T;` with no rhs for a composite (str/slice/
* (str/slice/tuple/struct/tagged/ARRAY). Zero the slot so * tuple/struct/tagged/ARRAY). Zero the slot so reads after
* reads after the bare let see {0...} rather than * the bare let see {0...} rather than whatever the stack
* whatever the stack already held. * already held.
* *
* #84: arrays were excluded here (`!TY_ARRAY`), so a * #84: arrays were excluded here (`!TY_ARRAY`), so a
* dirtied-stack `let a: [3]int;` read garbage — BOTH * dirtied-stack `let a: [3]int;` read garbage — BOTH
@@ -12909,7 +12909,17 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
* composite. Extent is lu->size (chased ABI size, rule-13 * composite. Extent is lu->size (chased ABI size, rule-13
* — never a hardcoded count×elemsize). The unrolled * — never a hardcoded count×elemsize). The unrolled
* word/dword/byte run mirrors the composite path; the * word/dword/byte run mirrors the composite path; the
* largest real local array ([256]u8) is 32 MOVQs. */ * largest real local array ([256]u8) is 32 MOVQs.
*
* #16: the gate was `sz > 8`, so a SUB-8 aggregate
* (`let c: [3]u8;` = 3, a 3-byte struct, etc.) matched
* neither this arm nor the `sz == 8` MOVQ-$0 arm above and
* fell through to NOTHING — the exact stack-garbage read
* ken's bytes verdict pinpointed (ltrim_cases' `let c:
* [3]u8;`). Widening to `sz > 0` routes 1..7-byte slots
* through the same MOVL/MOVB tail; the run already sizes
* itself to any extent. (sz == 8 stays on the immediate
* MOVQ $0 above; sz == 0 — `[0]T` — needs no stores.) */
ins2(c, A_XORQ, areg(D_AX), areg(D_AX)); ins2(c, A_XORQ, areg(D_AX), areg(D_AX));
int zi = 0; int zi = 0;
while (zi + 8 <= sz) { while (zi + 8 <= sz) {
@@ -12928,7 +12938,6 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
zi += 1; zi += 1;
} }
} }
/* arrays left uninitialised — caller writes via index */
letlink: letlink:
/* #152: link the binding into the lookup chain AFTER its /* #152: link the binding into the lookup chain AFTER its
* initializer emits, so a self-shadowing init (`let x = * initializer emits, so a self-shadowing init (`let x =

View File

@@ -1,7 +1,6 @@
// bytestest — exercises lib/bytes. Run with // bytestest — exercises lib/bytes. Run with
// `out/bin/ww run lib/bytes/bytestest.ww`. Same signalled-then- // `out/bin/ww run lib/bytes/bytestest.ww`. A failing row aborts via the
// fail()-with-+10 pattern as hex / utf8 / time tests: non-zero exit // assert/abort builtin (task #5 @test conversion).
// pinpoints the failing scenario.
// //
// Vectors mirror Hare's @test fns in ref/hare/bytes/equal.ha, // Vectors mirror Hare's @test fns in ref/hare/bytes/equal.ha,
// ref/hare/bytes/index.ha, ref/hare/bytes/contains.ha. // ref/hare/bytes/index.ha, ref/hare/bytes/contains.ha.
@@ -11,9 +10,6 @@ package bytes_test;
import bytes; import bytes;
import os; import os;
let signalled: i32 = 0;
fn fail() void = { os.exit(signalled + 10); };
// ---- equal ------------------------------------------------------------ // ---- equal ------------------------------------------------------------
// ref/hare/bytes/equal.ha:21. // ref/hare/bytes/equal.ha:21.
@@ -24,11 +20,11 @@ fn fail() void = { os.exit(signalled + 10); };
let d: [4]u8; d[0] = 1u8; d[1] = 2u8; d[2] = 3u8; d[3] = 4u8; let d: [4]u8; d[0] = 1u8; d[1] = 2u8; d[2] = 3u8; d[3] = 4u8;
let e: [2]u8; e[0] = 1u8; e[1] = 2u8; let e: [2]u8; e[0] = 1u8; e[1] = 2u8;
let z: [1]u8; let z: [1]u8;
if (!bytes.equal(a[0:3], b[0:3])) { fail(); }; assert(!(!bytes.equal(a[0:3], b[0:3])));
if ( bytes.equal(a[0:3], c[0:3])) { fail(); }; assert(!( bytes.equal(a[0:3], c[0:3])));
if ( bytes.equal(a[0:3], d[0:4])) { fail(); }; assert(!( bytes.equal(a[0:3], d[0:4])));
if ( bytes.equal(a[0:3], e[0:2])) { fail(); }; assert(!( bytes.equal(a[0:3], e[0:2])));
if (!bytes.equal(z[0:0], z[0:0])) { fail(); }; // empty-empty assert(!(!bytes.equal(z[0:0], z[0:0]))); // empty-empty
}; };
// ---- index(u8) -------------------------------------------------------- // ---- index(u8) --------------------------------------------------------
@@ -37,24 +33,24 @@ fn fail() void = { os.exit(signalled + 10); };
@test fn index_byte_cases() void = { @test fn index_byte_cases() void = {
let a: [4]u8; a[0] = 1u8; a[1] = 3u8; a[2] = 3u8; a[3] = 7u8; let a: [4]u8; a[0] = 1u8; a[1] = 3u8; a[2] = 3u8; a[3] = 7u8;
match (bytes.index(a[0:4], 1u8)) { match (bytes.index(a[0:4], 1u8)) {
case let i: i32 => { if (i != 0) { fail(); }; }; case let i: i32 => { assert(!(i != 0)); };
case void => { fail(); }; case void => abort();
}; };
match (bytes.index(a[0:4], 3u8)) { match (bytes.index(a[0:4], 3u8)) {
case let i: i32 => { if (i != 1) { fail(); }; }; case let i: i32 => { assert(!(i != 1)); };
case void => { fail(); }; case void => abort();
}; };
match (bytes.index(a[0:4], 7u8)) { match (bytes.index(a[0:4], 7u8)) {
case let i: i32 => { if (i != 3) { fail(); }; }; case let i: i32 => { assert(!(i != 3)); };
case void => { fail(); }; case void => abort();
}; };
match (bytes.index(a[0:4], 42u8)) { match (bytes.index(a[0:4], 42u8)) {
case let i: i32 => { fail(); }; case let i: i32 => abort();
case void => void; case void => void;
}; };
let z: [1]u8; let z: [1]u8;
match (bytes.index(z[0:0], 42u8)) { match (bytes.index(z[0:0], 42u8)) {
case let i: i32 => { fail(); }; case let i: i32 => abort();
case void => void; case void => void;
}; };
}; };
@@ -67,22 +63,22 @@ fn fail() void = { os.exit(signalled + 10); };
let h1: [4]u8; h1[0] = 1u8; h1[1] = 42u8; h1[2] = 24u8; h1[3] = 0u8; let h1: [4]u8; h1[0] = 1u8; h1[1] = 42u8; h1[2] = 24u8; h1[3] = 0u8;
let n1: [2]u8; n1[0] = 42u8; n1[1] = 24u8; let n1: [2]u8; n1[0] = 42u8; n1[1] = 24u8;
match (bytes.index(h1[0:3], n1[0:2])) { match (bytes.index(h1[0:3], n1[0:2])) {
case let i: i32 => { if (i != 1) { fail(); }; }; case let i: i32 => { assert(!(i != 1)); };
case void => { fail(); }; case void => abort();
}; };
let h2: [4]u8; h2[0] = 1u8; h2[1] = 3u8; h2[2] = 3u8; h2[3] = 7u8; let h2: [4]u8; h2[0] = 1u8; h2[1] = 3u8; h2[2] = 3u8; h2[3] = 7u8;
let n2: [2]u8; n2[0] = 3u8; n2[1] = 3u8; let n2: [2]u8; n2[0] = 3u8; n2[1] = 3u8;
match (bytes.index(h2[0:4], n2[0:2])) { match (bytes.index(h2[0:4], n2[0:2])) {
case let i: i32 => { if (i != 1) { fail(); }; }; case let i: i32 => { assert(!(i != 1)); };
case void => { fail(); }; case void => abort();
}; };
// needle longer than haystack — void // needle longer than haystack — void
let h3: [3]u8; h3[0] = 1u8; h3[1] = 2u8; h3[2] = 3u8; let h3: [3]u8; h3[0] = 1u8; h3[1] = 2u8; h3[2] = 3u8;
let n3: [4]u8; n3[0] = 1u8; n3[1] = 2u8; n3[2] = 3u8; n3[3] = 4u8; let n3: [4]u8; n3[0] = 1u8; n3[1] = 2u8; n3[2] = 3u8; n3[3] = 4u8;
match (bytes.index(h3[0:3], n3[0:4])) { match (bytes.index(h3[0:3], n3[0:4])) {
case let i: i32 => { fail(); }; case let i: i32 => abort();
case void => void; case void => void;
}; };
@@ -90,15 +86,15 @@ fn fail() void = { os.exit(signalled + 10); };
let h4: [2]u8; h4[0] = 42u8; h4[1] = 20u8; let h4: [2]u8; h4[0] = 42u8; h4[1] = 20u8;
let n4: [2]u8; n4[0] = 42u8; n4[1] = 20u8; let n4: [2]u8; n4[0] = 42u8; n4[1] = 20u8;
match (bytes.index(h4[0:2], n4[0:2])) { match (bytes.index(h4[0:2], n4[0:2])) {
case let i: i32 => { if (i != 0) { fail(); }; }; case let i: i32 => { assert(!(i != 0)); };
case void => { fail(); }; case void => abort();
}; };
// len(haystack) == len(needle), no match — void // len(haystack) == len(needle), no match — void
let h5: [4]u8; h5[0] = 1u8; h5[1] = 1u8; h5[2] = 1u8; h5[3] = 2u8; let h5: [4]u8; h5[0] = 1u8; h5[1] = 1u8; h5[2] = 1u8; h5[3] = 2u8;
let n5: [4]u8; n5[0] = 1u8; n5[1] = 1u8; n5[2] = 1u8; n5[3] = 3u8; let n5: [4]u8; n5[0] = 1u8; n5[1] = 1u8; n5[2] = 1u8; n5[3] = 3u8;
match (bytes.index(h5[0:4], n5[0:4])) { match (bytes.index(h5[0:4], n5[0:4])) {
case let i: i32 => { fail(); }; case let i: i32 => abort();
case void => void; case void => void;
}; };
@@ -108,15 +104,15 @@ fn fail() void = { os.exit(signalled + 10); };
let h6: [4]u8; h6[0] = 1u8; h6[1] = 1u8; h6[2] = 1u8; h6[3] = 2u8; let h6: [4]u8; h6[0] = 1u8; h6[1] = 1u8; h6[2] = 1u8; h6[3] = 2u8;
let n6s: [3]u8; n6s[0] = 1u8; n6s[1] = 1u8; n6s[2] = 2u8; let n6s: [3]u8; n6s[0] = 1u8; n6s[1] = 1u8; n6s[2] = 2u8;
match (bytes.index(h6[0:4], n6s[0:3])) { match (bytes.index(h6[0:4], n6s[0:3])) {
case let i: i32 => { if (i != 1) { fail(); }; }; case let i: i32 => { assert(!(i != 1)); };
case void => { fail(); }; case void => abort();
}; };
// Same shape, longer haystack with no match anywhere. // Same shape, longer haystack with no match anywhere.
let h7: [5]u8; h7[0] = 1u8; h7[1] = 1u8; h7[2] = 1u8; h7[3] = 3u8; h7[4] = 2u8; let h7: [5]u8; h7[0] = 1u8; h7[1] = 1u8; h7[2] = 1u8; h7[3] = 3u8; h7[4] = 2u8;
let n7: [4]u8; n7[0] = 1u8; n7[1] = 1u8; n7[2] = 1u8; n7[3] = 2u8; let n7: [4]u8; n7[0] = 1u8; n7[1] = 1u8; n7[2] = 1u8; n7[3] = 2u8;
match (bytes.index(h7[0:5], n7[0:4])) { match (bytes.index(h7[0:5], n7[0:4])) {
case let i: i32 => { fail(); }; case let i: i32 => abort();
case void => void; case void => void;
}; };
@@ -124,20 +120,20 @@ fn fail() void = { os.exit(signalled + 10); };
let z: [1]u8; let z: [1]u8;
let zn: [1]u8; let zn: [1]u8;
match (bytes.index(h2[0:4], zn[0:0])) { match (bytes.index(h2[0:4], zn[0:0])) {
case let i: i32 => { if (i != 0) { fail(); }; }; case let i: i32 => { assert(!(i != 0)); };
case void => { fail(); }; case void => abort();
}; };
// empty haystack, non-empty needle — void // empty haystack, non-empty needle — void
match (bytes.index(z[0:0], n3[0:3])) { match (bytes.index(z[0:0], n3[0:3])) {
case let i: i32 => { fail(); }; case let i: i32 => abort();
case void => void; case void => void;
}; };
// single-byte slice needle — should semantically equal u8 arm // single-byte slice needle — should semantically equal u8 arm
let n6: [1]u8; n6[0] = 7u8; let n6: [1]u8; n6[0] = 7u8;
match (bytes.index(h2[0:4], n6[0:1])) { match (bytes.index(h2[0:4], n6[0:1])) {
case let i: i32 => { if (i != 3) { fail(); }; }; case let i: i32 => { assert(!(i != 3)); };
case void => { fail(); }; case void => abort();
}; };
}; };
@@ -147,16 +143,16 @@ fn fail() void = { os.exit(signalled + 10); };
@test fn rindex_byte_cases() void = { @test fn rindex_byte_cases() void = {
let a: [4]u8; a[0] = 1u8; a[1] = 3u8; a[2] = 3u8; a[3] = 7u8; let a: [4]u8; a[0] = 1u8; a[1] = 3u8; a[2] = 3u8; a[3] = 7u8;
match (bytes.rindex(a[0:4], 3u8)) { match (bytes.rindex(a[0:4], 3u8)) {
case let i: i32 => { if (i != 2) { fail(); }; }; case let i: i32 => { assert(!(i != 2)); };
case void => { fail(); }; case void => abort();
}; };
match (bytes.rindex(a[0:4], 42u8)) { match (bytes.rindex(a[0:4], 42u8)) {
case let i: i32 => { fail(); }; case let i: i32 => abort();
case void => void; case void => void;
}; };
let z: [1]u8; let z: [1]u8;
match (bytes.rindex(z[0:0], 42u8)) { match (bytes.rindex(z[0:0], 42u8)) {
case let i: i32 => { fail(); }; case let i: i32 => abort();
case void => void; case void => void;
}; };
}; };
@@ -169,18 +165,18 @@ fn fail() void = { os.exit(signalled + 10); };
let a: [4]u8; a[0] = 1u8; a[1] = 1u8; a[2] = 1u8; a[3] = 2u8; let a: [4]u8; a[0] = 1u8; a[1] = 1u8; a[2] = 1u8; a[3] = 2u8;
let n11: [2]u8; n11[0] = 1u8; n11[1] = 1u8; let n11: [2]u8; n11[0] = 1u8; n11[1] = 1u8;
match (bytes.rindex(a[0:4], n11[0:2])) { match (bytes.rindex(a[0:4], n11[0:2])) {
case let i: i32 => { if (i != 1) { fail(); }; }; case let i: i32 => { assert(!(i != 1)); };
case void => { fail(); }; case void => abort();
}; };
let n12: [2]u8; n12[0] = 1u8; n12[1] = 2u8; let n12: [2]u8; n12[0] = 1u8; n12[1] = 2u8;
match (bytes.rindex(a[0:4], n12[0:2])) { match (bytes.rindex(a[0:4], n12[0:2])) {
case let i: i32 => { if (i != 2) { fail(); }; }; case let i: i32 => { assert(!(i != 2)); };
case void => { fail(); }; case void => abort();
}; };
// absent // absent
let n99: [2]u8; n99[0] = 9u8; n99[1] = 9u8; let n99: [2]u8; n99[0] = 9u8; n99[1] = 9u8;
match (bytes.rindex(a[0:4], n99[0:2])) { match (bytes.rindex(a[0:4], n99[0:2])) {
case let i: i32 => { fail(); }; case let i: i32 => abort();
case void => void; case void => void;
}; };
}; };
@@ -189,24 +185,19 @@ fn fail() void = { os.exit(signalled + 10); };
@test fn contains_cases() void = { @test fn contains_cases() void = {
let a: [4]u8; a[0] = 1u8; a[1] = 3u8; a[2] = 3u8; a[3] = 7u8; let a: [4]u8; a[0] = 1u8; a[1] = 3u8; a[2] = 3u8; a[3] = 7u8;
if (!bytes.contains(a[0:4], 7u8)) { fail(); }; assert(!(!bytes.contains(a[0:4], 7u8)));
if ( bytes.contains(a[0:4], 42u8)) { fail(); }; assert(!( bytes.contains(a[0:4], 42u8)));
let n: [2]u8; n[0] = 3u8; n[1] = 3u8; let n: [2]u8; n[0] = 3u8; n[1] = 3u8;
if (!bytes.contains(a[0:4], n[0:2])) { fail(); }; assert(!(!bytes.contains(a[0:4], n[0:2])));
let m: [2]u8; m[0] = 9u8; m[1] = 9u8; let m: [2]u8; m[0] = 9u8; m[1] = 9u8;
if ( bytes.contains(a[0:4], m[0:2])) { fail(); }; assert(!( bytes.contains(a[0:4], m[0:2])));
// Variadic rows. ref/hare/bytes/contains.ha:6. // Variadic rows. ref/hare/bytes/contains.ha:6.
signalled = 1700; assert(!( bytes.contains(a[0:4])));
if ( bytes.contains(a[0:4])) { fail(); }; assert(!(!bytes.contains(a[0:4], n[0:2])));
signalled = 1701; assert(!(!bytes.contains(a[0:4], 7u8)));
if (!bytes.contains(a[0:4], n[0:2])) { fail(); }; assert(!(!bytes.contains(a[0:4], m[0:2], n[0:2], 42u8)));
signalled = 1702; assert(!( bytes.contains(a[0:4], m[0:2], 42u8, m[0:2])));
if (!bytes.contains(a[0:4], 7u8)) { fail(); };
signalled = 1703;
if (!bytes.contains(a[0:4], m[0:2], n[0:2], 42u8)) { fail(); };
signalled = 1704;
if ( bytes.contains(a[0:4], m[0:2], 42u8, m[0:2])) { fail(); };
}; };
// ---- hasprefix -------------------------------------------------------- // ---- hasprefix --------------------------------------------------------
@@ -214,17 +205,17 @@ fn fail() void = { os.exit(signalled + 10); };
@test fn hasprefix_cases() void = { @test fn hasprefix_cases() void = {
let z: [1]u8; let z: [1]u8;
if (!bytes.hasprefix(z[0:0], z[0:0])) { fail(); }; assert(!(!bytes.hasprefix(z[0:0], z[0:0])));
let one: [1]u8; one[0] = 0u8; let one: [1]u8; one[0] = 0u8;
if (!bytes.hasprefix(one[0:1], z[0:0])) { fail(); }; assert(!(!bytes.hasprefix(one[0:1], z[0:0])));
if ( bytes.hasprefix(z[0:0], one[0:1])) { fail(); }; assert(!( bytes.hasprefix(z[0:0], one[0:1])));
let a: [3]u8; a[0] = 1u8; a[1] = 2u8; a[2] = 3u8; let a: [3]u8; a[0] = 1u8; a[1] = 2u8; a[2] = 3u8;
let p12: [2]u8; p12[0] = 1u8; p12[1] = 2u8; let p12: [2]u8; p12[0] = 1u8; p12[1] = 2u8;
if (!bytes.hasprefix(a[0:3], p12[0:2])) { fail(); }; assert(!(!bytes.hasprefix(a[0:3], p12[0:2])));
let p11: [2]u8; p11[0] = 1u8; p11[1] = 1u8; let p11: [2]u8; p11[0] = 1u8; p11[1] = 1u8;
if ( bytes.hasprefix(a[0:3], p11[0:2])) { fail(); }; assert(!( bytes.hasprefix(a[0:3], p11[0:2])));
let pl: [4]u8; pl[0] = 1u8; pl[1] = 2u8; pl[2] = 3u8; pl[3] = 4u8; let pl: [4]u8; pl[0] = 1u8; pl[1] = 2u8; pl[2] = 3u8; pl[3] = 4u8;
if ( bytes.hasprefix(a[0:3], pl[0:4])) { fail(); }; assert(!( bytes.hasprefix(a[0:3], pl[0:4])));
}; };
// ---- hassuffix -------------------------------------------------------- // ---- hassuffix --------------------------------------------------------
@@ -232,18 +223,18 @@ fn fail() void = { os.exit(signalled + 10); };
@test fn hassuffix_cases() void = { @test fn hassuffix_cases() void = {
let z: [1]u8; let z: [1]u8;
if (!bytes.hassuffix(z[0:0], z[0:0])) { fail(); }; assert(!(!bytes.hassuffix(z[0:0], z[0:0])));
let one: [1]u8; one[0] = 0u8; let one: [1]u8; one[0] = 0u8;
if (!bytes.hassuffix(one[0:1], z[0:0])) { fail(); }; assert(!(!bytes.hassuffix(one[0:1], z[0:0])));
if ( bytes.hassuffix(z[0:0], one[0:1])) { fail(); }; assert(!( bytes.hassuffix(z[0:0], one[0:1])));
let a: [3]u8; a[0] = 1u8; a[1] = 2u8; a[2] = 3u8; let a: [3]u8; a[0] = 1u8; a[1] = 2u8; a[2] = 3u8;
let s23: [2]u8; s23[0] = 2u8; s23[1] = 3u8; let s23: [2]u8; s23[0] = 2u8; s23[1] = 3u8;
if (!bytes.hassuffix(a[0:3], s23[0:2])) { fail(); }; assert(!(!bytes.hassuffix(a[0:3], s23[0:2])));
let s22: [2]u8; s22[0] = 2u8; s22[1] = 2u8; let s22: [2]u8; s22[0] = 2u8; s22[1] = 2u8;
if ( bytes.hassuffix(a[0:3], s22[0:2])) { fail(); }; assert(!( bytes.hassuffix(a[0:3], s22[0:2])));
let a4: [4]u8; a4[0] = 1u8; a4[1] = 2u8; a4[2] = 3u8; a4[3] = 4u8; let a4: [4]u8; a4[0] = 1u8; a4[1] = 2u8; a4[2] = 3u8; a4[3] = 4u8;
let s234: [3]u8; s234[0] = 2u8; s234[1] = 3u8; s234[2] = 4u8; let s234: [3]u8; s234[0] = 2u8; s234[1] = 3u8; s234[2] = 4u8;
if (!bytes.hassuffix(a4[0:4], s234[0:3])) { fail(); }; assert(!(!bytes.hassuffix(a4[0:4], s234[0:3])));
}; };
// ---- tokenize / rtokenize / peek_token / remaining_tokens ----------- // ---- tokenize / rtokenize / peek_token / remaining_tokens -----------
@@ -256,26 +247,26 @@ fn fail() void = { os.exit(signalled + 10); };
fn expect_token(t: *bytes.tokenizer, want: []u8) void = { fn expect_token(t: *bytes.tokenizer, want: []u8) void = {
match (bytes.peek_token(t)) { match (bytes.peek_token(t)) {
case let p: []u8 => { case let p: []u8 => {
if (!bytes.equal(p, want)) { fail(); }; assert(!(!bytes.equal(p, want)));
}; };
case bytes.done => { fail(); }; case bytes.done => abort();
}; };
match (bytes.next_token(t)) { match (bytes.next_token(t)) {
case let n: []u8 => { case let n: []u8 => {
if (!bytes.equal(n, want)) { fail(); }; assert(!(!bytes.equal(n, want)));
}; };
case bytes.done => { fail(); }; case bytes.done => abort();
}; };
}; };
// expect_done — table-row driver. peek and next must both be done. // expect_done — table-row driver. peek and next must both be done.
fn expect_done(t: *bytes.tokenizer) void = { fn expect_done(t: *bytes.tokenizer) void = {
match (bytes.peek_token(t)) { match (bytes.peek_token(t)) {
case let p: []u8 => { fail(); }; case let p: []u8 => abort();
case bytes.done => void; case bytes.done => void;
}; };
match (bytes.next_token(t)) { match (bytes.next_token(t)) {
case let n: []u8 => { fail(); }; case let n: []u8 => abort();
case bytes.done => void; case bytes.done => void;
}; };
}; };
@@ -284,7 +275,6 @@ fn expect_done(t: *bytes.tokenizer) void = {
let z: [1]u8; let z: [1]u8;
// simple — [1,2,0,3,4] / [0] -> [1,2],[3,4] // simple — [1,2,0,3,4] / [0] -> [1,2],[3,4]
signalled = 1710;
let a: [5]u8; a[0] = 1u8; a[1] = 2u8; a[2] = 0u8; a[3] = 3u8; a[4] = 4u8; let a: [5]u8; a[0] = 1u8; a[1] = 2u8; a[2] = 0u8; a[3] = 3u8; a[4] = 4u8;
let e_12: [2]u8; e_12[0] = 1u8; e_12[1] = 2u8; let e_12: [2]u8; e_12[0] = 1u8; e_12[1] = 2u8;
let e_34: [2]u8; e_34[0] = 3u8; e_34[1] = 4u8; let e_34: [2]u8; e_34[0] = 3u8; e_34[1] = 4u8;
@@ -294,7 +284,6 @@ fn expect_done(t: *bytes.tokenizer) void = {
expect_done(&t); expect_done(&t);
// multiple delimiters — [1,2,0,3,4,42,5,6] / [0,42] -> [1,2],[3,4],[5,6] // multiple delimiters — [1,2,0,3,4,42,5,6] / [0,42] -> [1,2],[3,4],[5,6]
signalled = 1711;
let b: [8]u8; let b: [8]u8;
b[0] = 1u8; b[1] = 2u8; b[2] = 0u8; b[3] = 3u8; b[0] = 1u8; b[1] = 2u8; b[2] = 0u8; b[3] = 3u8;
b[4] = 4u8; b[5] = 42u8; b[6] = 5u8; b[7] = 6u8; b[4] = 4u8; b[5] = 42u8; b[6] = 5u8; b[7] = 6u8;
@@ -306,7 +295,6 @@ fn expect_done(t: *bytes.tokenizer) void = {
expect_done(&t2); expect_done(&t2);
// empty interior tokens — [1,2,0,0,0,3,4] / [0] -> [1,2],[],[],[3,4] // empty interior tokens — [1,2,0,0,0,3,4] / [0] -> [1,2],[],[],[3,4]
signalled = 1712;
let c: [7]u8; let c: [7]u8;
c[0] = 1u8; c[1] = 2u8; c[2] = 0u8; c[3] = 0u8; c[0] = 1u8; c[1] = 2u8; c[2] = 0u8; c[3] = 0u8;
c[4] = 0u8; c[5] = 3u8; c[6] = 4u8; c[4] = 0u8; c[5] = 3u8; c[6] = 4u8;
@@ -318,7 +306,6 @@ fn expect_done(t: *bytes.tokenizer) void = {
expect_done(&t3); expect_done(&t3);
// leading + trailing empty — [0,1,2,3,0] / [0] -> [],[1,2,3],[] // leading + trailing empty — [0,1,2,3,0] / [0] -> [],[1,2,3],[]
signalled = 1713;
let d: [5]u8; let d: [5]u8;
d[0] = 0u8; d[1] = 1u8; d[2] = 2u8; d[3] = 3u8; d[4] = 0u8; d[0] = 0u8; d[1] = 1u8; d[2] = 2u8; d[3] = 3u8; d[4] = 0u8;
let e_123: [3]u8; e_123[0] = 1u8; e_123[1] = 2u8; e_123[2] = 3u8; let e_123: [3]u8; e_123[0] = 1u8; e_123[1] = 2u8; e_123[2] = 3u8;
@@ -329,14 +316,12 @@ fn expect_done(t: *bytes.tokenizer) void = {
expect_done(&t4); expect_done(&t4);
// no delim hit — [1,2,3] / [0] -> [1,2,3] // no delim hit — [1,2,3] / [0] -> [1,2,3]
signalled = 1714;
let f: [3]u8; f[0] = 1u8; f[1] = 2u8; f[2] = 3u8; let f: [3]u8; f[0] = 1u8; f[1] = 2u8; f[2] = 3u8;
let t5: bytes.tokenizer = bytes.tokenize(f[0:3], 0u8); let t5: bytes.tokenizer = bytes.tokenize(f[0:3], 0u8);
expect_token(&t5, e_123[0:3]); expect_token(&t5, e_123[0:3]);
expect_done(&t5); expect_done(&t5);
// empty input — [] / [0] -> done immediately // empty input — [] / [0] -> done immediately
signalled = 1715;
let t6: bytes.tokenizer = bytes.tokenize(z[0:0], 0u8); let t6: bytes.tokenizer = bytes.tokenize(z[0:0], 0u8);
expect_done(&t6); expect_done(&t6);
}; };
@@ -345,7 +330,6 @@ fn expect_done(t: *bytes.tokenizer) void = {
let z: [1]u8; let z: [1]u8;
// simple — [1,2,0,3,4] / [0] -> [3,4],[1,2] // simple — [1,2,0,3,4] / [0] -> [3,4],[1,2]
signalled = 1720;
let a: [5]u8; a[0] = 1u8; a[1] = 2u8; a[2] = 0u8; a[3] = 3u8; a[4] = 4u8; let a: [5]u8; a[0] = 1u8; a[1] = 2u8; a[2] = 0u8; a[3] = 3u8; a[4] = 4u8;
let e_12: [2]u8; e_12[0] = 1u8; e_12[1] = 2u8; let e_12: [2]u8; e_12[0] = 1u8; e_12[1] = 2u8;
let e_34: [2]u8; e_34[0] = 3u8; e_34[1] = 4u8; let e_34: [2]u8; e_34[0] = 3u8; e_34[1] = 4u8;
@@ -355,7 +339,6 @@ fn expect_done(t: *bytes.tokenizer) void = {
expect_done(&t); expect_done(&t);
// multiple delimiters — [1,2,0,3,4,42,5,6] / [0,42] -> [5,6],[3,4],[1,2] // multiple delimiters — [1,2,0,3,4,42,5,6] / [0,42] -> [5,6],[3,4],[1,2]
signalled = 1721;
let b: [8]u8; let b: [8]u8;
b[0] = 1u8; b[1] = 2u8; b[2] = 0u8; b[3] = 3u8; b[0] = 1u8; b[1] = 2u8; b[2] = 0u8; b[3] = 3u8;
b[4] = 4u8; b[5] = 42u8; b[6] = 5u8; b[7] = 6u8; b[4] = 4u8; b[5] = 42u8; b[6] = 5u8; b[7] = 6u8;
@@ -367,7 +350,6 @@ fn expect_done(t: *bytes.tokenizer) void = {
expect_done(&t2); expect_done(&t2);
// empty interior tokens — [1,2,0,0,0,3,4] / [0] -> [3,4],[],[],[1,2] // empty interior tokens — [1,2,0,0,0,3,4] / [0] -> [3,4],[],[],[1,2]
signalled = 1722;
let c: [7]u8; let c: [7]u8;
c[0] = 1u8; c[1] = 2u8; c[2] = 0u8; c[3] = 0u8; c[0] = 1u8; c[1] = 2u8; c[2] = 0u8; c[3] = 0u8;
c[4] = 0u8; c[5] = 3u8; c[6] = 4u8; c[4] = 0u8; c[5] = 3u8; c[6] = 4u8;
@@ -379,7 +361,6 @@ fn expect_done(t: *bytes.tokenizer) void = {
expect_done(&t3); expect_done(&t3);
// leading + trailing empty — [0,1,2,3,0] / [0] -> [],[1,2,3],[] // leading + trailing empty — [0,1,2,3,0] / [0] -> [],[1,2,3],[]
signalled = 1723;
let d: [5]u8; let d: [5]u8;
d[0] = 0u8; d[1] = 1u8; d[2] = 2u8; d[3] = 3u8; d[4] = 0u8; d[0] = 0u8; d[1] = 1u8; d[2] = 2u8; d[3] = 3u8; d[4] = 0u8;
let e_123: [3]u8; e_123[0] = 1u8; e_123[1] = 2u8; e_123[2] = 3u8; let e_123: [3]u8; e_123[0] = 1u8; e_123[1] = 2u8; e_123[2] = 3u8;
@@ -390,81 +371,74 @@ fn expect_done(t: *bytes.tokenizer) void = {
expect_done(&t4); expect_done(&t4);
// no delim hit — [1,2,3] / [0] -> [1,2,3] // no delim hit — [1,2,3] / [0] -> [1,2,3]
signalled = 1724;
let f: [3]u8; f[0] = 1u8; f[1] = 2u8; f[2] = 3u8; let f: [3]u8; f[0] = 1u8; f[1] = 2u8; f[2] = 3u8;
let t5: bytes.tokenizer = bytes.rtokenize(f[0:3], 0u8); let t5: bytes.tokenizer = bytes.rtokenize(f[0:3], 0u8);
expect_token(&t5, e_123[0:3]); expect_token(&t5, e_123[0:3]);
expect_done(&t5); expect_done(&t5);
// empty input — [] / [0] -> done immediately // empty input — [] / [0] -> done immediately
signalled = 1725;
let t6: bytes.tokenizer = bytes.rtokenize(z[0:0], 0u8); let t6: bytes.tokenizer = bytes.rtokenize(z[0:0], 0u8);
expect_done(&t6); expect_done(&t6);
}; };
@test fn peek_token_cases() void = { @test fn peek_token_cases() void = {
// Peeking twice without advancing returns the same token. // Peeking twice without advancing returns the same token.
signalled = 1730;
let a: [5]u8; a[0] = 1u8; a[1] = 2u8; a[2] = 0u8; a[3] = 3u8; a[4] = 4u8; let a: [5]u8; a[0] = 1u8; a[1] = 2u8; a[2] = 0u8; a[3] = 3u8; a[4] = 4u8;
let e_12: [2]u8; e_12[0] = 1u8; e_12[1] = 2u8; let e_12: [2]u8; e_12[0] = 1u8; e_12[1] = 2u8;
let t: bytes.tokenizer = bytes.tokenize(a[0:5], 0u8); let t: bytes.tokenizer = bytes.tokenize(a[0:5], 0u8);
match (bytes.peek_token(&t)) { match (bytes.peek_token(&t)) {
case let p: []u8 => { if (!bytes.equal(p, e_12[0:2])) { fail(); }; }; case let p: []u8 => { assert(!(!bytes.equal(p, e_12[0:2]))); };
case bytes.done => { fail(); }; case bytes.done => abort();
}; };
match (bytes.peek_token(&t)) { match (bytes.peek_token(&t)) {
case let p: []u8 => { if (!bytes.equal(p, e_12[0:2])) { fail(); }; }; case let p: []u8 => { assert(!(!bytes.equal(p, e_12[0:2]))); };
case bytes.done => { fail(); }; case bytes.done => abort();
}; };
// Then advance once — peek-after-next is the second token. // Then advance once — peek-after-next is the second token.
signalled = 1731;
let e_34: [2]u8; e_34[0] = 3u8; e_34[1] = 4u8; let e_34: [2]u8; e_34[0] = 3u8; e_34[1] = 4u8;
match (bytes.next_token(&t)) { match (bytes.next_token(&t)) {
case let n: []u8 => { if (!bytes.equal(n, e_12[0:2])) { fail(); }; }; case let n: []u8 => { assert(!(!bytes.equal(n, e_12[0:2]))); };
case bytes.done => { fail(); }; case bytes.done => abort();
}; };
match (bytes.peek_token(&t)) { match (bytes.peek_token(&t)) {
case let p: []u8 => { if (!bytes.equal(p, e_34[0:2])) { fail(); }; }; case let p: []u8 => { assert(!(!bytes.equal(p, e_34[0:2]))); };
case bytes.done => { fail(); }; case bytes.done => abort();
}; };
// Reverse peek symmetry — first peek is last token. // Reverse peek symmetry — first peek is last token.
signalled = 1732;
let t2: bytes.tokenizer = bytes.rtokenize(a[0:5], 0u8); let t2: bytes.tokenizer = bytes.rtokenize(a[0:5], 0u8);
match (bytes.peek_token(&t2)) { match (bytes.peek_token(&t2)) {
case let p: []u8 => { if (!bytes.equal(p, e_34[0:2])) { fail(); }; }; case let p: []u8 => { assert(!(!bytes.equal(p, e_34[0:2]))); };
case bytes.done => { fail(); }; case bytes.done => abort();
}; };
match (bytes.peek_token(&t2)) { match (bytes.peek_token(&t2)) {
case let p: []u8 => { if (!bytes.equal(p, e_34[0:2])) { fail(); }; }; case let p: []u8 => { assert(!(!bytes.equal(p, e_34[0:2]))); };
case bytes.done => { fail(); }; case bytes.done => abort();
}; };
}; };
@test fn remaining_tokens_cases() void = { @test fn remaining_tokens_cases() void = {
// After one next_token, remaining is bytes past the consumed delim. // After one next_token, remaining is bytes past the consumed delim.
signalled = 1740;
let a: [5]u8; a[0] = 1u8; a[1] = 2u8; a[2] = 0u8; a[3] = 3u8; a[4] = 4u8; let a: [5]u8; a[0] = 1u8; a[1] = 2u8; a[2] = 0u8; a[3] = 3u8; a[4] = 4u8;
let e_34: [2]u8; e_34[0] = 3u8; e_34[1] = 4u8; let e_34: [2]u8; e_34[0] = 3u8; e_34[1] = 4u8;
let t: bytes.tokenizer = bytes.tokenize(a[0:5], 0u8); let t: bytes.tokenizer = bytes.tokenize(a[0:5], 0u8);
match (bytes.next_token(&t)) { match (bytes.next_token(&t)) {
case let n: []u8 => void; case let n: []u8 => void;
case bytes.done => { fail(); }; case bytes.done => abort();
}; };
let r: []u8 = bytes.remaining_tokens(&t); let r: []u8 = bytes.remaining_tokens(&t);
if (!bytes.equal(r, e_34[0:2])) { fail(); }; assert(!(!bytes.equal(r, e_34[0:2])));
// Reverse — after one next_token, remaining is bytes before the // Reverse — after one next_token, remaining is bytes before the
// consumed delim. ref/hare/bytes/tokenize.ha:323-327 pins [1,2]. // consumed delim. ref/hare/bytes/tokenize.ha:323-327 pins [1,2].
signalled = 1741;
let e_12: [2]u8; e_12[0] = 1u8; e_12[1] = 2u8; let e_12: [2]u8; e_12[0] = 1u8; e_12[1] = 2u8;
let t2: bytes.tokenizer = bytes.rtokenize(a[0:5], 0u8); let t2: bytes.tokenizer = bytes.rtokenize(a[0:5], 0u8);
match (bytes.next_token(&t2)) { match (bytes.next_token(&t2)) {
case let n: []u8 => void; case let n: []u8 => void;
case bytes.done => { fail(); }; case bytes.done => abort();
}; };
let r2: []u8 = bytes.remaining_tokens(&t2); let r2: []u8 = bytes.remaining_tokens(&t2);
if (!bytes.equal(r2, e_12[0:2])) { fail(); }; assert(!(!bytes.equal(r2, e_12[0:2])));
}; };
// ---- ltrim / rtrim / trim --------------------------------------------- // ---- ltrim / rtrim / trim ---------------------------------------------
@@ -486,86 +460,74 @@ fn beq(got: []u8, want: []u8) bool = {
let z: [1]u8; let z: [1]u8;
// [0,0,1,2] / 0 -> [1,2] // [0,0,1,2] / 0 -> [1,2]
signalled = 1780;
let a: [4]u8; a[0] = 0u8; a[1] = 0u8; a[2] = 1u8; a[3] = 2u8; let a: [4]u8; a[0] = 0u8; a[1] = 0u8; a[2] = 1u8; a[3] = 2u8;
let ea: [2]u8; ea[0] = 1u8; ea[1] = 2u8; let ea: [2]u8; ea[0] = 1u8; ea[1] = 2u8;
if (!beq(bytes.ltrim(a[0:4], 0u8), ea[0:2])) { fail(); }; assert(!(!beq(bytes.ltrim(a[0:4], 0u8), ea[0:2])));
// [1,2,3] / 0 -> [1,2,3] (no leading match) // [1,2,3] / 0 -> [1,2,3] (no leading match)
signalled = 1781;
let b: [3]u8; b[0] = 1u8; b[1] = 2u8; b[2] = 3u8; let b: [3]u8; b[0] = 1u8; b[1] = 2u8; b[2] = 3u8;
if (!beq(bytes.ltrim(b[0:3], 0u8), b[0:3])) { fail(); }; assert(!(!beq(bytes.ltrim(b[0:3], 0u8), b[0:3])));
// [0,0,0] / 0 -> [] (full match) // [0,0,0] / 0 -> [] (full match) — the bare `let c: [3]u8;` zero-init
signalled = 1782; // row that surfaced #16 (a dirtied slot read non-zero, ltrim trimmed
// nothing). Now reads {0,0,0} and trims to empty.
let c: [3]u8; let c: [3]u8;
if (!beq(bytes.ltrim(c[0:3], 0u8), z[0:0])) { fail(); }; assert(!(!beq(bytes.ltrim(c[0:3], 0u8), z[0:0])));
// [] / 0 -> [] (empty input) // [] / 0 -> [] (empty input)
signalled = 1783; assert(!(!beq(bytes.ltrim(z[0:0], 0u8), z[0:0])));
if (!beq(bytes.ltrim(z[0:0], 0u8), z[0:0])) { fail(); };
}; };
@test fn rtrim_cases() void = { @test fn rtrim_cases() void = {
let z: [1]u8; let z: [1]u8;
// [1,2,0,0] / 0 -> [1,2] // [1,2,0,0] / 0 -> [1,2]
signalled = 1790;
let a: [4]u8; a[0] = 1u8; a[1] = 2u8; a[2] = 0u8; a[3] = 0u8; let a: [4]u8; a[0] = 1u8; a[1] = 2u8; a[2] = 0u8; a[3] = 0u8;
let ea: [2]u8; ea[0] = 1u8; ea[1] = 2u8; let ea: [2]u8; ea[0] = 1u8; ea[1] = 2u8;
if (!beq(bytes.rtrim(a[0:4], 0u8), ea[0:2])) { fail(); }; assert(!(!beq(bytes.rtrim(a[0:4], 0u8), ea[0:2])));
// [1,2,3] / 0 -> [1,2,3] (no trailing match) // [1,2,3] / 0 -> [1,2,3] (no trailing match)
signalled = 1791;
let b: [3]u8; b[0] = 1u8; b[1] = 2u8; b[2] = 3u8; let b: [3]u8; b[0] = 1u8; b[1] = 2u8; b[2] = 3u8;
if (!beq(bytes.rtrim(b[0:3], 0u8), b[0:3])) { fail(); }; assert(!(!beq(bytes.rtrim(b[0:3], 0u8), b[0:3])));
// [0,0,0] / 0 -> [] (full match) // [0,0,0] / 0 -> [] (full match)
signalled = 1792;
let c: [3]u8; let c: [3]u8;
if (!beq(bytes.rtrim(c[0:3], 0u8), z[0:0])) { fail(); }; assert(!(!beq(bytes.rtrim(c[0:3], 0u8), z[0:0])));
// [] / 0 -> [] (empty input) // [] / 0 -> [] (empty input)
signalled = 1793; assert(!(!beq(bytes.rtrim(z[0:0], 0u8), z[0:0])));
if (!beq(bytes.rtrim(z[0:0], 0u8), z[0:0])) { fail(); };
}; };
@test fn trim_cases() void = { @test fn trim_cases() void = {
let z: [1]u8; let z: [1]u8;
// [0,1,2,3,5,0] / 0 -> [1,2,3,5] // [0,1,2,3,5,0] / 0 -> [1,2,3,5]
signalled = 1800;
let a: [6]u8; let a: [6]u8;
a[0] = 0u8; a[1] = 1u8; a[2] = 2u8; a[0] = 0u8; a[1] = 1u8; a[2] = 2u8;
a[3] = 3u8; a[4] = 5u8; a[5] = 0u8; a[3] = 3u8; a[4] = 5u8; a[5] = 0u8;
let ea: [4]u8; ea[0] = 1u8; ea[1] = 2u8; ea[2] = 3u8; ea[3] = 5u8; let ea: [4]u8; ea[0] = 1u8; ea[1] = 2u8; ea[2] = 3u8; ea[3] = 5u8;
if (!beq(bytes.trim(a[0:6], 0u8), ea[0:4])) { fail(); }; assert(!(!beq(bytes.trim(a[0:6], 0u8), ea[0:4])));
// [0,5,0] / 5 -> [0,5,0] (only 5 in trim set; boundary mismatch) // [0,5,0] / 5 -> [0,5,0] (only 5 in trim set; boundary mismatch)
signalled = 1801;
let b: [3]u8; b[0] = 0u8; b[1] = 5u8; b[2] = 0u8; let b: [3]u8; b[0] = 0u8; b[1] = 5u8; b[2] = 0u8;
if (!beq(bytes.trim(b[0:3], 5u8), b[0:3])) { fail(); }; assert(!(!beq(bytes.trim(b[0:3], 5u8), b[0:3])));
// [0,1,42,1,0] / {0,42} -> [1,42,1] (multi-byte trim set) // [0,1,42,1,0] / {0,42} -> [1,42,1] (multi-byte trim set)
signalled = 1802;
let c: [5]u8; let c: [5]u8;
c[0] = 0u8; c[1] = 1u8; c[2] = 42u8; c[3] = 1u8; c[4] = 0u8; c[0] = 0u8; c[1] = 1u8; c[2] = 42u8; c[3] = 1u8; c[4] = 0u8;
let ec: [3]u8; ec[0] = 1u8; ec[1] = 42u8; ec[2] = 1u8; let ec: [3]u8; ec[0] = 1u8; ec[1] = 42u8; ec[2] = 1u8;
if (!beq(bytes.trim(c[0:5], 0u8, 42u8), ec[0:3])) { fail(); }; assert(!(!beq(bytes.trim(c[0:5], 0u8, 42u8), ec[0:3])));
// [0,0,0] / 0 -> [] (full match) // [0,0,0] / 0 -> [] (full match)
signalled = 1803;
let d: [3]u8; let d: [3]u8;
if (!beq(bytes.trim(d[0:3], 0u8), z[0:0])) { fail(); }; assert(!(!beq(bytes.trim(d[0:3], 0u8), z[0:0])));
// [] / 0 -> [] (empty input, Hare ref/hare/bytes/trim.ha:34) // [] / 0 -> [] (empty input, Hare ref/hare/bytes/trim.ha:34)
signalled = 1804; assert(!(!beq(bytes.trim(z[0:0], 0u8), z[0:0])));
if (!beq(bytes.trim(z[0:0], 0u8), z[0:0])) { fail(); };
// [1,2,3,5] / 0 -> [1,2,3,5] (Hare ref/hare/bytes/trim.ha:31) // [1,2,3,5] / 0 -> [1,2,3,5] (Hare ref/hare/bytes/trim.ha:31)
signalled = 1805;
let e: [4]u8; e[0] = 1u8; e[1] = 2u8; e[2] = 3u8; e[3] = 5u8; let e: [4]u8; e[0] = 1u8; e[1] = 2u8; e[2] = 3u8; e[3] = 5u8;
if (!beq(bytes.trim(e[0:4], 0u8), e[0:4])) { fail(); }; assert(!(!beq(bytes.trim(e[0:4], 0u8), e[0:4])));
}; };
// ---- splitn / rsplitn / split ----------------------------------------- // ---- splitn / rsplitn / split -----------------------------------------
@@ -578,12 +540,12 @@ fn beq(got: []u8, want: []u8) bool = {
// Pointer-then-fields lifts 8B at a time, which the cgen routes correctly. // Pointer-then-fields lifts 8B at a time, which the cgen routes correctly.
fn expect_tok(toks: [][]u8, i: i32, want: []u8) void = { fn expect_tok(toks: [][]u8, i: i32, want: []u8) void = {
if (i >= toks.len) { fail(); }; assert(!(i >= toks.len));
let p: *[]u8 = &toks.ptr[i]; let p: *[]u8 = &toks.ptr[i];
if (p.len != want.len) { fail(); }; assert(!(p.len != want.len));
let j: i32 = 0; let j: i32 = 0;
for (j < want.len) { for (j < want.len) {
if (p.ptr[j] != want[j]) { fail(); }; assert(!(p.ptr[j] != want[j]));
j += 1; j += 1;
}; };
}; };
@@ -592,7 +554,6 @@ fn expect_tok(toks: [][]u8, i: i32, want: []u8) void = {
// "Hello, my name is Drew" ─ space-delimited, n=4 yields the 4-th // "Hello, my name is Drew" ─ space-delimited, n=4 yields the 4-th
// token as the unconsumed remainder. Hare pins this exact shape // token as the unconsumed remainder. Hare pins this exact shape
// at ref/hare/bytes/tokenize.ha:340. // at ref/hare/bytes/tokenize.ha:340.
signalled = 1750;
let a: [22]u8; let a: [22]u8;
a[0] = 72u8; a[1] = 101u8; a[2] = 108u8; a[3] = 108u8; a[4] = 111u8; a[0] = 72u8; a[1] = 101u8; a[2] = 108u8; a[3] = 108u8; a[4] = 111u8;
a[5] = 44u8; a[6] = 32u8; a[7] = 109u8; a[8] = 121u8; a[9] = 32u8; a[5] = 44u8; a[6] = 32u8; a[7] = 109u8; a[8] = 121u8; a[9] = 32u8;
@@ -601,7 +562,7 @@ fn expect_tok(toks: [][]u8, i: i32, want: []u8) void = {
a[18] = 68u8; a[19] = 114u8; a[20] = 101u8; a[21] = 119u8; a[18] = 68u8; a[19] = 114u8; a[20] = 101u8; a[21] = 119u8;
let sp: [1]u8; sp[0] = 32u8; let sp: [1]u8; sp[0] = 32u8;
let t1: [][]u8 = bytes.splitn(a[0:22], sp[0:1], 4); let t1: [][]u8 = bytes.splitn(a[0:22], sp[0:1], 4);
if (t1.len != 4) { fail(); }; assert(!(t1.len != 4));
expect_tok(t1, 0, a[0:6]); expect_tok(t1, 0, a[0:6]);
expect_tok(t1, 1, a[7:9]); expect_tok(t1, 1, a[7:9]);
expect_tok(t1, 2, a[10:14]); expect_tok(t1, 2, a[10:14]);
@@ -610,48 +571,43 @@ fn expect_tok(toks: [][]u8, i: i32, want: []u8) void = {
// n > token count — final slot is "" if input ends in delim, // n > token count — final slot is "" if input ends in delim,
// otherwise the last token. Here three tokens, n=10 → 3 entries. // otherwise the last token. Here three tokens, n=10 → 3 entries.
signalled = 1751;
let b: [5]u8; b[0] = 1u8; b[1] = 0u8; b[2] = 2u8; b[3] = 0u8; b[4] = 3u8; let b: [5]u8; b[0] = 1u8; b[1] = 0u8; b[2] = 2u8; b[3] = 0u8; b[4] = 3u8;
let zd: [1]u8; zd[0] = 0u8; let zd: [1]u8; zd[0] = 0u8;
let t2: [][]u8 = bytes.splitn(b[0:5], zd[0:1], 10); let t2: [][]u8 = bytes.splitn(b[0:5], zd[0:1], 10);
if (t2.len != 3) { fail(); }; assert(!(t2.len != 3));
expect_tok(t2, 0, b[0:1]); expect_tok(t2, 0, b[0:1]);
expect_tok(t2, 1, b[2:3]); expect_tok(t2, 1, b[2:3]);
expect_tok(t2, 2, b[4:5]); expect_tok(t2, 2, b[4:5]);
os.free(t2.ptr: *void, (t2.cap: u64) * 24u64); os.free(t2.ptr: *void, (t2.cap: u64) * 24u64);
// n == 1 — single slot holding the whole input as remainder. // n == 1 — single slot holding the whole input as remainder.
signalled = 1752;
let t3: [][]u8 = bytes.splitn(b[0:5], zd[0:1], 1); let t3: [][]u8 = bytes.splitn(b[0:5], zd[0:1], 1);
if (t3.len != 1) { fail(); }; assert(!(t3.len != 1));
expect_tok(t3, 0, b[0:5]); expect_tok(t3, 0, b[0:5]);
os.free(t3.ptr: *void, (t3.cap: u64) * 24u64); os.free(t3.ptr: *void, (t3.cap: u64) * 24u64);
// delim absent — single slot holding input unchanged. // delim absent — single slot holding input unchanged.
signalled = 1753;
let c: [3]u8; c[0] = 1u8; c[1] = 2u8; c[2] = 3u8; let c: [3]u8; c[0] = 1u8; c[1] = 2u8; c[2] = 3u8;
let t4: [][]u8 = bytes.splitn(c[0:3], zd[0:1], 5); let t4: [][]u8 = bytes.splitn(c[0:3], zd[0:1], 5);
if (t4.len != 1) { fail(); }; assert(!(t4.len != 1));
expect_tok(t4, 0, c[0:3]); expect_tok(t4, 0, c[0:3]);
os.free(t4.ptr: *void, (t4.cap: u64) * 24u64); os.free(t4.ptr: *void, (t4.cap: u64) * 24u64);
// empty input — empty result. // empty input — empty result.
signalled = 1754;
let z: [1]u8; let z: [1]u8;
let t5: [][]u8 = bytes.splitn(z[0:0], zd[0:1], 5); let t5: [][]u8 = bytes.splitn(z[0:0], zd[0:1], 5);
if (t5.len != 0) { fail(); }; assert(!(t5.len != 0));
if (t5.cap > 0) { if (t5.cap > 0) {
os.free(t5.ptr: *void, (t5.cap: u64) * 24u64); os.free(t5.ptr: *void, (t5.cap: u64) * 24u64);
}; };
// Multi-byte delimiter set — both 0 and 42 split. // Multi-byte delimiter set — both 0 and 42 split.
signalled = 1755;
let d: [8]u8; let d: [8]u8;
d[0] = 1u8; d[1] = 2u8; d[2] = 0u8; d[3] = 3u8; d[0] = 1u8; d[1] = 2u8; d[2] = 0u8; d[3] = 3u8;
d[4] = 4u8; d[5] = 42u8; d[6] = 5u8; d[7] = 6u8; d[4] = 4u8; d[5] = 42u8; d[6] = 5u8; d[7] = 6u8;
let dd: [2]u8; dd[0] = 0u8; dd[1] = 42u8; let dd: [2]u8; dd[0] = 0u8; dd[1] = 42u8;
let t6: [][]u8 = bytes.splitn(d[0:8], dd[0:2], 100); let t6: [][]u8 = bytes.splitn(d[0:8], dd[0:2], 100);
if (t6.len != 3) { fail(); }; assert(!(t6.len != 3));
expect_tok(t6, 0, d[0:2]); expect_tok(t6, 0, d[0:2]);
expect_tok(t6, 1, d[3:5]); expect_tok(t6, 1, d[3:5]);
expect_tok(t6, 2, d[6:8]); expect_tok(t6, 2, d[6:8]);
@@ -662,7 +618,6 @@ fn expect_tok(toks: [][]u8, i: i32, want: []u8) void = {
// "Hello, my name is Drew" ─ rsplitn n=4 buckets the first three // "Hello, my name is Drew" ─ rsplitn n=4 buckets the first three
// tokens from the *end*; the remainder ("Hello, my") is index 0. // tokens from the *end*; the remainder ("Hello, my") is index 0.
// Hare pins this at ref/hare/bytes/tokenize.ha:379. // Hare pins this at ref/hare/bytes/tokenize.ha:379.
signalled = 1760;
let a: [22]u8; let a: [22]u8;
a[0] = 72u8; a[1] = 101u8; a[2] = 108u8; a[3] = 108u8; a[4] = 111u8; a[0] = 72u8; a[1] = 101u8; a[2] = 108u8; a[3] = 108u8; a[4] = 111u8;
a[5] = 44u8; a[6] = 32u8; a[7] = 109u8; a[8] = 121u8; a[9] = 32u8; a[5] = 44u8; a[6] = 32u8; a[7] = 109u8; a[8] = 121u8; a[9] = 32u8;
@@ -671,7 +626,7 @@ fn expect_tok(toks: [][]u8, i: i32, want: []u8) void = {
a[18] = 68u8; a[19] = 114u8; a[20] = 101u8; a[21] = 119u8; a[18] = 68u8; a[19] = 114u8; a[20] = 101u8; a[21] = 119u8;
let sp: [1]u8; sp[0] = 32u8; let sp: [1]u8; sp[0] = 32u8;
let t1: [][]u8 = bytes.rsplitn(a[0:22], sp[0:1], 4); let t1: [][]u8 = bytes.rsplitn(a[0:22], sp[0:1], 4);
if (t1.len != 4) { fail(); }; assert(!(t1.len != 4));
expect_tok(t1, 0, a[0:9]); expect_tok(t1, 0, a[0:9]);
expect_tok(t1, 1, a[10:14]); expect_tok(t1, 1, a[10:14]);
expect_tok(t1, 2, a[15:17]); expect_tok(t1, 2, a[15:17]);
@@ -682,20 +637,18 @@ fn expect_tok(toks: [][]u8, i: i32, want: []u8) void = {
// reverse-iteration order (last token first). Mirrors Hare's // reverse-iteration order (last token first). Mirrors Hare's
// behavior at ref/hare/bytes/tokenize.ha:196-199 where the // behavior at ref/hare/bytes/tokenize.ha:196-199 where the
// reverse-step is gated behind the n-1 loop completion. // reverse-step is gated behind the n-1 loop completion.
signalled = 1761;
let b: [5]u8; b[0] = 1u8; b[1] = 0u8; b[2] = 2u8; b[3] = 0u8; b[4] = 3u8; let b: [5]u8; b[0] = 1u8; b[1] = 0u8; b[2] = 2u8; b[3] = 0u8; b[4] = 3u8;
let zd: [1]u8; zd[0] = 0u8; let zd: [1]u8; zd[0] = 0u8;
let t2: [][]u8 = bytes.rsplitn(b[0:5], zd[0:1], 10); let t2: [][]u8 = bytes.rsplitn(b[0:5], zd[0:1], 10);
if (t2.len != 3) { fail(); }; assert(!(t2.len != 3));
expect_tok(t2, 0, b[4:5]); expect_tok(t2, 0, b[4:5]);
expect_tok(t2, 1, b[2:3]); expect_tok(t2, 1, b[2:3]);
expect_tok(t2, 2, b[0:1]); expect_tok(t2, 2, b[0:1]);
os.free(t2.ptr: *void, (t2.cap: u64) * 24u64); os.free(t2.ptr: *void, (t2.cap: u64) * 24u64);
// n == 1 — single slot holding the whole input as remainder. // n == 1 — single slot holding the whole input as remainder.
signalled = 1762;
let t3: [][]u8 = bytes.rsplitn(b[0:5], zd[0:1], 1); let t3: [][]u8 = bytes.rsplitn(b[0:5], zd[0:1], 1);
if (t3.len != 1) { fail(); }; assert(!(t3.len != 1));
expect_tok(t3, 0, b[0:5]); expect_tok(t3, 0, b[0:5]);
os.free(t3.ptr: *void, (t3.cap: u64) * 24u64); os.free(t3.ptr: *void, (t3.cap: u64) * 24u64);
@@ -704,10 +657,9 @@ fn expect_tok(toks: [][]u8, i: i32, want: []u8) void = {
// (the loop never runs and peek picks up the full input). // (the loop never runs and peek picks up the full input).
// With n>1 the loop's first next_token sees done (no delim // With n>1 the loop's first next_token sees done (no delim
// match anywhere) and returns toks={} per Hare's early-exit. // match anywhere) and returns toks={} per Hare's early-exit.
signalled = 1763;
let c: [3]u8; c[0] = 1u8; c[1] = 2u8; c[2] = 3u8; let c: [3]u8; c[0] = 1u8; c[1] = 2u8; c[2] = 3u8;
let t4: [][]u8 = bytes.rsplitn(c[0:3], zd[0:1], 5); let t4: [][]u8 = bytes.rsplitn(c[0:3], zd[0:1], 5);
if (t4.len != 1) { fail(); }; assert(!(t4.len != 1));
expect_tok(t4, 0, c[0:3]); expect_tok(t4, 0, c[0:3]);
os.free(t4.ptr: *void, (t4.cap: u64) * 24u64); os.free(t4.ptr: *void, (t4.cap: u64) * 24u64);
}; };
@@ -716,7 +668,6 @@ fn expect_tok(toks: [][]u8, i: i32, want: []u8) void = {
// Full split — every delim hit is a boundary; no per-call cap. // Full split — every delim hit is a boundary; no per-call cap.
// Hare pins five tokens for the canonical input at // Hare pins five tokens for the canonical input at
// ref/hare/bytes/tokenize.ha:347. // ref/hare/bytes/tokenize.ha:347.
signalled = 1770;
let a: [22]u8; let a: [22]u8;
a[0] = 72u8; a[1] = 101u8; a[2] = 108u8; a[3] = 108u8; a[4] = 111u8; a[0] = 72u8; a[1] = 101u8; a[2] = 108u8; a[3] = 108u8; a[4] = 111u8;
a[5] = 44u8; a[6] = 32u8; a[7] = 109u8; a[8] = 121u8; a[9] = 32u8; a[5] = 44u8; a[6] = 32u8; a[7] = 109u8; a[8] = 121u8; a[9] = 32u8;
@@ -725,7 +676,7 @@ fn expect_tok(toks: [][]u8, i: i32, want: []u8) void = {
a[18] = 68u8; a[19] = 114u8; a[20] = 101u8; a[21] = 119u8; a[18] = 68u8; a[19] = 114u8; a[20] = 101u8; a[21] = 119u8;
let sp: [1]u8; sp[0] = 32u8; let sp: [1]u8; sp[0] = 32u8;
let t1: [][]u8 = bytes.split(a[0:22], sp[0:1]); let t1: [][]u8 = bytes.split(a[0:22], sp[0:1]);
if (t1.len != 5) { fail(); }; assert(!(t1.len != 5));
expect_tok(t1, 0, a[0:6]); expect_tok(t1, 0, a[0:6]);
expect_tok(t1, 1, a[7:9]); expect_tok(t1, 1, a[7:9]);
expect_tok(t1, 2, a[10:14]); expect_tok(t1, 2, a[10:14]);
@@ -735,12 +686,11 @@ fn expect_tok(toks: [][]u8, i: i32, want: []u8) void = {
// Leading + trailing empty tokens — five entries, three of which // Leading + trailing empty tokens — five entries, three of which
// are []. Splits across all positions exactly the way Hare does. // are []. Splits across all positions exactly the way Hare does.
signalled = 1771;
let b: [5]u8; b[0] = 0u8; b[1] = 1u8; b[2] = 2u8; b[3] = 3u8; b[4] = 0u8; let b: [5]u8; b[0] = 0u8; b[1] = 1u8; b[2] = 2u8; b[3] = 3u8; b[4] = 0u8;
let zd: [1]u8; zd[0] = 0u8; let zd: [1]u8; zd[0] = 0u8;
let z: [1]u8; let z: [1]u8;
let t2: [][]u8 = bytes.split(b[0:5], zd[0:1]); let t2: [][]u8 = bytes.split(b[0:5], zd[0:1]);
if (t2.len != 3) { fail(); }; assert(!(t2.len != 3));
expect_tok(t2, 0, z[0:0]); expect_tok(t2, 0, z[0:0]);
expect_tok(t2, 1, b[1:4]); expect_tok(t2, 1, b[1:4]);
expect_tok(t2, 2, z[0:0]); expect_tok(t2, 2, z[0:0]);
@@ -760,70 +710,61 @@ fn expect_tok(toks: [][]u8, i: i32, want: []u8) void = {
let ec: [1]u8; ec[0] = 99u8; let ec: [1]u8; ec[0] = 99u8;
// ['a','b','c'] along byte 'b' -> ['a'],['c'] // ['a','b','c'] along byte 'b' -> ['a'],['c']
signalled = 1810;
let (b0, a0) = bytes.cut(abc[0:3], 98u8); let (b0, a0) = bytes.cut(abc[0:3], 98u8);
if (!bytes.equal(b0, ea[0:1])) { fail(); }; assert(!(!bytes.equal(b0, ea[0:1])));
if (!bytes.equal(a0, ec[0:1])) { fail(); }; assert(!(!bytes.equal(a0, ec[0:1])));
// same, single-byte []u8 delim -> identical // same, single-byte []u8 delim -> identical
signalled = 1811;
let nb: [1]u8; nb[0] = 98u8; let nb: [1]u8; nb[0] = 98u8;
let (b1, a1) = bytes.cut(abc[0:3], nb[0:1]); let (b1, a1) = bytes.cut(abc[0:3], nb[0:1]);
if (!bytes.equal(b1, ea[0:1])) { fail(); }; assert(!(!bytes.equal(b1, ea[0:1])));
if (!bytes.equal(a1, ec[0:1])) { fail(); }; assert(!(!bytes.equal(a1, ec[0:1])));
// ['b','c'] along 'b' -> [],['c'] (empty before) // ['b','c'] along 'b' -> [],['c'] (empty before)
signalled = 1812;
let bc: [2]u8; bc[0] = 98u8; bc[1] = 99u8; let bc: [2]u8; bc[0] = 98u8; bc[1] = 99u8;
let (b2, a2) = bytes.cut(bc[0:2], 98u8); let (b2, a2) = bytes.cut(bc[0:2], 98u8);
if (!bytes.equal(b2, z[0:0])) { fail(); }; assert(!(!bytes.equal(b2, z[0:0])));
if (!bytes.equal(a2, ec[0:1])) { fail(); }; assert(!(!bytes.equal(a2, ec[0:1])));
// ['a','b'] along 'b' -> ['a'],[] (empty after) // ['a','b'] along 'b' -> ['a'],[] (empty after)
signalled = 1813;
let ab: [2]u8; ab[0] = 97u8; ab[1] = 98u8; let ab: [2]u8; ab[0] = 97u8; ab[1] = 98u8;
let (b3, a3) = bytes.cut(ab[0:2], 98u8); let (b3, a3) = bytes.cut(ab[0:2], 98u8);
if (!bytes.equal(b3, ea[0:1])) { fail(); }; assert(!(!bytes.equal(b3, ea[0:1])));
if (!bytes.equal(a3, z[0:0])) { fail(); }; assert(!(!bytes.equal(a3, z[0:0])));
// delim absent -> (whole input, []) // delim absent -> (whole input, [])
signalled = 1814;
let (b4, a4) = bytes.cut(abc[0:3], 120u8); let (b4, a4) = bytes.cut(abc[0:3], 120u8);
if (!bytes.equal(b4, abc[0:3])) { fail(); }; assert(!(!bytes.equal(b4, abc[0:3])));
if (!bytes.equal(a4, z[0:0])) { fail(); }; assert(!(!bytes.equal(a4, z[0:0])));
// empty input -> ([],[]) // empty input -> ([],[])
signalled = 1815;
let (b5, a5) = bytes.cut(z[0:0], 120u8); let (b5, a5) = bytes.cut(z[0:0], 120u8);
if (!bytes.equal(b5, z[0:0])) { fail(); }; assert(!(!bytes.equal(b5, z[0:0])));
if (!bytes.equal(a5, z[0:0])) { fail(); }; assert(!(!bytes.equal(a5, z[0:0])));
// repeated delim -> FIRST instance: ['a'],['c','b','a'] // repeated delim -> FIRST instance: ['a'],['c','b','a']
signalled = 1816;
let abcba: [5]u8; let abcba: [5]u8;
abcba[0] = 97u8; abcba[1] = 98u8; abcba[2] = 99u8; abcba[0] = 97u8; abcba[1] = 98u8; abcba[2] = 99u8;
abcba[3] = 98u8; abcba[4] = 97u8; abcba[3] = 98u8; abcba[4] = 97u8;
let ecba: [3]u8; ecba[0] = 99u8; ecba[1] = 98u8; ecba[2] = 97u8; let ecba: [3]u8; ecba[0] = 99u8; ecba[1] = 98u8; ecba[2] = 97u8;
let (b6, a6) = bytes.cut(abcba[0:5], 98u8); let (b6, a6) = bytes.cut(abcba[0:5], 98u8);
if (!bytes.equal(b6, ea[0:1])) { fail(); }; assert(!(!bytes.equal(b6, ea[0:1])));
if (!bytes.equal(a6, ecba[0:3])) { fail(); }; assert(!(!bytes.equal(a6, ecba[0:3])));
// 2-byte []u8 delim present — "ab<XY>c" -> ['a','b'],['c'] // 2-byte []u8 delim present — "ab<XY>c" -> ['a','b'],['c']
signalled = 1817;
let h: [5]u8; let h: [5]u8;
h[0] = 97u8; h[1] = 98u8; h[2] = 88u8; h[3] = 89u8; h[4] = 99u8; h[0] = 97u8; h[1] = 98u8; h[2] = 88u8; h[3] = 89u8; h[4] = 99u8;
let xy: [2]u8; xy[0] = 88u8; xy[1] = 89u8; let xy: [2]u8; xy[0] = 88u8; xy[1] = 89u8;
let eab: [2]u8; eab[0] = 97u8; eab[1] = 98u8; let eab: [2]u8; eab[0] = 97u8; eab[1] = 98u8;
let (b7, a7) = bytes.cut(h[0:5], xy[0:2]); let (b7, a7) = bytes.cut(h[0:5], xy[0:2]);
if (!bytes.equal(b7, eab[0:2])) { fail(); }; assert(!(!bytes.equal(b7, eab[0:2])));
if (!bytes.equal(a7, ec[0:1])) { fail(); }; assert(!(!bytes.equal(a7, ec[0:1])));
// 2-byte []u8 delim absent -> (whole, []) // 2-byte []u8 delim absent -> (whole, [])
signalled = 1818;
let zz: [2]u8; zz[0] = 9u8; zz[1] = 9u8; let zz: [2]u8; zz[0] = 9u8; zz[1] = 9u8;
let (b8, a8) = bytes.cut(h[0:5], zz[0:2]); let (b8, a8) = bytes.cut(h[0:5], zz[0:2]);
if (!bytes.equal(b8, h[0:5])) { fail(); }; assert(!(!bytes.equal(b8, h[0:5])));
if (!bytes.equal(a8, z[0:0])) { fail(); }; assert(!(!bytes.equal(a8, z[0:0])));
}; };
@test fn rcut_cases() void = { @test fn rcut_cases() void = {
@@ -831,32 +772,28 @@ fn expect_tok(toks: [][]u8, i: i32, want: []u8) void = {
let ea: [1]u8; ea[0] = 97u8; let ea: [1]u8; ea[0] = 97u8;
// ['a','b','c'] along 'b' -> ['a'],['c'] (single instance == cut) // ['a','b','c'] along 'b' -> ['a'],['c'] (single instance == cut)
signalled = 1820;
let abc: [3]u8; abc[0] = 97u8; abc[1] = 98u8; abc[2] = 99u8; let abc: [3]u8; abc[0] = 97u8; abc[1] = 98u8; abc[2] = 99u8;
let ec: [1]u8; ec[0] = 99u8; let ec: [1]u8; ec[0] = 99u8;
let (b0, a0) = bytes.rcut(abc[0:3], 98u8); let (b0, a0) = bytes.rcut(abc[0:3], 98u8);
if (!bytes.equal(b0, ea[0:1])) { fail(); }; assert(!(!bytes.equal(b0, ea[0:1])));
if (!bytes.equal(a0, ec[0:1])) { fail(); }; assert(!(!bytes.equal(a0, ec[0:1])));
// repeated delim -> LAST instance: ['a','b','c'],['a'] // repeated delim -> LAST instance: ['a','b','c'],['a']
signalled = 1821;
let abcba: [5]u8; let abcba: [5]u8;
abcba[0] = 97u8; abcba[1] = 98u8; abcba[2] = 99u8; abcba[0] = 97u8; abcba[1] = 98u8; abcba[2] = 99u8;
abcba[3] = 98u8; abcba[4] = 97u8; abcba[3] = 98u8; abcba[4] = 97u8;
let eabc: [3]u8; eabc[0] = 97u8; eabc[1] = 98u8; eabc[2] = 99u8; let eabc: [3]u8; eabc[0] = 97u8; eabc[1] = 98u8; eabc[2] = 99u8;
let (b1, a1) = bytes.rcut(abcba[0:5], 98u8); let (b1, a1) = bytes.rcut(abcba[0:5], 98u8);
if (!bytes.equal(b1, eabc[0:3])) { fail(); }; assert(!(!bytes.equal(b1, eabc[0:3])));
if (!bytes.equal(a1, ea[0:1])) { fail(); }; assert(!(!bytes.equal(a1, ea[0:1])));
// delim absent -> (whole input, []) // delim absent -> (whole input, [])
signalled = 1822;
let (b2, a2) = bytes.rcut(abc[0:3], 120u8); let (b2, a2) = bytes.rcut(abc[0:3], 120u8);
if (!bytes.equal(b2, abc[0:3])) { fail(); }; assert(!(!bytes.equal(b2, abc[0:3])));
if (!bytes.equal(a2, z[0:0])) { fail(); }; assert(!(!bytes.equal(a2, z[0:0])));
// 2-byte []u8 delim present, two instances -> cut at LAST. // 2-byte []u8 delim present, two instances -> cut at LAST.
// "<XY>a<XY>b" -> ['X','Y','a'],['b'] // "<XY>a<XY>b" -> ['X','Y','a'],['b']
signalled = 1823;
let h: [6]u8; let h: [6]u8;
h[0] = 88u8; h[1] = 89u8; h[2] = 97u8; h[0] = 88u8; h[1] = 89u8; h[2] = 97u8;
h[3] = 88u8; h[4] = 89u8; h[5] = 98u8; h[3] = 88u8; h[4] = 89u8; h[5] = 98u8;
@@ -864,37 +801,36 @@ fn expect_tok(toks: [][]u8, i: i32, want: []u8) void = {
let exya: [3]u8; exya[0] = 88u8; exya[1] = 89u8; exya[2] = 97u8; let exya: [3]u8; exya[0] = 88u8; exya[1] = 89u8; exya[2] = 97u8;
let eb: [1]u8; eb[0] = 98u8; let eb: [1]u8; eb[0] = 98u8;
let (b3, a3) = bytes.rcut(h[0:6], xy[0:2]); let (b3, a3) = bytes.rcut(h[0:6], xy[0:2]);
if (!bytes.equal(b3, exya[0:3])) { fail(); }; assert(!(!bytes.equal(b3, exya[0:3])));
if (!bytes.equal(a3, eb[0:1])) { fail(); }; assert(!(!bytes.equal(a3, eb[0:1])));
// 2-byte []u8 delim absent -> (whole, []) // 2-byte []u8 delim absent -> (whole, [])
signalled = 1824;
let zz: [2]u8; zz[0] = 9u8; zz[1] = 9u8; let zz: [2]u8; zz[0] = 9u8; zz[1] = 9u8;
let (b4, a4) = bytes.rcut(h[0:6], zz[0:2]); let (b4, a4) = bytes.rcut(h[0:6], zz[0:2]);
if (!bytes.equal(b4, h[0:6])) { fail(); }; assert(!(!bytes.equal(b4, h[0:6])));
if (!bytes.equal(a4, z[0:0])) { fail(); }; assert(!(!bytes.equal(a4, z[0:0])));
}; };
export fn main() i32 = { export fn main() i32 = {
signalled = 1; equal_cases(); equal_cases();
signalled = 2; index_byte_cases(); index_byte_cases();
signalled = 3; index_slice_cases(); index_slice_cases();
signalled = 4; rindex_byte_cases(); rindex_byte_cases();
signalled = 5; rindex_slice_cases(); rindex_slice_cases();
signalled = 6; contains_cases(); contains_cases();
signalled = 7; hasprefix_cases(); hasprefix_cases();
signalled = 8; hassuffix_cases(); hassuffix_cases();
signalled = 9; tokenize_cases(); tokenize_cases();
signalled = 10; rtokenize_cases(); rtokenize_cases();
signalled = 11; peek_token_cases(); peek_token_cases();
signalled = 12; remaining_tokens_cases(); remaining_tokens_cases();
signalled = 13; splitn_cases(); splitn_cases();
signalled = 14; rsplitn_cases(); rsplitn_cases();
signalled = 15; split_cases(); split_cases();
signalled = 16; ltrim_cases(); ltrim_cases();
signalled = 17; rtrim_cases(); rtrim_cases();
signalled = 18; trim_cases(); trim_cases();
signalled = 19; cut_cases(); cut_cases();
signalled = 20; rcut_cases(); rcut_cases();
return 0; return 0;
}; };

View File

@@ -37317,7 +37317,28 @@ fn cgletbody(c: *cgen, n: *node, off: i32) void = {
emitline("\tMOVQ\t$0, "); emitline("\tMOVQ\t$0, ");
emitoff(off: i64); emitoff(off: i64);
emitline("(BP)\n"); emitline("(BP)\n");
} else { if (zsz > 8) { } else { if (zsz == 8) {
// #213: an 8B composite (single-field struct / tagged) is
// neither an 8B primitive nor zsz>8, so it fell through
// un-zeroed while cstage emits MOVQ $0 (cgen.c N_LET
// `else if (sz == 8)`); a read-before-init then saw stack
// garbage (cs!=ww byte-id + a latent garbage-read). Match
// cstage's immediate MOVQ $0, checked BEFORE the run arm
// below so an 8B slot stays one immediate store, not
// XORQ+MOVQ (rule-10 byte-id).
emitline("\tMOVQ\t$0, ");
emitoff(off: i64);
emitline("(BP)\n");
} else { if (zsz > 0) {
// #16: the run arm was gated `zsz > 8`, so a SUB-8
// aggregate (`let c: [3]u8;` = 3, a 3-byte struct, etc.)
// matched no arm and fell through un-zeroed — the exact
// stack-garbage read ken's bytes verdict pinpointed
// (ltrim_cases' `let c: [3]u8;`), BOTH stages, gate-blind
// (#263). cstage widened its `!n->rhs && sz > 8` gate to
// `sz > 0` symmetrically; the MOVL/MOVB tail already sizes
// the run to any 1..7-byte extent. (`[0]T`, zsz == 0, needs
// no stores — the lone XORQ is skipped, matching cstage.)
emitline("\tXORQ\tAX, AX\n"); emitline("\tXORQ\tAX, AX\n");
let zi: i32 = 0; let zi: i32 = 0;
for (zi + 8 <= zsz) { for (zi + 8 <= zsz) {
@@ -37338,19 +37359,6 @@ fn cgletbody(c: *cgen, n: *node, off: i32) void = {
emitline("(BP)\n"); emitline("(BP)\n");
zi += 1; zi += 1;
}; };
} else { if (zsz == 8) {
// #213: an 8B composite (single-field struct / tagged) is
// neither an 8B primitive nor zsz>8, so it fell through
// un-zeroed while cstage emits MOVQ $0 (cgen.c N_LET
// `else if (sz == 8)`); a read-before-init then saw stack
// garbage (cs!=ww byte-id + a latent garbage-read). Match
// cstage. Sub-8 (4B/1B) composites stay un-zeroed — cstage
// doesn't zero them either, so zeroing here would re-
// diverge; that sub-8 read-before-init garbage is a SHARED
// latent, out of this slice's scope.
emitline("\tMOVQ\t$0, ");
emitoff(off: i64);
emitline("(BP)\n");
}; }; }; }; }; };
}; };
c.lastwasreturn = 0; c.lastwasreturn = 0;

View File

@@ -3090,7 +3090,28 @@ fn cgletbody(c: *cgen, n: *node, off: i32) void = {
emitline("\tMOVQ\t$0, "); emitline("\tMOVQ\t$0, ");
emitoff(off: i64); emitoff(off: i64);
emitline("(BP)\n"); emitline("(BP)\n");
} else { if (zsz > 8) { } else { if (zsz == 8) {
// #213: an 8B composite (single-field struct / tagged) is
// neither an 8B primitive nor zsz>8, so it fell through
// un-zeroed while cstage emits MOVQ $0 (cgen.c N_LET
// `else if (sz == 8)`); a read-before-init then saw stack
// garbage (cs!=ww byte-id + a latent garbage-read). Match
// cstage's immediate MOVQ $0, checked BEFORE the run arm
// below so an 8B slot stays one immediate store, not
// XORQ+MOVQ (rule-10 byte-id).
emitline("\tMOVQ\t$0, ");
emitoff(off: i64);
emitline("(BP)\n");
} else { if (zsz > 0) {
// #16: the run arm was gated `zsz > 8`, so a SUB-8
// aggregate (`let c: [3]u8;` = 3, a 3-byte struct, etc.)
// matched no arm and fell through un-zeroed — the exact
// stack-garbage read ken's bytes verdict pinpointed
// (ltrim_cases' `let c: [3]u8;`), BOTH stages, gate-blind
// (#263). cstage widened its `!n->rhs && sz > 8` gate to
// `sz > 0` symmetrically; the MOVL/MOVB tail already sizes
// the run to any 1..7-byte extent. (`[0]T`, zsz == 0, needs
// no stores — the lone XORQ is skipped, matching cstage.)
emitline("\tXORQ\tAX, AX\n"); emitline("\tXORQ\tAX, AX\n");
let zi: i32 = 0; let zi: i32 = 0;
for (zi + 8 <= zsz) { for (zi + 8 <= zsz) {
@@ -3111,19 +3132,6 @@ fn cgletbody(c: *cgen, n: *node, off: i32) void = {
emitline("(BP)\n"); emitline("(BP)\n");
zi += 1; zi += 1;
}; };
} else { if (zsz == 8) {
// #213: an 8B composite (single-field struct / tagged) is
// neither an 8B primitive nor zsz>8, so it fell through
// un-zeroed while cstage emits MOVQ $0 (cgen.c N_LET
// `else if (sz == 8)`); a read-before-init then saw stack
// garbage (cs!=ww byte-id + a latent garbage-read). Match
// cstage. Sub-8 (4B/1B) composites stay un-zeroed — cstage
// doesn't zero them either, so zeroing here would re-
// diverge; that sub-8 read-before-init garbage is a SHARED
// latent, out of this slice's scope.
emitline("\tMOVQ\t$0, ");
emitoff(off: i64);
emitline("(BP)\n");
}; }; }; }; }; };
}; };
c.lastwasreturn = 0; c.lastwasreturn = 0;

View File

@@ -37317,7 +37317,28 @@ fn cgletbody(c: *cgen, n: *node, off: i32) void = {
emitline("\tMOVQ\t$0, "); emitline("\tMOVQ\t$0, ");
emitoff(off: i64); emitoff(off: i64);
emitline("(BP)\n"); emitline("(BP)\n");
} else { if (zsz > 8) { } else { if (zsz == 8) {
// #213: an 8B composite (single-field struct / tagged) is
// neither an 8B primitive nor zsz>8, so it fell through
// un-zeroed while cstage emits MOVQ $0 (cgen.c N_LET
// `else if (sz == 8)`); a read-before-init then saw stack
// garbage (cs!=ww byte-id + a latent garbage-read). Match
// cstage's immediate MOVQ $0, checked BEFORE the run arm
// below so an 8B slot stays one immediate store, not
// XORQ+MOVQ (rule-10 byte-id).
emitline("\tMOVQ\t$0, ");
emitoff(off: i64);
emitline("(BP)\n");
} else { if (zsz > 0) {
// #16: the run arm was gated `zsz > 8`, so a SUB-8
// aggregate (`let c: [3]u8;` = 3, a 3-byte struct, etc.)
// matched no arm and fell through un-zeroed — the exact
// stack-garbage read ken's bytes verdict pinpointed
// (ltrim_cases' `let c: [3]u8;`), BOTH stages, gate-blind
// (#263). cstage widened its `!n->rhs && sz > 8` gate to
// `sz > 0` symmetrically; the MOVL/MOVB tail already sizes
// the run to any 1..7-byte extent. (`[0]T`, zsz == 0, needs
// no stores — the lone XORQ is skipped, matching cstage.)
emitline("\tXORQ\tAX, AX\n"); emitline("\tXORQ\tAX, AX\n");
let zi: i32 = 0; let zi: i32 = 0;
for (zi + 8 <= zsz) { for (zi + 8 <= zsz) {
@@ -37338,19 +37359,6 @@ fn cgletbody(c: *cgen, n: *node, off: i32) void = {
emitline("(BP)\n"); emitline("(BP)\n");
zi += 1; zi += 1;
}; };
} else { if (zsz == 8) {
// #213: an 8B composite (single-field struct / tagged) is
// neither an 8B primitive nor zsz>8, so it fell through
// un-zeroed while cstage emits MOVQ $0 (cgen.c N_LET
// `else if (sz == 8)`); a read-before-init then saw stack
// garbage (cs!=ww byte-id + a latent garbage-read). Match
// cstage. Sub-8 (4B/1B) composites stay un-zeroed — cstage
// doesn't zero them either, so zeroing here would re-
// diverge; that sub-8 read-before-init garbage is a SHARED
// latent, out of this slice's scope.
emitline("\tMOVQ\t$0, ");
emitoff(off: i64);
emitline("(BP)\n");
}; }; }; }; }; };
}; };
c.lastwasreturn = 0; c.lastwasreturn = 0;

271
test/wcc/840_zeroinit_run.c Normal file
View File

@@ -0,0 +1,271 @@
/*
* 840_zeroinit_run — task #16. A bare `let x: T;` (no initializer) must
* zero-fill its slot (Go zero-value, rob's ruling). Before the fix, cgen
* emitted the zero-fill ONLY for 8B-primitive slots and >8B composites; a
* SUB-8 aggregate (`let c: [3]u8;` = 3 bytes, a 3-byte struct, …) matched
* neither arm and fell through to NOTHING, so the slot read whatever the
* stack held.
*
* THE BUG (cstage == wwstage, BOTH wrong — shared gap, NOT rule-10):
* ken's bytes verdict (.ai/ken-bytes16-verdict.md) traced lib/bytes'
* green-but-broken 967 to ltrim_cases' `let c: [3]u8;` reading a prior
* deep-frame sibling's leftover bytes. The masking was a stack-zero
* accident: a bare let on a FRESH frame happens to read 0, so the bug
* only fires when a deep-framed fn ran first. byte-id was BLIND (#263):
* both stages emitted the identical no-store sequence.
*
* REPRO SHAPE (ken's bytes-independent minimal repro, generalised):
* dirty() writes a big local ([64]u8 = 222) to soil the stack region;
* probe() then declares the bare let as its FIRST local — reusing
* dirty()'s slot — and reads it. A correct zero-fill returns 0; the
* pre-fix garbage returned (222 * width) & 0xff (154 for [3]u8).
*
* THE FIX (#16, both stages): widen the no-rhs zero-fill gate from
* `sz > 8` to `sz > 0` (cgen.c N_LET) and add the symmetric `zsz > 0`
* run arm (cgenstmt.ww cglet), so 1..7-byte slots zero through the same
* MOVL/MOVB tail. sz == 8 keeps its immediate MOVQ $0; sz == 0 (`[0]T`)
* needs no stores.
*
* EACH ROW CARRIES BOTH DIMENSIONS (802 model):
* (a) cstage `ww build` + run, asserting exit 0 — pins that the bare let
* reads zero even after a dirtied frame (runtime correctness the
* byte-id gate cannot see).
* (b) w6c vs w6c_ww `.s` cmp — FAILS if the stages diverge (rule-10).
*
* GATE POLARITY: must stay GREEN. A nonzero exit means a bare-let slot
* regressed to reading stack garbage; a byte-id FAIL means the stages
* diverged on the zero-fill emission.
*/
#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_exit; };
/* Every row shares the dirty()→probe() spine: dirty soils the frame,
* probe declares the bare let FIRST and reads it. want_exit is 0 (all
* reads zero). The discriminating rows are the SUB-8 aggregates ([3]u8,
* [5]u8, [7]u8, the 3-byte struct) — the exact gap #16 closed; the
* scalar / [20]u8 / str / slice rows are controls (already zeroed pre-#16
* via the sz==8 and sz>8 arms) pinning no-regression. */
static const struct row rows[] = {
/* the exact ken repro: [3]u8 sub-8 array. Pre-fix returned 154. */
{ "arr3_u8",
"package main;\n"
"fn dirty() void = {\n"
" let big: [64]u8; let i: i32 = 0;\n"
" for (i < 64) { big[i] = 222u8; i += 1; };\n"
"};\n"
"fn probe() i32 = {\n"
" let c: [3]u8;\n"
" return (c[0]: i32) + (c[1]: i32) + (c[2]: i32);\n"
"};\n"
"export fn main() i32 = { dirty(); return probe(); };\n", 0 },
/* sub-8, width 5 — exercises the MOVL+MOVB tail split (4 + 1). */
{ "arr5_u8",
"package main;\n"
"fn dirty() void = {\n"
" let big: [64]u8; let i: i32 = 0;\n"
" for (i < 64) { big[i] = 222u8; i += 1; };\n"
"};\n"
"fn probe() i32 = {\n"
" let c: [5]u8; let acc: i32 = 0; let j: i32 = 0;\n"
" for (j < 5) { acc += (c[j]: i32); j += 1; };\n"
" return acc;\n"
"};\n"
"export fn main() i32 = { dirty(); return probe(); };\n", 0 },
/* sub-8, width 7 — the widest sub-8 extent (MOVL + MOVW + MOVB is
* unreachable under the run's 4/1 tail, so this is MOVL + 3×MOVB). */
{ "arr7_u8",
"package main;\n"
"fn dirty() void = {\n"
" let big: [64]u8; let i: i32 = 0;\n"
" for (i < 64) { big[i] = 222u8; i += 1; };\n"
"};\n"
"fn probe() i32 = {\n"
" let c: [7]u8; let acc: i32 = 0; let j: i32 = 0;\n"
" for (j < 7) { acc += (c[j]: i32); j += 1; };\n"
" return acc;\n"
"};\n"
"export fn main() i32 = { dirty(); return probe(); };\n", 0 },
/* sub-8 STRUCT (3 bytes) — the non-array half of the gap. Read each
* field; a garbage slot would sum nonzero. */
{ "struct3_u8",
"package main;\n"
"type S = struct { a: u8, b: u8, c: u8 };\n"
"fn dirty() void = {\n"
" let big: [64]u8; let i: i32 = 0;\n"
" for (i < 64) { big[i] = 222u8; i += 1; };\n"
"};\n"
"fn probe() i32 = {\n"
" let s: S;\n"
" return (s.a: i32) + (s.b: i32) + (s.c: i32);\n"
"};\n"
"export fn main() i32 = { dirty(); return probe(); };\n", 0 },
/* CONTROL: scalar i32 (sz==8) — zeroed pre-#16 by the MOVQ $0 arm. */
{ "scalar_i32",
"package main;\n"
"fn dirty() void = {\n"
" let big: [64]u8; let i: i32 = 0;\n"
" for (i < 64) { big[i] = 222u8; i += 1; };\n"
"};\n"
"fn probe() i32 = { let x: i32; return x; };\n"
"export fn main() i32 = { dirty(); return probe(); };\n", 0 },
/* CONTROL: [20]u8 (>8) — zeroed pre-#16 by the #84 sz>8 run. */
{ "arr20_u8",
"package main;\n"
"fn dirty() void = {\n"
" let big: [64]u8; let i: i32 = 0;\n"
" for (i < 64) { big[i] = 222u8; i += 1; };\n"
"};\n"
"fn probe() i32 = {\n"
" let c: [20]u8; let acc: i32 = 0; let j: i32 = 0;\n"
" for (j < 20) { acc += (c[j]: i32); j += 1; };\n"
" return acc;\n"
"};\n"
"export fn main() i32 = { dirty(); return probe(); };\n", 0 },
/* CONTROL: bare str header (24B) — rob's declmod `let empty: str;`
* shape. .len must read 0 (zeroed pre-#16 by the sz>8 run). */
{ "str_hdr",
"package main;\n"
"fn dirty() void = {\n"
" let big: [64]u8; let i: i32 = 0;\n"
" for (i < 64) { big[i] = 222u8; i += 1; };\n"
"};\n"
"fn probe() i32 = { let empty: str; return empty.len; };\n"
"export fn main() i32 = { dirty(); return probe(); };\n", 0 },
/* CONTROL: bare slice header (24B) — .len must read 0. */
{ "slice_hdr",
"package main;\n"
"fn dirty() void = {\n"
" let big: [64]u8; let i: i32 = 0;\n"
" for (i < 64) { big[i] = 222u8; i += 1; };\n"
"};\n"
"fn probe() i32 = { let xs: []i32; return xs.len; };\n"
"export fn main() i32 = { dirty(); return probe(); };\n", 0 },
{ NULL, NULL, 0 }
};
static int
slurp_eq(const char *a, const char *b)
{
FILE *fa = fopen(a, "rb");
FILE *fb = fopen(b, "rb");
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
int rc = 0;
for (;;) {
int ca = fgetc(fa);
int cb = fgetc(fb);
if (ca != cb) { rc = -1; break; }
if (ca == EOF) break;
}
fclose(fa); fclose(fb);
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 w6c[1100], w6c_ww[1100];
snprintf(w6c, sizeof w6c, "%s/w6c", bin);
snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin);
if (access(w6c_ww, X_OK) != 0) {
fprintf(stderr, "zeroinit: w6c_ww missing — cannot run the "
"cs==ww byte-id gate\n");
return 1;
}
int n = 0, fail = 0;
for (int i = 0; rows[i].src; i++, n++) {
char src[64];
snprintf(src, sizeof src, "/tmp/wwzi_%d_%d.ww", getpid(), i);
FILE *f = fopen(src, "wb");
if (f == NULL) { fail++; continue; }
fputs(rows[i].src, f);
fclose(f);
/* (a) cstage build + run in a scratch dir. */
char tmpdir[64];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwzi_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
char cmd[2048];
snprintf(cmd, sizeof cmd, "cd %s && %s/ww build %s",
tmpdir, bin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: cstage build failed\n",
rows[i].label);
fail++;
unlink(src); rmdir(tmpdir);
continue;
}
char outbin[128];
const char *base = strrchr(src, '/');
base = base ? base + 1 : src;
snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base);
char *dot = strrchr(outbin, '.');
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
int got = runwait(outbin);
if (got != rows[i].want_exit) {
fprintf(stderr, "row[%s]: cstage exit %d, want %d "
"(bare let read stack garbage?)\n",
rows[i].label, got, rows[i].want_exit);
fail++;
}
unlink(outbin); rmdir(tmpdir);
/* (b) cs==ww byte-id gate. */
char cs_s[64], ws_s[64];
snprintf(cs_s, sizeof cs_s, "/tmp/wwzi_%d_%d_cs.s", getpid(), i);
snprintf(ws_s, sizeof ws_s, "/tmp/wwzi_%d_%d_ww.s", getpid(), i);
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, cs_s, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label);
fail++; unlink(src); continue;
}
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
w6c_ww, ws_s, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww failed\n", rows[i].label);
fail++; unlink(src); unlink(cs_s); continue;
}
if (slurp_eq(cs_s, ws_s) != 0) {
fprintf(stderr, "row[%s]: cstage/wwstage .s DIFFER "
"(rule-10 byte-id violation)\n", rows[i].label);
fail++;
}
unlink(src); unlink(cs_s); unlink(ws_s);
}
if (fail) {
fprintf(stderr, "%d/%d zeroinit tests failed\n", fail, n);
return 1;
}
printf("zeroinit: %d/%d ok (cstage run + cs==ww byte-id)\n", n, n);
return 0;
}