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.
136 lines
4.0 KiB
Plaintext
136 lines
4.0 KiB
Plaintext
// str_abi_test — runtime contract for the str->24B {ptr,len,cap} 3-reg ABI
|
|
// (Phase 3, str IS []u8), migrated from test/wcc/928_str_abi_run.c. byte-id
|
|
// proves the two stages agree, NOT that the emitted code is correct (a shared
|
|
// miscompile passes byte-id silently); these @test fns pin the *runtime*
|
|
// contract — T1 (`ww test`) runs the asserts, T2 (test-lang-byteid) keeps
|
|
// cs==ww. Covers the ABI dimensions that exercise the cap word and the AX/BX/CX
|
|
// value / AX:DX:CX:R8 tagged+tuple register layout: str literal (cap=len), str
|
|
// arg, str return, str struct field, the (i64,str)/(str,i64) tuple shapes,
|
|
// deref-store `*p=s`, []str index write+read, the s[i] u8-stride byte read, and
|
|
// str widened into a tagged-union variant.
|
|
|
|
package str_abi_test;
|
|
|
|
fn slen(s: str) i32 = { return s.len: i32; };
|
|
|
|
fn greet() str = { return "hello world"; };
|
|
|
|
type box = struct { s: str, n: i32 };
|
|
|
|
fn pairis() (i64, str) = { return (42i64, "hello"); };
|
|
|
|
fn pairsi() (str, i64) = { return ("hi", 7i64); };
|
|
|
|
fn setit(p: *str, v: str) void = { *p = v; };
|
|
|
|
type sv = (str | i64);
|
|
|
|
fn wrapsv(s: str) sv = { return s; };
|
|
|
|
@test fn literal_len_cap() void = {
|
|
// Literal: cap == len for a static literal (no spare storage); .cap on a
|
|
// LOCAL str reads back the new third word.
|
|
let s: str = "hello";
|
|
assert(s.len: i32 == 5);
|
|
assert(s.cap: i32 == 5);
|
|
};
|
|
|
|
@test fn arg_len() void = {
|
|
// str passed as a 3-word arg (ptr,len,cap), len read in the callee; a
|
|
// let-bound str and a bare literal arg.
|
|
let s: str = "hello";
|
|
assert(slen(s) == 5);
|
|
assert(slen("hi") == 2);
|
|
};
|
|
|
|
@test fn return_str() void = {
|
|
// callee returns a str in AX/BX/CX (no AX:DX shuffle — exactly like a
|
|
// slice now).
|
|
let g: str = greet();
|
|
assert(g.len: i32 == 11);
|
|
};
|
|
|
|
@test fn struct_field_store_load() void = {
|
|
// store a str into a 3-word field, read .len and .cap back (field-store
|
|
// routes the base through DX to dodge CX=cap; the cap word is stored, so
|
|
// b.s.cap == len).
|
|
let b: box;
|
|
b.s = "abcd";
|
|
b.n = 7i32;
|
|
assert(b.s.len: i32 == 4);
|
|
assert(b.n == 7);
|
|
assert(b.s.cap: i32 == 4);
|
|
};
|
|
|
|
@test fn tuple_int_str() void = {
|
|
// (i64, str) return: AX=scalar, DX=ptr, CX=len, R8=cap; 32B receive slot.
|
|
let n, s = pairis();
|
|
assert(n: i32 == 42);
|
|
assert(s.len: i32 == 5);
|
|
};
|
|
|
|
@test fn tuple_str_int() void = {
|
|
// (str, i64) return: reversed order, registers keyed by element type not
|
|
// position.
|
|
let s, n = pairsi();
|
|
assert(s.len: i32 == 2);
|
|
assert(n: i32 == 7);
|
|
};
|
|
|
|
@test fn deref_store() void = {
|
|
// `*p = s` writes all three words through the pointer (cap stashed across
|
|
// the pointer eval).
|
|
let s: str = "hello";
|
|
let d: str;
|
|
setit(&d, s);
|
|
assert(d.len: i32 == 5);
|
|
};
|
|
|
|
@test fn index_write_read() void = {
|
|
// []str index write + read: the str-element store pushes cap/len and
|
|
// writes 3 words; the read loads them back.
|
|
let xs: [2]str;
|
|
xs[0] = "hi";
|
|
xs[1] = "abc";
|
|
assert(xs[0].len: i32 == 2);
|
|
assert(xs[1].len: i32 == 3);
|
|
};
|
|
|
|
@test fn str_index_read() void = {
|
|
// `s[i]` strides by the u8 element size (1), routed through the type table
|
|
// post-collapse; guards the N_INDEX str-element stride against a
|
|
// slice-width (24B) miscompute.
|
|
let s: str = "hello";
|
|
assert(s[0]: i32 == 104);
|
|
assert(s[1]: i32 == 101);
|
|
assert(s[4]: i32 == 111);
|
|
};
|
|
|
|
@test fn tagged_union_str_store() void = {
|
|
// str widened into a tagged-union variant: the payload store folds onto
|
|
// the common slice arm (3-word ptr/len/cap + tag). Literal, str-variable,
|
|
// a str returned from a fn into (str | i64), plus the i64 arm so the
|
|
// variant-tag selection is checked both ways.
|
|
let x: sv = "hello";
|
|
match (x) {
|
|
case let s: str => { assert(s.len: i32 == 5); };
|
|
case let n: i64 => { assert(false); };
|
|
};
|
|
let a: str = "world";
|
|
let y: sv = a;
|
|
match (y) {
|
|
case let s: str => { assert(s.len: i32 == 5); };
|
|
case let n: i64 => { assert(false); };
|
|
};
|
|
let z: sv = wrapsv("abcd");
|
|
match (z) {
|
|
case let s: str => { assert(s.len: i32 == 4); };
|
|
case let n: i64 => { assert(false); };
|
|
};
|
|
let w: sv = 42i64;
|
|
match (w) {
|
|
case let s: str => { assert(false); };
|
|
case let n: i64 => { assert(n: i32 == 42); };
|
|
};
|
|
};
|