949_dotfield_compound + 949_idxfield_compound are one bug class (#133-lineage compound-assign load-op-store on field lvalues; #34/#33, #263 carve-out) sharing the combine + hard-error path, so the two C carriers fuse into one commit: 26 value rows -> test/lang @test row-tables (primitive-only asserts), 8 reject rows -> runww //ww:error dual-stage carriers. byteid floor 50->52.
124 lines
3.1 KiB
Plaintext
124 lines
3.1 KiB
Plaintext
// dotfield_compound_test — single-dot field compound-assign (#34, #263 both
|
|
// stages), migrated from test/wcc/949_dotfield_compound_run.c. A compound
|
|
// assign on a single-dot field lvalue (`s.f OP= v`, `p.f OP= v`, `g.f OP= v`,
|
|
// `sl.len OP= v`) must do a real LOAD-OP-STORE for ALL ten integer ops, not
|
|
// silently demote a non-+=/-= op to a plain `s.f = rhs` (the prior bug: *= /=
|
|
// %= &= |= ^= <<= >>= left old-in-BX/rhs-in-AX then stored AX, so `s.f *= 3`
|
|
// compiled to `s.f = 3`, gate-blind/byte-identical). Every row init-poisons
|
|
// the field with a value != the expected result, runs the op, reads the field
|
|
// back, and asserts the sibling field `g` (or the slice `.cap`) is UNTOUCHED to
|
|
// catch an over-wide / wrong-offset store. PRIMITIVE-only asserts (no
|
|
// fmt/strconv) so a co-miscompile in the assert path cannot mask the bug. The
|
|
// matrix covers + * / % << >> across int/i32/u32 fields, a via-ptr base, a
|
|
// global struct field, and the str/slice `.len` pseudo-field; the plain-assign
|
|
// control pins the ASSIGN path the fix must leave unchanged. T2 keeps cs==ww.
|
|
|
|
package dotfield_compound_test;
|
|
|
|
type Sii = struct { f: int, g: int };
|
|
type S32 = struct { f: i32, g: i32 };
|
|
type Su32 = struct { f: u32, g: u32 };
|
|
|
|
let gs: Sii = Sii{f=20, g=0};
|
|
|
|
fn bump(p: *Sii) void = { p.f *= 3; };
|
|
|
|
@test fn dotfld_pluseq_ctrl() void = {
|
|
let s: Sii = Sii{f=10, g=0};
|
|
s.f += 5;
|
|
assert(s.f == 15);
|
|
assert(s.g == 0);
|
|
};
|
|
|
|
@test fn dotfld_stareq() void = {
|
|
let s: Sii = Sii{f=5, g=0};
|
|
s.f *= 3;
|
|
assert(s.f == 15);
|
|
assert(s.g == 0);
|
|
};
|
|
|
|
@test fn dotfld_slasheq_signed() void = {
|
|
let s: Sii = Sii{f=100, g=0};
|
|
s.f /= 4;
|
|
assert(s.f == 25);
|
|
assert(s.g == 0);
|
|
};
|
|
|
|
@test fn dotfld_slasheq_neg() void = {
|
|
let s: S32 = S32{f=-17, g=0};
|
|
s.f /= 4;
|
|
assert(s.f == -4);
|
|
assert(s.g == 0);
|
|
};
|
|
|
|
@test fn dotfld_slasheq_unsigned() void = {
|
|
let s: Su32 = Su32{f=100u32, g=0u32};
|
|
s.f /= 4u32;
|
|
assert(s.f == 25u32);
|
|
assert(s.g == 0u32);
|
|
};
|
|
|
|
@test fn dotfld_percenteq() void = {
|
|
let s: Sii = Sii{f=17, g=0};
|
|
s.f %= 5;
|
|
assert(s.f == 2);
|
|
assert(s.g == 0);
|
|
};
|
|
|
|
@test fn dotfld_lshifteq() void = {
|
|
let s: Sii = Sii{f=3, g=0};
|
|
s.f <<= 4;
|
|
assert(s.f == 48);
|
|
assert(s.g == 0);
|
|
};
|
|
|
|
@test fn dotfld_rshifteq_neg() void = {
|
|
let s: S32 = S32{f=-16, g=0};
|
|
s.f >>= 2;
|
|
assert(s.f == -4);
|
|
assert(s.g == 0);
|
|
};
|
|
|
|
@test fn dotfld_rshifteq_unsigned() void = {
|
|
let s: Su32 = Su32{f=200u32, g=0u32};
|
|
s.f >>= 2u32;
|
|
assert(s.f == 50u32);
|
|
assert(s.g == 0u32);
|
|
};
|
|
|
|
@test fn dotfld_viaptr_stareq() void = {
|
|
let a: Sii = Sii{f=4, g=0};
|
|
bump(&a);
|
|
assert(a.f == 12);
|
|
assert(a.g == 0);
|
|
};
|
|
|
|
@test fn dotfld_global_slasheq() void = {
|
|
gs.f /= 4;
|
|
assert(gs.f == 5);
|
|
assert(gs.g == 0);
|
|
};
|
|
|
|
@test fn dotfld_pseudo_len_minuseq() void = {
|
|
let buf: [8]u8 = [1u8,2u8,3u8,4u8,5u8,6u8,7u8,8u8];
|
|
let sl: []u8 = buf[0:8];
|
|
sl.len -= 3;
|
|
assert(sl.len == 5);
|
|
assert(sl.cap == 8);
|
|
};
|
|
|
|
@test fn dotfld_pseudo_len_stareq() void = {
|
|
let buf: [8]u8 = [1u8,2u8,3u8,4u8,5u8,6u8,7u8,8u8];
|
|
let sl: []u8 = buf[0:4];
|
|
sl.len *= 3;
|
|
assert(sl.len == 12);
|
|
assert(sl.cap == 8);
|
|
};
|
|
|
|
@test fn dotfld_plain_assign_ctrl() void = {
|
|
let s: Sii = Sii{f=1, g=0};
|
|
s.f = 99;
|
|
assert(s.f == 99);
|
|
assert(s.g == 0);
|
|
};
|