w6c: resolve ptr-to-array and slice-element aggregate sources (task #6 residue)

The two loud siblings filed in d3822d77: (a) an auto-deref *[N]T
indexed base (hs[k].result) failed cgplaceaddr's N_INDEX arm, which
knew only TY_SLICE/TY_ARRAY bases — add the one-pointer hop (the
(*hs)[k] form made implicit; load shape is the slice .ptr word's);
(b) a bare slice-element aggregate source (use(sl[k])) fell off
aggarg_srcaddr's ident-array-only N_INDEX arm into the #271 loud
stop — route the miss through the same cgplaceaddr fallback the
N_DOT arm uses. Both stages, byte-identical (probe matrix: let-copy,
field-assign, call-arg, return, indexed-store all IDENT).

After this, every ADDRESSABLE aggregate source resolves: ident
(local/module-let), deref, dot-chain, and index over array/slice/
ptr-array spines recurse through cgplaceaddr; the remaining loud
paths are rvalue sources (#40 family) and the #274 const-def arg,
both documented divergences. 8 rows added to idxdot_aggcopy_test.
This commit is contained in:
2026-08-08 19:08:19 +09:00
parent cf69cf2f06
commit 84206ff4ed
3 changed files with 120 additions and 11 deletions

View File

@@ -1704,14 +1704,17 @@ fn aggargsrcaddr(c: *cgen, src: *syntax.node, dst: str) bool = {
return cgplaceaddr(c, src, dst);
};
if (src.kind == syntax.nkind.N_INDEX) {
// task #6 sibling: slice-element / non-ident-array base
// (use(sl[k])) — the same C4/#40 place-resolver fallback
// as the N_DOT arm; the missed walk is emission-free.
let base: *syntax.node = src.lhs;
let idx: *syntax.node = src.rhs;
if (base == nil) { return false; };
if (base.kind != syntax.nkind.N_IDENT) { return false; };
if (base == nil) { return cgplaceaddr(c, src, dst); };
if (base.kind != syntax.nkind.N_IDENT) { return cgplaceaddr(c, src, dst); };
let bu: *syntax.tinfo = base.type_: *syntax.tinfo;
bu = tichase(bu);
if (bu == nil) { return false; };
if (bu.kind != syntax.tykind.TY_ARRAY) { return false; };
if (bu == nil) { return cgplaceaddr(c, src, dst); };
if (bu.kind != syntax.tykind.TY_ARRAY) { return cgplaceaddr(c, src, dst); };
let esz: i32 = 1;
if (bu.sub != nil) { esz = bu.sub.size: i32; };
cgexpr(c, idx);
@@ -1966,7 +1969,18 @@ fn cgplaceaddr(c: *cgen, n: *syntax.node, dstreg: str) bool = {
let bu: *syntax.tinfo = base.type_: *syntax.tinfo;
bu = tichase(bu);
if (bu == nil) { return false; };
if (bu.kind != syntax.tykind.TY_SLICE && bu.kind != syntax.tykind.TY_ARRAY) {
// Auto-deref: a *[N]T indexed base takes one pointer hop —
// the (*hs)[k] form made implicit (checker already stamps
// the element type). Load shape is the slice .ptr word's.
let viaptr: bool = false;
if (bu.kind == syntax.tykind.TY_PTR) {
let p: *syntax.tinfo = bu.sub;
p = tichase(p);
if (p != nil) { if (p.kind == syntax.tykind.TY_ARRAY) {
viaptr = true;
}; };
};
if (!viaptr && bu.kind != syntax.tykind.TY_SLICE && bu.kind != syntax.tykind.TY_ARRAY) {
return false;
};
let et: *syntax.tinfo = n.type_: *syntax.tinfo;
@@ -1984,8 +1998,8 @@ fn cgplaceaddr(c: *cgen, n: *syntax.node, dstreg: str) bool = {
if (!cgplaceaddr(c, base, dstreg)) { return false; };
// A slice place holds the {ptr,len,cap} header — the
// element base is its .ptr word; an array place IS the
// element storage.
if (bu.kind == syntax.tykind.TY_SLICE) {
// element storage; a *[N]T place holds the array address.
if (bu.kind == syntax.tykind.TY_SLICE || viaptr) {
emitline("\tMOVQ\t(");
emitline(dstreg);
emitline("), ");