// A nested aggregate field VALUE inside an // alloc(struct-literal) heap init must survive (C7c, task #6). The // alloc(value) field-fill loop had only float/str/scalar arms; a field // whose value is itself an N_STRUCTLIT / N_ARRLIT fell to the scalar // tail, so cgexpr(inner-literal) left AX=0 and the loop stored $0 over // the whole inner slot — silently dropping every inner leaf (byte-id- // gate-blind: both stages emitted the same wrong fill). The fix routes // the heap fill through the shared cg_structlit_fill / cgstructlitfill // helper in DST_PTR_SP mode (base reloaded from the pushed heap ptr at // (SP)), which recurses to arbitrary depth. // // The nested-field rows read the heap struct back via `let o: T = *p` so // they isolate heap filling from chained pointer traversal. The alias-head and // exact direct control both read p.x/p.y: together they preserve the old #26 // wrapper's alias-vs-non-alias comparison. Every row asserts the affected // values independently. package alloc_nested_field_test; type Inner = struct { q: i64 }; type Outer = struct { x: Inner, y: i64 }; type Mid = struct { lo: i64, inner: Inner, hi: i64 }; type Nest = struct { a: i64, mid: Mid, b: i64 }; type WithArr = struct { lead: i64, arr: [3]i64, trail: i64 }; type TwoNest = struct { head: i64, s: Inner, arr: [2]i64, tail: i64 }; type AliasPoint = struct { x: i64, y: i64 }; type AliasPt = AliasPoint; @test fn alias_named_struct() void = { let p: *AliasPt = alloc(AliasPt{ x = 1, y = 2 })!; assert(p.x == 1); assert(p.y == 2); }; @test fn direct_named_struct_control() void = { let p: *AliasPoint = alloc(AliasPoint{ x = 1, y = 2 })!; assert(p.x == 1); assert(p.y == 2); }; @test fn depth1_nested_struct() void = { // Inner{q=10} is the field VALUE of x; pre-fix x.q dropped to 0. let p = alloc(Outer{ x = Inner{ q = 10 }, y = 5 })!; let o: Outer = *p; assert(o.x.q == 10); // the dropped leaf assert(o.y == 5); // trailing sibling, must not clobber }; @test fn depth2_nested_struct() void = { // Outer{ mid = Mid{ inner = Inner{q} } } — two levels of nesting. // Every inner leaf lands at base + accumulated offset. let p = alloc(Nest{ a = 1, mid = Mid{ lo = 2, inner = Inner{ q = 3 }, hi = 4 }, b = 5 })!; let o: Nest = *p; assert(o.a == 1); // lead sibling assert(o.mid.lo == 2); // inner lead sibling assert(o.mid.inner.q == 3); // depth-2 dropped leaf assert(o.mid.hi == 4); // inner trail sibling assert(o.b == 5); // trail sibling }; @test fn nested_array_field() void = { // arr = [10,20,30] is an N_ARRLIT field VALUE; pre-fix every // element dropped (scalar tail stored word0 only). lead/trail // bracket the array to prove the element stores stay in bounds. let p = alloc(WithArr{ lead = 7, arr = [10, 20, 30], trail = 9 })!; let o: WithArr = *p; assert(o.lead == 7); // lead sibling assert(o.arr[0] == 10); // dropped elements assert(o.arr[1] == 20); assert(o.arr[2] == 30); assert(o.trail == 9); // trail sibling }; @test fn multi_nested_fields() void = { // Two ADJACENT nested-aggregate fields (nested STRUCT s, then nested // ARRAY arr) bracketed by scalar siblings — proves the heap-base // reload after a full nested recursion serves the NEXT nested field's // recursion, not only a trailing scalar (every other row follows a // nested field with a scalar). pre-fix both s.q and the arr elems drop. let p = alloc(TwoNest{ head = 1, s = Inner{ q = 2 }, arr = [3, 4], tail = 5 })!; let o: TwoNest = *p; assert(o.head == 1); // lead sibling assert(o.s.q == 2); // nested-struct leaf assert(o.arr[0] == 3); // nested-array elems, after a nested struct assert(o.arr[1] == 4); assert(o.tail == 5); // trail sibling };