Files
ww/test/lang/str_arrfield_cap_test.ww
Hojun-Cho e1740fff10 test: migrate 933-939 str-cap family to test/lang @test
Fan out the str-cap read (933-936) and store (937-939) families into in-language @test files, following the 932 str_elem_cap template. Additive: the *_run.c stay in the C corpus (they are the only wwstage-runtime net for these cs==ww byte-id-blind shapes); de-dup deferred to fold 6.

Per-shape @test fns, not a data table: each fn varies the codegen shape (base reg / chain depth / tuple return-ABI / store position), so the row-array idiom (blocked by #111) would lose coverage. Read family keeps the spoil()/register-clobber + junk==44 discrimination where the .c has it; store family pre-poisons the slot via a path distinct from the store under test. Asserts are primitives only.
2026-06-22 03:54:54 +09:00

82 lines
2.9 KiB
Plaintext

// str_arrfield_cap_test — a str-typed FIELD of an INDEXED element `arr[i].f`
// (N_INDEX-rooted N_DOT) must load the full 24B {ptr,len,cap} header, not just
// {ptr,len}, migrated from test/wcc/936_str_arrfield_cap_run.c (C4.6 arrfield,
// the LAST member of the 3-word-value-read cluster). str is 24B since Phase 2
// (#1); pre-arrfield the `arr[i].f` str arm loaded 2 words (len in BX, ptr in
// AX) and dropped cap. The miscompile was cs==ww, so the 990-997 byte-id gates
// stayed GREEN while the runtime was wrong — behavioral @test is the net. There
// is no adjacent slice-element sibling at this leaf, so the 3-word triple is
// authored directly to the canonical slice-header ABI (len->BX, cap->CX,
// ptr->AX LAST).
//
// POISONING: the store side of `arr[i].f = v` is a separate, still-broken
// store-side gap (filed; out of scope for this read-side fold). So the value
// elements (A, B) are poisoned through `&arr[i]` + a *struct field write, which
// lands a real cap into the element's +16 word; D points its element at a
// separately-built struct (the proven s1local 3-word field store).
//
// DISCRIMINATION: a 2-word read leaves CX holding whatever the index scale-
// multiply left there, never the poison; spoil() additionally interposes a
// CX-clobbering call between the build and the `arr[i].f` read, so a broken
// 2-word read observes spoil's leftover (44), never the poison cap.
package str_arrfield_cap_test;
type rec = struct { f: str };
fn spoil() i32 = {
let z: str = "zzzz";
z.cap = 44i32;
let w: str = z;
return w.cap: i32;
};
@test fn arrfield_array_value() void = {
// A — `arr[i].f` value element of a [N]rec local array (LEAQ base).
// Poison cap=8 (len=2) via &arr[1] + a *struct field store.
let p: str = "hi";
p.cap = 8i32;
let arr: [3]rec;
let pr: *rec = &arr[1];
pr.f = p;
let junk: i32 = spoil();
let s: str = arr[1].f;
assert(s.cap: i32 == 8);
assert(s.len: i32 == 2);
assert(junk == 44);
};
@test fn arrfield_slice_value() void = {
// B — `sl[i].f` value element of a []rec local slice (MOVQ slice.ptr
// base) viewing the same poisoned backing array. Poison cap=9 (len=5).
let p: str = "world";
p.cap = 9i32;
let arr: [3]rec;
let pr: *rec = &arr[1];
pr.f = p;
let sl: []rec = arr[0:3];
let junk: i32 = spoil();
let s: str = sl[1].f;
assert(s.cap: i32 == 9);
assert(s.len: i32 == 5);
assert(junk == 44);
};
@test fn arrfield_ptr_elem() void = {
// D — `arr[i].f` pointer element of a [N]*rec array (LEAQ base, MOVQ
// deref to the *rec, then the leaf field load). The element points at a
// separately-built struct so the poison rides the proven s1local 3-word
// field store, not the broken array-element store. Poison cap=7 (len=3).
let p: str = "abc";
p.cap = 7i32;
let st: rec;
st.f = p;
let arr: [3]*rec;
arr[1] = &st;
let junk: i32 = spoil();
let s: str = arr[1].f;
assert(s.cap: i32 == 7);
assert(s.len: i32 == 3);
assert(junk == 44);
};