Files
ww/test/lang/chained_dot_struct_copy_test.ww
Hojun-Cho d8e7470555 test: migrate struct/sret round-trip family-7 to test/lang @test, retire C twins (fold-2)
Continue fold-2: migrate the struct-by-value / sret round-trip family from
bespoke build+run C twins to test/lang @test, retiring each 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); the $(TESTS)
headline drops 6. Every assert is a primitive int/u8/bool comparison (no
fmt/strconv in the assert path); each returned struct/tuple FIELD is asserted
individually so a dropped/mis-offset/over-wide word FAILS. 46 @test cases, a
strict superset of the 46 C rows (799=4, 925=10, 930=6, 949odd=7, 949chained=9,
949aggret=10).

  799_tuple_sret_receive_run.c   -> tuple_sret_receive_test.ww    (#10 Fold B: over-4-GP tuple `([]u8,[]u8)` sret RECEIVE — destructure/single-var/return-forward/reassign; len() only on destructured bindings, never len(t.N))
  925_sret_struct_return_run.c   -> sret_struct_return_test.ww     (#23: >24B sret round-trip; 32B/40B/nested/slice-payload, reassign-receive, struct16-by-value-arg #11 collision, N_IDENT return rhs, forward #9 simple/multi-arg/slice)
  930_sret_narrow_field_run.c    -> sret_narrow_field_test.ww      (#33: sret narrow trailing-field copy — bool/u8/i16/i32 + mixed bool+i32+i64 after the 24B slice)
  949_oddstruct_byval_ret_run.c  -> oddstruct_byval_ret_test.ww    (#107: by-value return of odd sub-8 size {3,5,6,7} single-eightbyte + 8/12/24 boundaries)
  949_chained_dot_struct_copy_run.c -> chained_dot_struct_copy_test.ww (#107 sibling: chained-DOT `t.m.l = s` natural-size tail copy {0..7} + via-CX global dest; neighbour z is the oracle)
  949_aggret_source_run.c        -> aggret_source_test.ww          (#272: aggregate return from every addressable source — arrlit/N_DOT/N_INDEX/deref/ident + >24B sret arm + global-receive caller-half)

This family is sret / struct-by-value-return (the #107/#38/#271/#272 ABI area):
all 6 new files are byte-id cs==ww (no fold-5 divergence surfaced). Bump
LANGBYTEID_EXPECTED_MIN 43->49 to ratchet the new corpus floor.
2026-06-22 13:52:41 +09:00

114 lines
3.8 KiB
Plaintext

// chained_dot_struct_copy_test — depth-≥2 chained-DOT struct-field store from a
// struct IDENT source (`t.m.l = s;`, lhs.lhs is itself an N_DOT), migrated from
// test/wcc/949_chained_dot_struct_copy_run.c (#107-class sibling). The copy
// length is the field's NATURAL (non-slot-padded) struct size; cstage's tail
// selector handled only {4,1} and over-MOVQ'd every other tail {2,3,5,6,7},
// clobbering the @packed neighbour `z` that follows the field within 8 bytes —
// silently, and divergent from wwstage's descending 8/4/2/1 ladder. The fix
// aligns cstage UP. The oracle is the neighbour `z` read back AFTER the copy
// (a wrong width over-writes it); T2 keeps the cs==ww net. Coverage = the tail
// widths the bug lived in {2,3,5,6,7} plus boundaries {0,1,4} + the via-CX
// (global dest) arm a BP-local `t` never reaches.
package chained_dot_struct_copy_test;
type L2 = struct { a: u16, b: [8]u8 };
type M2 = struct @packed { l: L2, z: u8 };
type T2 = struct { m: M2, tail: u64 };
type L3 = struct @packed { a: u8, b: u8, c: u8 };
type M3 = struct @packed { l: L3, z: u8 };
type T3 = struct { m: M3, tail: u64 };
type L4 = struct @packed { a: u32 };
type M4 = struct @packed { l: L4, z: u8 };
type T4 = struct { m: M4, tail: u64 };
type L5 = struct @packed { a: u8, b: u32 };
type M5 = struct @packed { l: L5, z: u8 };
type T5 = struct { m: M5, tail: u64 };
type L6 = struct @packed { a: u16, b: u32 };
type M6 = struct @packed { l: L6, z: u8 };
type T6 = struct { m: M6, tail: u64 };
type L7 = struct @packed { a: u8, b: u16, c: u32 };
type M7 = struct @packed { l: L7, z: u8 };
type T7 = struct { m: M7, tail: u64 };
type Lg = struct { a: u16, b: [8]u8 };
type Mg = struct @packed { l: Lg, z: u8 };
type Tg = struct { m: Mg, tail: u64 };
type L1 = struct @packed { a: u8 };
type M1 = struct @packed { l: L1, z: u8 };
type T1 = struct { m: M1, tail: u64 };
type L0 = struct { a: i64, b: i64 };
type M0 = struct { l: L0, z: u8 };
type T0 = struct { m: M0, tail: u64 };
let g_viacx: Tg = Tg { m = Mg { l = Lg { a = 0u16, b = [0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8] }, z = 222u8 }, tail = 0u64 };
@test fn tail2_leaf10() void = {
let s = L2 { a = 0u16, b = [9u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8] };
let t = T2 { m = M2 { l = L2 { a = 0u16, b = [0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8] }, z = 222u8 }, tail = 0u64 };
t.m.l = s;
assert(t.m.z: i32 == 222);
};
@test fn tail3_leaf3() void = {
let s = L3 { a = 1u8, b = 2u8, c = 3u8 };
let t = T3 { m = M3 { l = L3 { a = 0u8, b = 0u8, c = 0u8 }, z = 88u8 }, tail = 0u64 };
t.m.l = s;
assert(t.m.z: i32 == 88);
};
@test fn tail4_leaf4() void = {
let s = L4 { a = 7u32 };
let t = T4 { m = M4 { l = L4 { a = 0u32 }, z = 123u8 }, tail = 0u64 };
t.m.l = s;
assert(t.m.z: i32 == 123);
};
@test fn tail5_leaf5() void = {
let s = L5 { a = 1u8, b = 0u32 };
let t = T5 { m = M5 { l = L5 { a = 0u8, b = 0u32 }, z = 177u8 }, tail = 0u64 };
t.m.l = s;
assert(t.m.z: i32 == 177);
};
@test fn tail6_leaf6() void = {
let s = L6 { a = 1u16, b = 0u32 };
let t = T6 { m = M6 { l = L6 { a = 0u16, b = 0u32 }, z = 99u8 }, tail = 0u64 };
t.m.l = s;
assert(t.m.z: i32 == 99);
};
@test fn tail7_leaf7() void = {
let s = L7 { a = 1u8, b = 2u16, c = 0u32 };
let t = T7 { m = M7 { l = L7 { a = 0u8, b = 0u16, c = 0u32 }, z = 111u8 }, tail = 0u64 };
t.m.l = s;
assert(t.m.z: i32 == 111);
};
@test fn viacx_global_tail2() void = {
let s = Lg { a = 0u16, b = [9u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8] };
g_viacx.m.l = s;
assert(g_viacx.m.z: i32 == 222);
};
@test fn tail1_leaf1() void = {
let s = L1 { a = 5u8 };
let t = T1 { m = M1 { l = L1 { a = 0u8 }, z = 144u8 }, tail = 0u64 };
t.m.l = s;
assert(t.m.z: i32 == 144);
};
@test fn tail0_leaf16() void = {
let s = L0 { a = 5i64, b = 6i64 };
let t = T0 { m = M0 { l = L0 { a = 0i64, b = 0i64 }, z = 200u8 }, tail = 0u64 };
t.m.l = s;
assert(t.m.z: i32 == 200);
};