From 84206ff4ed6d44a851d209d70eeb0e6b63d3b98b Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Sat, 8 Aug 2026 19:08:19 +0900 Subject: [PATCH] w6c: resolve ptr-to-array and slice-element aggregate sources (task #6 residue) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- cmd/w6c/cgen.c | 20 ++++++-- selfhost/cmd/wcc/cgenexpr.ww | 28 ++++++++--- test/lang/idxdot_aggcopy_test.ww | 83 ++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 11 deletions(-) diff --git a/cmd/w6c/cgen.c b/cmd/w6c/cgen.c index be82abec..ec1637e8 100644 --- a/cmd/w6c/cgen.c +++ b/cmd/w6c/cgen.c @@ -2313,7 +2313,10 @@ aggarg_srcaddr(Cg *c, Node *src, int dst, Local *locals) ins2(c, A_MOVQ, areg(D_AX), areg(dst)); return 1; } - return 0; + /* 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. */ + return cgplaceaddr(c, src, dst, locals); } return 0; } @@ -2451,7 +2454,16 @@ cgplaceaddr(Cg *c, Node *n, int dst_reg, Local *locals) * dispatch first), so their asm is untouched. */ Type *bu = type_chase_named(base->type); if (bu == NULL) return 0; - if (bu->kind != TY_SLICE && bu->kind != 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. */ + int viaptr = 0; + if (bu->kind == TY_PTR) { + Type *p = type_chase_named(bu->sub); + if (p && p->kind == TY_ARRAY) + viaptr = 1; + } + if (!viaptr && bu->kind != TY_SLICE && bu->kind != TY_ARRAY) return 0; Type *et = type_chase_named(n->type); if (et == NULL) return 0; @@ -2465,8 +2477,8 @@ cgplaceaddr(Cg *c, Node *n, int dst_reg, Local *locals) if (!cgplaceaddr(c, base, dst_reg, locals)) return 0; /* 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 == TY_SLICE) + * element storage; a *[N]T place holds the array address. */ + if (bu->kind == TY_SLICE || viaptr) ins2(c, A_MOVQ, amem(dst_reg, 0), areg(dst_reg)); ins1(c, A_POPQ, areg(D_AX)); ins2(c, A_ADDQ, areg(D_AX), areg(dst_reg)); diff --git a/selfhost/cmd/wcc/cgenexpr.ww b/selfhost/cmd/wcc/cgenexpr.ww index eb76d430..2e26faec 100644 --- a/selfhost/cmd/wcc/cgenexpr.ww +++ b/selfhost/cmd/wcc/cgenexpr.ww @@ -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("), "); diff --git a/test/lang/idxdot_aggcopy_test.ww b/test/lang/idxdot_aggcopy_test.ww index 9111e7cb..037a005c 100644 --- a/test/lang/idxdot_aggcopy_test.ww +++ b/test/lang/idxdot_aggcopy_test.ww @@ -131,3 +131,86 @@ fn pickresult(hs: *[4]handle, k: i64) inner = { assert(hs[0].result.a == 40); assert(hs[0].result.b == 41); }; + +// task #6 siblings (the d3822d77 loud residue): a *[N]T indexed base +// auto-derefs through cgplaceaddr's pointer hop, and a bare slice +// element rides aggarg_srcaddr's place-resolver fallback. +@test fn ptrbaseletcopy() void = { + let hs: [4]handle = mk(); + let hp: *[4]handle = &hs; + let k: i64 = 2; + let r: inner = hp[k].result; + assert(r.a == 11); + assert(r.b == 31); +}; + +@test fn ptrbasecallarg() void = { + let hs: [4]handle = mk(); + let hp: *[4]handle = &hs; + let k: i64 = 2; + assert(use(hp[k].result) == 42); +}; + +fn pickptr(hp: *[4]handle, k: i64) inner = { + return hp[k].result; +}; + +@test fn ptrbasereturn() void = { + let hs: [4]handle = mk(); + let r: inner = pickptr(&hs, 2); + assert(r.a == 11); + assert(r.b == 31); +}; + +@test fn ptrbasefieldassign() void = { + let hs: [4]handle = mk(); + let hp: *[4]handle = &hs; + let k: i64 = 2; + let g: bag; + g.n = 0; + g.res = hp[k].result; + assert(g.res.a == 11); + assert(g.res.b == 31); +}; + +@test fn sliceelemletcopy() void = { + let hs: [4]handle = mk(); + let sl: []handle = hs[0:4]; + let k: i64 = 2; + let e: handle = sl[k]; + assert(e.id == 9); + assert(e.result.a == 11); +}; + +@test fn sliceelemcallarg() void = { + let hs: [4]handle = mk(); + let sl: []handle = hs[0:4]; + let k: i64 = 2; + assert(usehandle(sl[k]) == 51); +}; + +fn usehandle(h: handle) i64 = { + return h.id + h.result.a + h.result.b; +}; + +fn pickelem(sl: []handle, k: i64) handle = { + return sl[k]; +}; + +@test fn sliceelemreturn() void = { + let hs: [4]handle = mk(); + let sl: []handle = hs[0:4]; + let e: handle = pickelem(sl, 2); + assert(e.id == 9); + assert(e.result.b == 31); +}; + +@test fn sliceelemindexedstore() void = { + let hs: [4]handle = mk(); + let sl: []handle = hs[0:4]; + let out: [2]inner; + let j: i64 = 0; + out[j] = sl[2].result; + assert(out[0].a == 11); + assert(out[0].b == 31); +};