// #12: an aggregate (struct) success-variant unwrap // `(S|e)!` / `r?` dropped payload words (SILENT, both stages, byte-id-BLIND). // A struct success variant rides the in-cap {AX=w0,DX=w1,CX=w2} GP ABI — a // DIFFERENT shuffle from #6's str/slice {ptr,len,cap} header. Two layers: // L1 PRODUCER (cgtrytaggedshift / cgen.c N_TRYPROP+N_TRYUNW): a struct/array // success variant matched no arm and fell to the bare `MOVQ DX,AX`, // materialising only w0 — w1/w2 (in CX/R8) dropped. // L2 CONSUMER (the aggregate-STORE arms): gated rhs.kind==N_CALL and stored // one word for an N_TRYUNW/N_TRYPROP rhs — admitting the unwrap kinds // reuses the #10-correct scratch materialise. // Both layers are silent both-stage, so the byte-id gate is BLIND to the drop — // these VALUE asserts are the sole tooth. Reverting either stage's L2 relax (or // the shared L1 producer) reverts the dropped word to the poison sentinel 9 and // reddens. POISON-SEED every dest member to 9 so a dropped w1/w2 reads 9 != the // expected nonzero value (a fresh-frame 0 would also mismatch, but 9 is robust). // // Coverage = the three SILENT store shapes (arr-elem C2c, single-dot field // direct + via-ptr, indexed-field #11) for a clean 2-eightbyte s2 (w1 tooth) // plus a 3-eightbyte s24 (w2 tooth — exercises the producer's R8->CX shift). // The sub-8-tail-THROUGH-unwrap case (#10's tail) is NOT pinnable: an ARRAY // success variant is checker-rejected (`([7]i16|e)`, task #5/#60) and a sub-8- // tail STRUCT success variant trips a SEPARATE pre-existing bug — the union- // return struct-lit fill overruns saved BP when the variant ends at the frame // edge (g's MOVQ writes past the 14B struct into [BP]); filed for the lead. package struct_unwrap_test; type e = !i32; type s2 = struct { a: i64, b: i64 }; // 2 eightbytes: w0=a, w1=b type s24 = struct { a: i64, b: i64, c: i64 }; // 3 eightbytes: w0,w1,w2 type box = struct { f: s2, g: i64 }; fn mk2() (s2 | e) = { return s2 { a = 111i64, b = 222i64 }; }; fn mk24() (s24 | e) = { return s24 { a = 111i64, b = 222i64, c = 333i64 }; }; @test fn arr_elem() void = { // L2 site: C2c whole-element arr[i]=mk()! let arr: [2]s2; arr[1].a = 9i64; arr[1].b = 9i64; // poison: w1 sentinel 9 != 222 arr[1] = mk2()!; assert(arr[1].a == 111i64); assert(arr[1].b == 222i64); // w1 — the dropped-word tooth }; @test fn arr_elem_w2() void = { // C2c, 3 eightbytes — w2 (R8->CX) tooth let arr: [2]s24; arr[1].a = 9i64; arr[1].b = 9i64; arr[1].c = 9i64; arr[1] = mk24()!; assert(arr[1].a == 111i64); assert(arr[1].b == 222i64); assert(arr[1].c == 333i64); // w2 tooth (>16B producer shift) }; @test fn field_direct() void = { // L2 site: single-dot direct b.f=mk()! let b: box = box { f = s2{a=9i64,b=9i64}, g = 7i64 }; b.f = mk2()!; assert(b.f.a == 111i64); assert(b.f.b == 222i64); // w1 tooth assert(b.g == 7i64); // neighbour field intact }; @test fn field_via_ptr() void = { // L2 site: single-dot via *struct p.f=mk()! let b: box = box { f = s2{a=9i64,b=9i64}, g = 7i64 }; let p: *box = &b; p.f = mk2()!; assert(b.f.a == 111i64); assert(b.f.b == 222i64); // w1 tooth assert(b.g == 7i64); }; @test fn field_of_indexed() void = { // L2 site: #11 indexed-field arr[i].f=mk()! let arr: [2]box; arr[1].g = 5i64; arr[1].f.a = 9i64; arr[1].f.b = 9i64; arr[1].f = mk2()!; assert(arr[1].f.a == 111i64); assert(arr[1].f.b == 222i64); // w1 tooth assert(arr[1].g == 5i64); // neighbour field intact };