// tuple_elem_slice_len_test — `len(t.N)` where t is a tuple and t.N is a // slice/str-typed element, migrated from test/wcc/903_tuple_elem_slice_len_run.c // (#235). The len() builtin special-cased only a PLAIN N_IDENT slice operand // and an array operand; every other shape fell back to a bare `cgexpr(operand)`, // which for a slice leaves AX=.ptr. A tuple-element read (`t.N`) loads only // AX=.ptr, so `len(t.N)` returned the slice's .ptr word AS the length — a SILENT // miscompile, gate-blind because the bootstrap never does len() on a // slice-typed tuple element. The fix (both stages, byte-id) loads the element's // .len word directly at BP + tuple_off + element_off + 8. Every element has a // DISTINCT length so a dropped/wrong field is caught. Tuples are slice/str-ONLY // here — a leading scalar element exercises a separate mixed-tuple sret // divergence (filed apart, out of scope). package tuple_elem_slice_len_test; fn mk2() ([]u8, []u8) = { let a: []u8; a.len = 3; a.cap = 7; let b: []u8; b.len = 5; b.cap = 9; return (a, b); }; fn mk3() ([]u8, []u8, []u8) = { let a: []u8; a.len = 3; a.cap = 7; let b: []u8; b.len = 5; b.cap = 9; let c: []u8; c.len = 11; c.cap = 13; return (a, b, c); }; fn mksb() (str, []u8) = { let s: str = "abcd"; let b: []u8; b.len = 6; b.cap = 8; return (s, b); }; fn mkbs() ([]u8, str) = { let b: []u8; b.len = 7; b.cap = 9; let s: str = "hi"; return (b, s); }; @test fn two_slice() void = { let t: ([]u8, []u8) = mk2(); assert(len(t.0): i32 == 3); assert(len(t.1): i32 == 5); }; @test fn three_slice() void = { let t: ([]u8, []u8, []u8) = mk3(); assert(len(t.0): i32 == 3); assert(len(t.1): i32 == 5); assert(len(t.2): i32 == 11); }; @test fn str_slice() void = { let t: (str, []u8) = mksb(); assert(len(t.0): i32 == 4); assert(len(t.1): i32 == 6); }; @test fn slice_str() void = { let t: ([]u8, str) = mkbs(); assert(len(t.0): i32 == 7); assert(len(t.1): i32 == 2); };