From 1a57de6ffea7481e770bd5953c941add249ebab2 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Fri, 7 Aug 2026 23:02:56 +0900 Subject: [PATCH] test/lang: absorb retired-carrier coverage local_zeroinit_test.ww takes the 840/944 zero-init seam (dirty-frame prime and probe share one @test because the runtime forks per test); alias_cgen_b6, alloc_nested_field, array_static_init, and strarray_static gain the rows their retired wrappers held; the remaining files re-point reject-row citations at the r788_* and stage-matrix fixtures. --- test/lang/alias_cgen_b6_test.ww | 12 +- test/lang/alias_global_decl_test.ww | 4 +- test/lang/alloc_nested_field_test.ww | 26 +++- test/lang/array_static_init_test.ww | 21 ++++ test/lang/arrlit_slice_test.ww | 5 +- test/lang/local_zeroinit_test.ww | 182 +++++++++++++++++++++++++++ test/lang/strarray_static_test.ww | 11 +- test/lang/type_value_shadow_test.ww | 4 +- 8 files changed, 247 insertions(+), 18 deletions(-) create mode 100644 test/lang/local_zeroinit_test.ww diff --git a/test/lang/alias_cgen_b6_test.ww b/test/lang/alias_cgen_b6_test.ww index e6393eaf..cf56216c 100644 --- a/test/lang/alias_cgen_b6_test.ww +++ b/test/lang/alias_cgen_b6_test.ww @@ -169,12 +169,16 @@ type c3es_my32b = c3es_my32; }; type c3kw_my32 = u32; -type c3kw_S = struct { arr: [3]c3kw_my32, n: int }; +type c3kw_my32b = c3kw_my32; +type c3kw_S = struct { arr: [3]c3kw_my32b, tail: int }; @test fn kw1_101() void = { - let s = c3kw_S { arr = [10u32, 20u32, 30u32], n = 4 }; - assert(s.arr[0] + s.arr[1] + s.arr[2] == 60u32); - assert(s.n == 4); + let s = c3kw_S { arr = [10u32, 20u32, 30u32], tail = 77 }; + assert(size(c3kw_my32b) == 4); + assert(s.arr[0] == 10u32); + assert(s.arr[1] == 20u32); + assert(s.arr[2] == 30u32); + assert(s.tail == 77); }; type c3kc_my16 = u16; diff --git a/test/lang/alias_global_decl_test.ww b/test/lang/alias_global_decl_test.ww index 5e9531b5..10a15316 100644 --- a/test/lang/alias_global_decl_test.ww +++ b/test/lang/alias_global_decl_test.ww @@ -7,8 +7,8 @@ // on the UNCHASED N_TARRAY tnode so an alias-typed global ARRAY got `undefined // reference to main.g`. Both fixed by ONE tichase at the dispatch entry. // -// This table is the PERMANENT GUARD: the global decl path is lint-invisible -// (ww side is a no-peel consumer; cs helpers spell the one chased accessor). +// This table is the PERMANENT GUARD for the global-declaration dispatch path: +// ww starts from a raw type node while cs starts from resolved type metadata. // Values exceed 255 (no little-endian prefix-luck) and readbacks assert the // LAST element. The C driver ran each row both stages + asserted cs==ww exit; // here the cstage run is test-lang (T1) and byte-id is test-lang-byteid (T2). diff --git a/test/lang/alloc_nested_field_test.ww b/test/lang/alloc_nested_field_test.ww index 8ada193b..9a2dbd9e 100644 --- a/test/lang/alloc_nested_field_test.ww +++ b/test/lang/alloc_nested_field_test.ww @@ -9,12 +9,11 @@ // helper in DST_PTR_SP mode (base reloaded from the pushed heap ptr at // (SP)), which recurses to arbitrary depth. // -// Every row reads the heap struct back via `let o: T = *p` (a whole- -// struct deref-load to a BP-rel local) so the assertions never traverse -// the chained-dot-through-heap-ptr read path (a separate pre-existing -// cs!=ww the C7c fill fix does not touch). Each row asserts the nested -// leaf VALUE (reddens when the fix is reverted: the dropped leaf reads -// 0) AND the sibling scalar leaves before/after it (no clobber). +// The nested-field rows read the heap struct back via `let o: T = *p` so +// they isolate heap filling from chained pointer traversal. The alias-head and +// exact direct control both read p.x/p.y: together they preserve the old #26 +// wrapper's alias-vs-non-alias comparison. Every row asserts the affected +// values independently. package alloc_nested_field_test; @@ -28,6 +27,21 @@ type WithArr = struct { lead: i64, arr: [3]i64, trail: i64 }; type TwoNest = struct { head: i64, s: Inner, arr: [2]i64, tail: i64 }; +type AliasPoint = struct { x: i64, y: i64 }; +type AliasPt = AliasPoint; + +@test fn alias_named_struct() void = { + let p: *AliasPt = alloc(AliasPt{ x = 1, y = 2 })!; + assert(p.x == 1); + assert(p.y == 2); +}; + +@test fn direct_named_struct_control() void = { + let p: *AliasPoint = alloc(AliasPoint{ x = 1, y = 2 })!; + assert(p.x == 1); + assert(p.y == 2); +}; + @test fn depth1_nested_struct() void = { // Inner{q=10} is the field VALUE of x; pre-fix x.q dropped to 0. let p = alloc(Outer{ x = Inner{ q = 10 }, y = 5 })!; diff --git a/test/lang/array_static_init_test.ww b/test/lang/array_static_init_test.ww index a9307ce0..29347cae 100644 --- a/test/lang/array_static_init_test.ww +++ b/test/lang/array_static_init_test.ww @@ -11,12 +11,18 @@ // array-field elements directly (D1.buf[k], D2.m[i][j]): the old #137/#150 cs≠ww // global-struct-field-base bug is CLOSED at HEAD, so the read is now cs==ww IDENT // — the original deferred this to byte-id only because the bug was then open. +// The under-length and `...` rows distinguish the two scalar-tail policies: +// ordinary omitted slots are zero, while an explicit repeat marker replays the +// last value. Every slot is asserted independently so neither policy can mask +// the other through a sum. package array_static_init_test; let A_U8: [4]u8 = [1u8, 2u8, 3u8, 4u8]; let A_U32: [4]u32 = [1u32, 2u32, 3u32, 4u32]; let A_U64: [2]u64 = [1u64, 2u64]; +let A_U64_TAIL: [4]u64 = [1, 2]; +let A_U64_REPEAT: [4]u64 = [7, 9...]; let A_INEG: [4]i32 = [-1, -2, -3, -4]; let A_F64: [4]f64 = [1.5, -2.5, 3.5, -4.5]; let A_F32: [4]f32 = [1.5f32, -2.5f32, 3.5f32, -4.5f32]; @@ -53,6 +59,21 @@ fn at(i: i32, j: i32) u64 = { return A2V[i][j]; }; @test fn let_u8_arr() void = { assert(A_U8[0]: i32 == 1); }; @test fn let_u32_arr() void = { assert(A_U32[0]: i32 == 1); }; @test fn let_u64_arr() void = { assert(A_U64[0]: i32 == 1); }; + +@test fn let_u64_zero_tail() void = { + assert(A_U64_TAIL[0] == 1u64); + assert(A_U64_TAIL[1] == 2u64); + assert(A_U64_TAIL[2] == 0u64); + assert(A_U64_TAIL[3] == 0u64); +}; + +@test fn let_u64_repeat_tail() void = { + assert(A_U64_REPEAT[0] == 7u64); + assert(A_U64_REPEAT[1] == 9u64); + assert(A_U64_REPEAT[2] == 9u64); + assert(A_U64_REPEAT[3] == 9u64); +}; + @test fn let_i32_neg_arr() void = { assert(A_INEG[0] == -1); }; @test fn let_f64_arr() void = { diff --git a/test/lang/arrlit_slice_test.ww b/test/lang/arrlit_slice_test.ww index 21a67c44..edb4b72a 100644 --- a/test/lang/arrlit_slice_test.ww +++ b/test/lang/arrlit_slice_test.ww @@ -1,8 +1,7 @@ // arrlit_slice_test — #25/#31: a one-step array-LITERAL initialiser for a SLICE // local (`let xs: []T = [..]`). Migrated from test/wcc/953_arrlit_slice_run.c -// (value rows; cs==ww byte-id rides T2). Reject rows (out-of-range element + -// the three non-let-borrow contexts) stay as runww //ww:error carriers under -// test/wcc/data/. +// (value rows; cs==ww byte-id rides T2). Reject rows, including the +// stage-asymmetric assignment diagnostic, live under test/wcc/data/. // // #31 (silent cs!=ww): the borrow wrapped the un-addressable arrlit directly, // so .ptr pointed at garbage (xs[1] returned 1, not 20; []u8/[]str SEGV). #25 diff --git a/test/lang/local_zeroinit_test.ww b/test/lang/local_zeroinit_test.ww new file mode 100644 index 00000000..80e0e7b5 --- /dev/null +++ b/test/lang/local_zeroinit_test.ww @@ -0,0 +1,182 @@ +// local_zeroinit_test preserves the retired C wrappers' dirty-frame runtime +// seam. Each @test child +// dirties a callee frame and immediately calls a sibling probe whose bare local +// is its first slot. Keeping the calls in one @test matters: the native runner +// forks once per test, so separate dirty and probe tests could receive clean +// pages and mask a missing zero-fill. + +package local_zeroinit_test; + +type tiny3 = struct { a: u8, b: u8, c: u8 }; + +// Preserve the two original poison frames. Besides their distinct byte values, +// the 512-byte int frame exercises the larger array cases while the 64-byte u8 +// frame is the historical sub-8 aggregate reproducer. +fn dirty_int_frame() int = { + let j: [64]int; + for (let i: int = 0; i < 64; i += 1) { + j[i] = 165; + }; + return j[0]; +}; + +fn dirty_byte_frame() void = { + let big: [64]u8; + let i: i32 = 0; + for (i < 64) { + big[i] = 222u8; + i += 1; + }; +}; + +fn probe_int3() i32 = { + let a: [3]int; + return (a[0] + a[1] + a[2]): i32; +}; + +fn probe_u32x4() i32 = { + let a: [4]u32; + return (a[0] + a[1] + a[2] + a[3]): i32; +}; + +fn probe_u8x20_int_frame() i32 = { + let b: [20]u8; + let s: i32 = 0; + for (let i: int = 0; i < 20; i += 1) { + s += b[i]: i32; + }; + return s; +}; + +fn probe_int2x2() i32 = { + let m: [2][2]int; + return (m[0][0] + m[0][1] + m[1][0] + m[1][1]): i32; +}; + +fn probe_initialized_int3() i32 = { + let c: [3]int = [7, 8, 9]; + return (c[0] + c[1] + c[2]): i32; +}; + +@test fn array_int3_zero() void = { + dirty_int_frame(); + assert(probe_int3() == 0); +}; + +@test fn array_u32x4_zero() void = { + dirty_int_frame(); + assert(probe_u32x4() == 0); +}; + +@test fn array_u8x20_zero() void = { + dirty_int_frame(); + assert(probe_u8x20_int_frame() == 0); +}; + +@test fn nested_array_int2x2_zero() void = { + dirty_int_frame(); + assert(probe_int2x2() == 0); +}; + +@test fn initialized_array_control() void = { + dirty_int_frame(); + assert(probe_initialized_int3() == 24); +}; + +fn probe_u8x3() i32 = { + let c: [3]u8; + return (c[0]: i32) + (c[1]: i32) + (c[2]: i32); +}; + +fn probe_u8x5() i32 = { + let c: [5]u8; + let acc: i32 = 0; + let j: i32 = 0; + for (j < 5) { + acc += c[j]: i32; + j += 1; + }; + return acc; +}; + +fn probe_u8x7() i32 = { + let c: [7]u8; + let acc: i32 = 0; + let j: i32 = 0; + for (j < 7) { + acc += c[j]: i32; + j += 1; + }; + return acc; +}; + +fn probe_tiny3() i32 = { + let s: tiny3; + return (s.a: i32) + (s.b: i32) + (s.c: i32); +}; + +fn probe_scalar_i32() i32 = { + let x: i32; + return x; +}; + +fn probe_u8x20_byte_frame() i32 = { + let c: [20]u8; + let acc: i32 = 0; + let j: i32 = 0; + for (j < 20) { + acc += c[j]: i32; + j += 1; + }; + return acc; +}; + +fn probe_str_len() i32 = { + let empty: str; + return empty.len; +}; + +fn probe_slice_len() i32 = { + let xs: []i32; + return xs.len; +}; + +@test fn sub8_array_u8x3_zero() void = { + dirty_byte_frame(); + assert(probe_u8x3() == 0); +}; + +@test fn sub8_array_u8x5_zero() void = { + dirty_byte_frame(); + assert(probe_u8x5() == 0); +}; + +@test fn sub8_array_u8x7_zero() void = { + dirty_byte_frame(); + assert(probe_u8x7() == 0); +}; + +@test fn sub8_struct_u8x3_zero() void = { + dirty_byte_frame(); + assert(probe_tiny3() == 0); +}; + +@test fn scalar_i32_control() void = { + dirty_byte_frame(); + assert(probe_scalar_i32() == 0); +}; + +@test fn array_u8x20_control() void = { + dirty_byte_frame(); + assert(probe_u8x20_byte_frame() == 0); +}; + +@test fn str_header_control() void = { + dirty_byte_frame(); + assert(probe_str_len() == 0); +}; + +@test fn slice_header_control() void = { + dirty_byte_frame(); + assert(probe_slice_len() == 0); +}; diff --git a/test/lang/strarray_static_test.ww b/test/lang/strarray_static_test.ww index 5bdd802c..b634fb6d 100644 --- a/test/lang/strarray_static_test.ww +++ b/test/lang/strarray_static_test.ww @@ -7,7 +7,8 @@ // .len-only check would miss a wrong/swapped element relocation. The empty-slot // and `...` repeat-suffix rows pin that an empty element doesn't shift the // following element's reloc and that the repeat-fill row's DATAR resolves to the -// last explicit element. +// last explicit element. The under-length row separately pins that omitted +// elements have zero-length headers rather than replaying the last string. package strarray_static_test; @@ -16,6 +17,7 @@ let TPTR: [3]str = ["A", "B", "C"]; let TVAR: [3]str = ["xx", "yyy", "z"]; let TEMP: [2]str = ["", "Z"]; let TREP: [3]str = ["X", "Y"...]; +let TTAIL: [4]str = ["ab", "cde"]; @test fn strtab_len() void = { assert((TLEN[0].len + TLEN[1].len + TLEN[2].len): i32 == 6); @@ -42,3 +44,10 @@ let TREP: [3]str = ["X", "Y"...]; let p: *u8 = TREP[2].ptr; assert((*p): i32 == 89); }; + +@test fn strtab_zero_tail() void = { + assert(TTAIL[0].len == 2); + assert(TTAIL[1].len == 3); + assert(TTAIL[2].len == 0); + assert(TTAIL[3].len == 0); +}; diff --git a/test/lang/type_value_shadow_test.ww b/test/lang/type_value_shadow_test.ww index efe8c299..1b2b89f2 100644 --- a/test/lang/type_value_shadow_test.ww +++ b/test/lang/type_value_shadow_test.ww @@ -1,8 +1,8 @@ // type_value_shadow_test — #225: a value binding that shadows a same-named TYPE // must NOT hide that type in type-annotation or cast position. ww keeps type and // value namespaces separate. Migrated from test/wcc/788_type_value_shadow_run.c -// (value rows; cs==ww byte-id rides T2). The value-in-type-position reject row -// (no same-named type) stays as a carrier pin. +// (value rows; cs==ww byte-id rides T2). The value-in-type-position reject and +// its positive control are stage-matrix fixtures under r788_*. // // Pre-fix cstage resolved the type name via the kind-blind lookup, so the inner // value shadowed the global type → "unknown type". The fix is a kind-filtered