Files
ww/test/lang/widen_pad_test.ww
Hojun-Cho 7db30bf609 test: migrate callret-unsigned/sar-shr/widen-pad behavior to test/lang @test
Three more value-observable behavior families ported from the C corpus
to in-language @test, routed by the ratified observability rule (value
-> test/lang @test; process-outcome stays in runww). Additive: the .c
sources keep running in $(TESTS), so no byte-id coverage is removed --
de-dup is deferred to fold 6 (task #12).

  906_callret_unsigned_arith -> callret_unsigned_test: call-result
    unsigned opcode select keyed by callee return type (#168, the N_CALL
    twin of gunsigned's module-global #134); operands flow through real
    calls so the return-type arm is exercised, not N_IDENT.
  912_sar_shr -> sar_shr_test: signed >> / >>= must emit SAR not SHR
    (#136); asserts the i64/i32 value directly, dropping the C 8-bit
    exit-code encoding.
  793_widen_pad_zero -> widen_pad_test: widening a narrow value into a
    wider tagged slot zeroes the high pad words (#227).
2026-06-22 02:53:16 +09:00

73 lines
2.9 KiB
Plaintext

// widen_pad_test — widening a NARROW scalar/float value into a tagged-union
// slot whose payload is WIDER than one word must ZERO the high pad words
// (slot+16, slot+24), not leave them at whatever the frame slot last held,
// migrated from test/wcc/793_widen_pad_zero_run.c (#227). cg_widen_tagged_store
// (cstage) and cgwidentaggedstorebp (wwstage) wrote only the tag (slot+0) and
// value (slot+8) in their scalar/float arms, leaving the rest uninitialised.
// For a >16B union (e.g. `(i64 | str)`, whose str variant makes the slot 32B =
// 4 words) a later `*u8` reinterpret then read stack garbage at slot+16/+24.
//
// Both stages were wrong the SAME way, so the byte-id gates were GREEN while
// the runtime was wrong — dead in the bootstrap corpus. Each shape first
// widens a STR into the slot (which fills slot+16/+24 with the str's len/cap),
// then reassigns a SCALAR / FLOAT into the SAME slot, then reads slot+16/+24
// back through a `*u8` reinterpret (the bit-pinning idiom from
// 715_tagged_widen_f64). Pre-fix the reassign left the str's stale len/cap in
// the pad; post-fix the pad reads back 0. These are distinct widen SHAPES
// (scalar arm, float arm, repeated-reassign), not data, so per-shape asserts.
package widen_pad_test;
@test fn scalar_i64_after_str() void = {
// Reassign a scalar i64 over a str-occupied (i64|str) slot: the str
// write fills slot+16 (len) and slot+24 (cap); the scalar reassign must
// zero them. payload == 123, tag == 0 (i64 is variant 0).
let a: (i64 | str) = "abcdefgh";
a = 123i64;
let pp: *(i64 | str) = &a;
let pu: *u8 = pp: *u8;
let tagp: *i64 = pu: *i64;
let valp: *i64 = (pu + 8u64): *i64;
let pad1: *i64 = (pu + 16u64): *i64;
let pad2: *i64 = (pu + 24u64): *i64;
assert(*valp == 123i64);
assert(*pad1 == 0i64);
assert(*pad2 == 0i64);
assert(*tagp == 0i64);
};
@test fn float_f64_after_str() void = {
// The float arm: reassign an f64 over a str-occupied (f64|str) slot.
// f64 1.0 == 0x3FF0000000000000 == 4607182418800017408. tag == 0.
let a: (f64 | str) = "abcdefgh";
a = 1.0;
let pp: *(f64 | str) = &a;
let pu: *u8 = pp: *u8;
let tagp: *i64 = pu: *i64;
let valp: *u64 = (pu + 8u64): *u64;
let pad1: *i64 = (pu + 16u64): *i64;
let pad2: *i64 = (pu + 24u64): *i64;
assert(*valp == 4607182418800017408u64);
assert(*pad1 == 0i64);
assert(*pad2 == 0i64);
assert(*tagp == 0i64);
};
@test fn scalar_after_str_twice() void = {
// Reassign a scalar over a str TWICE — confirms the tail-zero is emitted
// on EVERY scalar widen (not just a first write), with a str's len/cap
// dirtying the pad in between. Final payload == 222.
let a: (i64 | str) = "firstone";
a = 11i64;
a = "secondxx";
a = 222i64;
let pp: *(i64 | str) = &a;
let pu: *u8 = pp: *u8;
let valp: *i64 = (pu + 8u64): *i64;
let pad1: *i64 = (pu + 16u64): *i64;
let pad2: *i64 = (pu + 24u64): *i64;
assert(*valp == 222i64);
assert(*pad1 == 0i64);
assert(*pad2 == 0i64);
};