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

@@ -23887,6 +23887,21 @@ fn cgcall(c: *cgen, n: *node) void = {
};
};
};
// #19: len() of an INDEXED str/slice element
// (`len(xs[i])`). The N_INDEX str/slice load
// leaves AX=.ptr, BX=.len, CX=.cap — the bare
// cgexpr fallback below 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 rule-10).
if ((u.kind == tykind.TY_SLICE
|| u.kind == tykind.TY_STR)
&& a.kind == nkind.N_INDEX) {
cgexpr(c, a);
emitline("\tMOVQ\tBX, AX\n");
return;
};
if (u.kind == tykind.TY_ARRAY) {
emitline("\tMOVQ\t$");
emitint(u.alen: i64);

View File

@@ -4180,6 +4180,21 @@ fn cgcall(c: *cgen, n: *node) void = {
};
};
};
// #19: len() of an INDEXED str/slice element
// (`len(xs[i])`). The N_INDEX str/slice load
// leaves AX=.ptr, BX=.len, CX=.cap — the bare
// cgexpr fallback below 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 rule-10).
if ((u.kind == tykind.TY_SLICE
|| u.kind == tykind.TY_STR)
&& a.kind == nkind.N_INDEX) {
cgexpr(c, a);
emitline("\tMOVQ\tBX, AX\n");
return;
};
if (u.kind == tykind.TY_ARRAY) {
emitline("\tMOVQ\t$");
emitint(u.alen: i64);

View File

@@ -23887,6 +23887,21 @@ fn cgcall(c: *cgen, n: *node) void = {
};
};
};
// #19: len() of an INDEXED str/slice element
// (`len(xs[i])`). The N_INDEX str/slice load
// leaves AX=.ptr, BX=.len, CX=.cap — the bare
// cgexpr fallback below 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 rule-10).
if ((u.kind == tykind.TY_SLICE
|| u.kind == tykind.TY_STR)
&& a.kind == nkind.N_INDEX) {
cgexpr(c, a);
emitline("\tMOVQ\tBX, AX\n");
return;
};
if (u.kind == tykind.TY_ARRAY) {
emitline("\tMOVQ\t$");
emitint(u.alen: i64);