Files
ww/test/lang/idxdot_aggcopy_test.ww
Hojun-Cho d3822d7730 w6c: fall to the place resolver for indexed dot-chain aggregate sources
The dot-chain SOURCE-address walkers (cg_dotchain_addr /
dotchainaddr) deliberately have no index hop — their AX-clean spill
contract serves the chained dotbase arm — so an aggregate copy from
elem[k].field (N_DOT over N_INDEX) fell out of the walk. Every
aggarg_srcaddr consumer loud-stopped (arg push #271, aggregate
return #272, field assign, structlit fill, ident reassign) EXCEPT
the indexed element-store arm, which left the walker's result
UNCHECKED and copied through a stale SI — a silent both-stage
miscompile (out[j] = hs[k].result read garbage). Task #6.

Both stages, two twin edits each: the aggarg_srcaddr N_DOT arm falls
back to cgplaceaddr (the C4/#40 place resolver, emission-free-on-
failure so the fallback starts clean), and the indexed store site
checks the walker before the same fallback.

Still loud (filed follow-ups): auto-deref pointer-to-array indexed
bases (hs: *[4]T; hs[k].field — cgplaceaddr's index arm takes no
deref hop; the explicit (*hs)[k].field form works), and the bare
N_INDEX slice-element aggregate source.
2026-08-08 17:12:18 +09:00

134 lines
2.8 KiB
Plaintext

// idxdot_aggcopy_test — aggregate copies whose SOURCE is an
// indexed-then-dotted chain (elem[k].field). The dot-chain address
// walker has no index hop (AX-clean spill contract), so these shapes
// ride the generic place-resolver fallback (task #6); the indexed
// element-store row pins the once-SILENT unchecked-walker site that
// copied through a stale SI in both stages.
package idxdot_aggcopy_test;
type inner = struct {
a: i64,
b: i64,
};
type handle = struct {
id: i64,
result: inner,
};
type mid = struct {
pad: i64,
in_: inner,
};
type outer = struct {
tag: i64,
m: mid,
};
fn mk() [4]handle = {
let hs: [4]handle;
hs[0] = handle{id = 1, result = inner{a = 1, b = 2}};
hs[1] = handle{id = 2, result = inner{a = 3, b = 4}};
hs[2] = handle{id = 9, result = inner{a = 11, b = 31}};
hs[3] = handle{id = 4, result = inner{a = 5, b = 6}};
return hs;
};
fn use(v: inner) i64 = {
return v.a + v.b;
};
@test fn letcopy() void = {
let hs: [4]handle = mk();
let k: i64 = 2;
let r: inner = hs[k].result;
assert(r.a == 11);
assert(r.b == 31);
};
@test fn callarg() void = {
let hs: [4]handle = mk();
let k: i64 = 2;
assert(use(hs[k].result) == 42);
};
type bag = struct {
n: i64,
res: inner,
};
@test fn fieldassign() void = {
let hs: [4]handle = mk();
let k: i64 = 2;
let g: bag;
g.n = 0;
g.res = hs[k].result;
assert(g.res.a == 11);
assert(g.res.b == 31);
};
fn pickresult(hs: *[4]handle, k: i64) inner = {
return (*hs)[k].result;
};
@test fn aggreturn() void = {
let hs: [4]handle = mk();
let r: inner = pickresult(&hs, 2);
assert(r.a == 11);
assert(r.b == 31);
};
@test fn slicebase() void = {
let hs: [4]handle = mk();
let sl: []handle = hs[0:4];
let k: i64 = 2;
assert(use(sl[k].result) == 42);
};
@test fn nesteddot() void = {
let os_: [3]outer;
os_[1] = outer{tag = 7, m = mid{pad = 0, in_ = inner{a = 20, b = 22}}};
let k: i64 = 1;
let l: inner = os_[k].m.in_;
assert(l.a == 20);
assert(l.b == 22);
};
// the once-silent shape: indexed element STORE from an indexed-dotted
// source (unchecked walker left SI stale; both stages copied garbage).
@test fn indexedstore() void = {
let hs: [4]handle = mk();
let out: [3]inner;
out[0] = inner{a = 1, b = 1};
out[1] = inner{a = 2, b = 2};
out[2] = inner{a = 3, b = 3};
let k: i64 = 2;
let j: i64 = 1;
out[j] = hs[k].result;
assert(out[1].a == 11);
assert(out[1].b == 31);
assert(out[0].a == 1);
assert(out[2].a == 3);
};
@test fn identreassign() void = {
let hs: [4]handle = mk();
let k: i64 = 2;
let r: inner = inner{a = 0, b = 0};
r = hs[k].result;
assert(r.a == 11);
assert(r.b == 31);
};
// control: the LHS twin (store INTO elem[k].field) was already wired.
@test fn lhsstore() void = {
let hs: [4]handle = mk();
let k: i64 = 0;
let r: inner = inner{a = 40, b = 41};
hs[k].result = r;
assert(hs[0].result.a == 40);
assert(hs[0].result.b == 41);
};