w6c+cgen: len() over indexed str/slice element extracts .len (fix #19)

`len(xs[i])` over a [N]str/[]str (and []T slice) element returned the
element's .ptr, not its length, on BOTH stages (shared gap, not rule-10):
the len() builtin had no N_INDEX arm, so it fell to the bare-cgexpr
fallback, where the N_INDEX str/slice load (cgslicehdr) leaves AX=.ptr,
BX=.len, CX=.cap — and len() returned AX (the ptr) as the length.

Add an N_INDEX arm gated on a (TY_SLICE||TY_STR) element in both stages:
cgexpr the element, then MOVQ BX,AX to shuffle the len word into the
result reg — the same shape as the #14 .len pseudo-field fix. Byte-id
neutral (no bootstrap source uses len(indexed-element)); regenerated
w6c + wwdump combined.ww. New 802_lenidx_run pins runtime + cs==ww.
This commit is contained in:
2026-06-03 11:56:16 +09:00
parent 58c8f4be02
commit 6b67655eca
6 changed files with 280 additions and 0 deletions

View File

@@ -5998,6 +5998,18 @@ cgexpr(Cg *c, Node *n, Local *locals)
} else {
cgexpr(c, a, locals);
}
} else if (u && (u->kind == TY_SLICE || u->kind == TY_STR)
&& a->kind == N_INDEX) {
/* #19: len() of an INDEXED str/slice element
* (`len(xs[i])`). The N_INDEX str/slice load leaves
* AX=.ptr, BX=.len, CX=.cap (cgslicehdr) — the bare
* cgexpr fallback below then returned AX (the ptr) AS
* the length. Shuffle BX (the len word) into AX, the
* same MOVQ BX,AX shape as the #14 .len pseudo-field
* fix. Same family as #18 (shared cstage==wwstage gap,
* not a rule-10 divergence). */
cgexpr(c, a, locals);
ins2(c, A_MOVQ, areg(D_BX), areg(D_AX));
} else if (u && u->kind == TY_ARRAY) {
ins2(c, A_MOVQ, aimm((long long)u->alen), areg(D_AX));
} else {