Files
ww/test/lang/widen_pad_test.ww
Hojun-Cho 83f5956df2 test: banner purge + WHY-only comment sweep (rule 8)
Every section banner dies (103 -> 0) across test/lang, the observer
suites, the C carriers, and the five comment-heavy corpus fixtures;
banner provenance (#N cites, carrier numbers, repair-cluster labels)
folded into headers or adjacent WHY comments. Narration deleted; row
provenance, ref cites, divergence pins, and layout contracts kept
(fwd-ref decl-order guards and bootstrap-gate corpus rationale
restored where the sweep over-cut). Comment-only proven: all 3742
wwbuild workdir .s byte-identical before/after; test-commit and
test-byteid (161 lang + 1399 data, 0 pinned-divergent) green.
2026-08-08 21:40:23 +09:00

73 lines
2.9 KiB
Plaintext

// 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);
};