Migrate the slice/str-header core of drew's Family 1 from bespoke build+run C twins to test/lang @test, retiring each now-redundant twin in the same commit. Runtime coverage MOVES from $(TESTS) to test-lang (T1 runs+asserts via `ww test`) + test-lang-byteid (T2 keeps cs==ww .s byte-id); coverage is preserved, the $(TESTS) headline drops 6 (440->434). All asserts are primitive int/u8/bool comparisons (no fmt/strconv in the assert path); the slice-store families poison the slot (cap!=len) and read it back so a dropped data word FAILS. 928_str_abi_run.c -> str_abi_test.ww (str 24B ABI: .len/.cap across literal/arg/return/field/tuple/deref/index/tagged) 941_slice_store_cap_run.c -> slice_store_cap_test.ww (slice value store through indexed/field/chained lhs writes full 24B header; cap==8) 942_subslice_cap_run.c -> subslice_cap_test.ww (sub-slice cap = base_cap-lo; array/slice/str/append-no-realloc/hi-default) 943_subslice_ptresz_run.c -> subslice_ptresz_test.ww (sub-slice ptr advances lo*esz bytes; esz 2/4/8, let + call-arg) 944_deref_slice_store_run.c-> deref_slice_store_test.ww(*p=sliceval whole-deref store writes 24B header; cap==8) 949_f6_header_run.c -> f6_header_test.ww (str/slice header partial load/store; .cap/.len after clobber) Bump LANGBYTEID_EXPECTED_MIN 16->22 to ratchet the new corpus floor.
105 lines
3.7 KiB
Plaintext
105 lines
3.7 KiB
Plaintext
// f6_header_test — 24B str/slice header partial load/store; .cap/.len read back
|
|
// after a clobber, migrated from test/wcc/949_f6_header_run.c. Three closed
|
|
// sub-bugs, all cs==ww-blind at the byte-id gates so a behavioral @test is the
|
|
// net:
|
|
// #19 (align-UP, wwstage-only): a str->[]u8 cast from a NON-LOCAL source
|
|
// (global ident / struct field / call result) missed the cap=len synth,
|
|
// leaving CX = the str's stale word-16 garbage cap.
|
|
// #28 (#263 both-stages): a tuple slice-element read `t.N` over a ([]u8,i64)
|
|
// loaded only the ptr word — a SLICE element fell to the scalar tail, so
|
|
// len/cap took stale registers across a clobber() call.
|
|
// #29 (#263 both-stages): a chained-dot str leaf `o.i.s` (depth-2) dropped the
|
|
// cap word, so a junk strlit before the chain left CX stale.
|
|
// All asserted post-fix both-stages-CORRECT; the runtime read-back with DISTINCT
|
|
// values (cap=3/5, len=5/6) is the net a byte-id gate alone was blind to.
|
|
|
|
package f6_header_test;
|
|
|
|
let g_abc: str = "abc";
|
|
|
|
type box = struct { s: str, x: i64 };
|
|
|
|
type innerc = struct { s: str, x: i64 };
|
|
type outerc = struct { i: innerc, y: i64 };
|
|
|
|
type innero = struct { pad: i64, s: str };
|
|
type outero = struct { i: innero, y: i64 };
|
|
|
|
fn castg() []u8 = { return g_abc: []u8; };
|
|
|
|
fn clobber() i64 = {
|
|
let a: i64 = 111;
|
|
let b: i64 = 222;
|
|
return a + b;
|
|
};
|
|
|
|
@test fn global_str_cap() void = {
|
|
// #19: str->[]u8 cast from a GLOBAL ident source, threaded through a call
|
|
// return. cap must == len == 3. Pre-fix wwstage left CX = stale word-16.
|
|
let s: []u8 = castg();
|
|
assert(s.cap: i32 == 3);
|
|
};
|
|
|
|
@test fn field_str_cap() void = {
|
|
// #19 sibling: str->[]u8 cast from a struct FIELD source; distinct len (5)
|
|
// catches a dropped cap.
|
|
let b: box = box { s = "hello", x = 0 };
|
|
let v: []u8 = b.s: []u8;
|
|
assert(v.cap: i32 == 5);
|
|
};
|
|
|
|
@test fn local_str_cap() void = {
|
|
// #19 control: LOCAL str source (the pre-fix-covered shape) still emits the
|
|
// synth — guards the common case against regression.
|
|
let s: str = "abcd";
|
|
let v: []u8 = s: []u8;
|
|
assert(v.cap: i32 == 4);
|
|
};
|
|
|
|
@test fn tuple_slice_elem() void = {
|
|
// #28: tuple slice-element read `t.0` over a ([]u8, i64). A clobber()
|
|
// between the build and the read leaves junk in BX/CX; pre-fix the
|
|
// str-only arm fell to the scalar tail (ptr word only), so ys.len read
|
|
// stale BX. len == 5.
|
|
let xs: []u8 = [1u8, 2u8, 3u8, 4u8, 5u8];
|
|
let t: ([]u8, i64) = (xs, 7);
|
|
let junk: i64 = clobber();
|
|
let ys: []u8 = t.0;
|
|
assert(ys.len: i32 == 5);
|
|
assert(junk == 333);
|
|
};
|
|
|
|
@test fn tuple_slice_elem_pos1() void = {
|
|
// #28 sibling: the slice element sits at tuple position 1 (an i64 occupies
|
|
// slot 0, the []u8 header starts at +8). Exercises the widened arm's offset
|
|
// arithmetic. len == 6.
|
|
let xs: []u8 = [1u8, 2u8, 3u8, 4u8, 5u8, 6u8];
|
|
let t: (i64, []u8) = (9, xs);
|
|
let junk: i64 = clobber();
|
|
let ys: []u8 = t.1;
|
|
assert(ys.len: i32 == 6);
|
|
assert(junk == 333);
|
|
};
|
|
|
|
@test fn chained_str_cap() void = {
|
|
// #29: chained-dot str leaf `o.i.s` (depth-2) dropped the cap word — a junk
|
|
// strlit before the chain left CX stale, so t.cap read 2 (junk's cap)
|
|
// instead of 5. Now the str leaf loads all three header words.
|
|
let iv: innerc = innerc { s = "hello", x = 0 };
|
|
let o: outerc = outerc { i = iv, y = 0 };
|
|
let junk: str = "ab";
|
|
let t: str = o.i.s;
|
|
assert(t.cap: i32 == 5);
|
|
};
|
|
|
|
@test fn chained_str_cap_offset() void = {
|
|
// #29 sibling: the str leaf sits at a NON-ZERO field offset (a leading i64
|
|
// pad pushes `s` to +8). Exercises the chained-leaf offset arithmetic;
|
|
// cap == len == 7.
|
|
let iv: innero = innero { pad = 0, s = "worldly" };
|
|
let o: outero = outero { i = iv, y = 0 };
|
|
let junk: str = "ab";
|
|
let t: str = o.i.s;
|
|
assert(t.cap: i32 == 7);
|
|
};
|