w6c+wwstage: len() of tuple-element slice reads .len not .ptr (#235)

len() special-cased only a plain N_IDENT slice operand (load .len at
BP+off+8) and an array operand (fold $alen); every other shape fell back
to a bare cgexpr(operand), which for a slice leaves AX=.ptr. A tuple-
element read (t.N) loads only AX=.ptr, so len(t.N) on a slice/str tuple
element returned the slice's .ptr word AS its length — a silent
miscompile, gate-blind because the bootstrap never does len() on a
slice-typed tuple element (sibling of the #234/#237 tuple-sret cluster).

Both stages: detect a slice/str tuple-element len() operand and load the
element's .len word directly at BP + element_off + 8, mirroring the
N_IDENT slice arm and the tuple-field-offset walk (element_off sums
preceding element sizes through the type table). Byte-identical asm
(rule 10). The separate tuple-element-read full-header gap is #238; a
leading-scalar mixed-tuple has its own pre-existing sret-layout cs/ww
divergence, filed apart from #235.

Test 903_tuple_elem_slice_len_run: 4 slice/str-only tuple rows (two/
three slices, str+slice, slice+str; distinct lengths), build+run both
drivers + cs==ww byte-id. 12/12 ok.
This commit is contained in:
2026-06-01 18:17:07 +09:00
parent 20fe5419d2
commit 6acddc3a82
6 changed files with 428 additions and 0 deletions

View File

@@ -5328,6 +5328,39 @@ cgexpr(Cg *c, Node *n, Local *locals)
ins2(c, A_MOVQ, amem(D_BP, off + 8),
areg(D_AX));
}
} else if (u && (u->kind == TY_SLICE || u->kind == TY_STR)
&& a->kind == N_DOT && a->lhs
&& a->lhs->kind == N_IDENT && a->str) {
/* #235: len() of a tuple-element slice/str
* (`len(t.N)`). The tuple-element read leaves only
* AX=.ptr — it has no slice-header sibling (that
* gap is #238) — so the bare cgexpr fallback below
* returned .ptr AS the length. Load the element's
* .len word directly at BP + element_off + 8,
* mirroring the N_IDENT slice arm above and the
* tuple-field-offset walk (cgen.c N_DOT TY_TUPLE). */
Type *bt = a->lhs->type;
Type *bu = (bt && bt->kind == TY_NAMED)
? bt->under : bt;
if (bu && bu->kind == TY_TUPLE) {
int idx = 0;
for (const char *q = a->str; *q; q++)
idx = idx * 10 + (*q - '0');
Tparam *tp = bu->params;
int foff = 0;
while (idx > 0 && tp) {
if (tp->type)
foff += (int)tp->type->size;
tp = tp->next;
idx--;
}
int off = localfind(locals, a->lhs->str);
ins2(c, A_MOVQ,
amem(D_BP, off + foff + 8),
areg(D_AX));
} else {
cgexpr(c, a, locals);
}
} else if (u && u->kind == TY_ARRAY) {
ins2(c, A_MOVQ, aimm((long long)u->alen), areg(D_AX));
} else {