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.
73 lines
2.9 KiB
Plaintext
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);
|
|
};
|