Files
ww/test/lang/idx_dot_src_aggregate_runonly_test.ww
Hojun-Cho 3719ff1c64 cgen: copy all eightbytes when a non-call aggregate assigns into a field of an indexed element (#11b)
The arr[i].f=src legacy assign block enumerated scalar field-type arms then fell to a 1-word scalar default, so a non-call aggregate source (ident/dot/index) cgexpr'd only its first word into AX and stored one eightbyte — silent on BOTH stages (byte-id blind). The non-indexed bases (local/deref/chained/global) reach the general assign resolver's canonical aggargsrcaddr+aggcopy; the indexed arm short-circuited before it. Route the indexed base through the block's own proven &arr[i] spine into the same aggargsrcaddr+aggcopy emitters (DRY — no third copy), dual-site symmetric. Unlike #11's in-cap arm, the source is a memory address so aggcopy is a pure memcpy: float bits and the sub-8 tail transport verbatim, no loud-stop needed. Did not fall through to the general resolver because its cgplaceaddr N_INDEX arm rejects a *[N]S (TY_PTR) base (latent resolver gap, filed separately).

Contained to the indexed base + non-call aggregate-field rhs; value-asserting pins redden under each stage's independent revert.
2026-06-27 19:40:04 +09:00

44 lines
1.9 KiB
Plaintext

// idx_dot_src_aggregate_runonly_test — #11b sub-8-tail anti-clobber through a
// LOCAL [N]<struct-with-sub-8-tail-field> array. The fix's MOVL (not MOVQ) tail
// is VALUE-verified here: the dropped tail would read the poison 9, and a tail
// WIDENED to MOVQ would write bytes 8..15 and smash the adjacent g at +12 — both
// caught by asserting f.c AND g. Exit-correct under BOTH stages.
//
// _runonly (excluded from the test-lang-byteid T2 gate) because a local
// [N]<sub-8-tail-struct> trips a SEPARATE PRE-EXISTING let-array slotsize cs!=ww
// FRAME divergence — ww sizes the array element by slotsize (s12 slot-padded),
// cstage by natural size — that PREDATES and is INDEPENDENT of #11b: empirically
// the #11b receive INSTRUCTIONS are byte-identical (both stages emit `MOVQ
// (SI),AX` then the `MOVL 8(SI),AX` tail); only the container's frame SIZE and
// the BP-relative offsets shift ($48 vs $64). The byte-id twin
// (idx_dot_src_aggregate_test) covers the same sub-8-tail shape via global-
// backed bases, where the slotsize bug does not bite (task #9).
package idx_dot_src_aggregate_runonly_test;
type t12 = struct { a: i32, b: i32, c: i32 };
type s12 = struct { f: t12, g: i32 };
fn one() i64 = { return 1i64; };
@test fn subtail_local_ident_const() void = {
let a: [2]s12 = [s12{f=t12{a=9i32,b=9i32,c=9i32},g=9i32}, s12{f=t12{a=9i32,b=9i32,c=9i32},g=9i32}];
let src: t12 = t12{a=10i32, b=20i32, c=30i32};
a[1].f = src;
assert(a[1].f.a == 10i32);
assert(a[1].f.b == 20i32);
assert(a[1].f.c == 30i32);
assert(a[1].g == 9i32);
assert(a[0].f.a == 9i32);
};
@test fn subtail_local_ident_runtime() void = {
let a: [2]s12 = [s12{f=t12{a=9i32,b=9i32,c=9i32},g=9i32}, s12{f=t12{a=9i32,b=9i32,c=9i32},g=9i32}];
let src: t12 = t12{a=10i32, b=20i32, c=30i32};
a[one()].f = src;
assert(a[1].f.a == 10i32);
assert(a[1].f.b == 20i32);
assert(a[1].f.c == 30i32);
assert(a[1].g == 9i32);
};