Files
ww/test/lang/str_massign_store_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

63 lines
2.4 KiB
Plaintext

// str_massign_store_cap_test — a tuple-destructure REASSIGNMENT `a, s = call()`
// (N_MASSIGN) whose str element must write the full 24B {ptr,len,cap} header
// into the str slot, not just {ptr}, migrated from
// test/wcc/939_str_massign_store_cap_run.c (G3 fold). str is 24B since Phase 2
// (#1); the N_MASSIGN arm previously stored the str element with the bare
// scalar path (one word), dropping len/cap. cs==ww held (byte-id-blind), so
// behavioral @test is the net.
//
// N_MASSIGN vs N_MLET: `let a, s = call()` is N_MLET (fresh bindings) and
// ALREADY destructures 3-word. `a, s = call()` with a, s PRE-DECLARED is
// N_MASSIGN (reassignment) — the arm under test. The bindings MUST be
// pre-declared then reassigned WITHOUT `let`, or the parser emits N_MLET and
// this arm is never reached (silent false green).
//
// RHS cap!=len: the returned str is a literal whose .cap is mutated DISTINCT
// from its len. PRE-POISON: s is seeded with a DIFFERENT str (ptr='q', len=4,
// cap=5) via `let s: str = q` — a PROVEN already-3-word let-init copy, which
// both DECLARES s (so the comma-assign is N_MASSIGN) and poisons its slot. A
// broken 1-word store writes only s.ptr, so the read-back observes the poison
// len 4 / cap 5, never 2 / 8. The full-triple assert (s[0]='h'=104, len=2,
// cap=8) plus the scalar guard (a==5) confirm the XOR branch wires both slots.
package str_massign_store_cap_test;
fn mk() (i64, str) = {
let p: str = "hi"; p.cap = 8i32;
return (5i64, p);
};
fn mk2() (str, i64) = {
let p: str = "hi"; p.cap = 8i32;
return (p, 5i64);
};
@test fn massign_store_str_pos1() void = {
// A — `a, s = mk()`, str the 2nd tuple element (l1). a and s are
// pre-declared (so the comma-assign is N_MASSIGN, not N_MLET); s is
// poisoned (cap=5,len=4,'q') by its let-init copy. The G3 store then
// lands the test str (cap=8,len=2,'h') via (DX,CX,R8)->s, AX->a.
let q: str = "qqqq"; q.cap = 5i32;
let a: i64 = 7i64;
let s: str = q;
a, s = mk();
assert(s.cap: i32 == 8);
assert(s.len: i32 == 2);
assert(s[0] == 104u8);
assert(a == 5);
};
@test fn massign_store_str_pos0() void = {
// B — `s, a = mk2()`, str the 1st tuple element (l0). Same poison and
// test values; exercises the s0_is_str routing (DX,CX,R8)->s, AX->a from
// the other XOR side.
let q: str = "qqqq"; q.cap = 5i32;
let s: str = q;
let a: i64 = 7i64;
s, a = mk2();
assert(s.cap: i32 == 8);
assert(s.len: i32 == 2);
assert(s[0] == 104u8);
assert(a == 5);
};