cgen: route aggregate field-to-field assignment through the aggregate copier

The direct-field assignment arms enumerate CALL, STRUCTLIT, and local
IDENT producers; an addressable N_DOT/N_INDEX/deref rhs fell through to
the scalar tail, so a 16-byte struct field copied only its first word.
Resolve both places through the existing address funnels and use the
tail-aware aggregate copier. Both stages.
This commit is contained in:
2026-08-07 22:59:52 +09:00
parent c654e97db1
commit 078708770b
3 changed files with 148 additions and 0 deletions

View File

@@ -8719,6 +8719,48 @@ fn cgassign(c: *cgen, n: *syntax.node) void = {
};
};
};
// A plain aggregate field-to-field assignment is a memory copy, not
// a scalar expression/store. The direct-field arms below enumerate
// CALL, STRUCTLIT and local IDENT producers; an addressable DOT/INDEX/
// deref rhs fell through, so a 16-byte time.instant copied one word.
// Resolve both places through the existing address funnels and use the
// canonical tail-aware aggregate copier. Calls/literals/unwraps and
// tagged/str/slice fields remain on their specialized ABI paths.
if (lhs != nil && n.rhs != nil && n.op == syntax.tkind.TK_ASSIGN) {
if (lhs.kind == syntax.nkind.N_DOT && lhs.lhs != nil
&& (lhs.lhs.kind == syntax.nkind.N_IDENT
|| lhs.lhs.kind == syntax.nkind.N_DOT
|| (lhs.lhs.kind == syntax.nkind.N_UN
&& lhs.lhs.op == syntax.tkind.TK_STAR))) {
let au: *syntax.tinfo = lhs.type_: *syntax.tinfo;
au = tichase(au);
let memrhs: bool = n.rhs.kind == syntax.nkind.N_IDENT
|| n.rhs.kind == syntax.nkind.N_DOT
|| n.rhs.kind == syntax.nkind.N_INDEX
|| (n.rhs.kind == syntax.nkind.N_UN
&& n.rhs.op == syntax.tkind.TK_STAR);
if (au != nil && memrhs) {
if (au.kind == syntax.tykind.TY_STRUCT
|| au.kind == syntax.tykind.TY_ARRAY
|| au.kind == syntax.tykind.TY_TUPLE) {
if (!cgplaceaddr(c, lhs, "BX")) {
let md: str = "aggregate field destination unresolved\n";
os.write(2, md.ptr, md.len: u64);
os.exit(1);
};
emitline("\tPUSHQ\tBX\n");
if (!aggargsrcaddr(c, n.rhs, "SI")) {
let ms: str = "aggregate field source unresolved\n";
os.write(2, ms.ptr, ms.len: u64);
os.exit(1);
};
emitline("\tPOPQ\tBX\n");
aggcopy(c, au.size: i32);
return;
};
};
};
};
// #21: a COMPOUND op on a whole tagged-union IDENT (`g OP= v`
// with g:(int|bool)) is nonsense — the ident load-combine-store
// tail below reads and writes one word of the {payload,tag} box,