wcc: for-range destructure copies the full str/slice binding, both stages

The per-binding copy loop moved ONE word of a 24B str/slice binding —
.len and .cap read zero/garbage in BOTH stages (byte-identical, the
deepest both-wrong-identical of the drain: the F7-era stride fix
asserted convergence without re-measuring the absolute). Copy the full
extent for an sz>8 str/slice binding; the rewritten 989_tupfieldsize
pins all three header words with sliced caps so cap!=len has teeth.
The tagged-binding arm remains open as task #53 (wwstage
paramfieldsize). Review-era task #40, recategorized #263 fused.

Both stages move in one commit: one emission contract; splitting
would leave the byte-id gates red between the halves.
This commit is contained in:
2026-06-12 22:52:20 +09:00
parent 77747061c6
commit 2a2ac49c64
5 changed files with 252 additions and 25 deletions

View File

@@ -39696,6 +39696,54 @@ fn cgforrange(c: *cgen, n: *node) void = {
} else {
let b: i32 = 0;
for (b < nbinds) {
// #40 (#263): a str/slice/struct destructure binding
// (24B header / aggregate, sz>8) copies its FULL extent
// — the single load word truncated a slice binding to
// its .ptr, dropping .len/.cap (both stages identically,
// byte-id-WRONG; F7-c4 fixed only the STRIDE). Same
// word-run + sized-tail idiom as the non-destructure
// aggregate copy above.
if (bind_sz[b] > 8) {
let k: i32 = 0;
for (k + 8 <= bind_sz[b]) {
emitline("\tMOVQ\t");
emitoff((bind_foff[b] + k): i64);
emitline("(BX), AX\n");
emitline("\tMOVQ\tAX, ");
emitoff((bind_off[b] + k): i64);
emitline("(BP)\n");
k += 8;
};
if (k + 4 <= bind_sz[b]) {
emitline("\tMOVL\t");
emitoff((bind_foff[b] + k): i64);
emitline("(BX), AX\n");
emitline("\tMOVL\tAX, ");
emitoff((bind_off[b] + k): i64);
emitline("(BP)\n");
k += 4;
};
if (k + 2 <= bind_sz[b]) {
emitline("\tMOVW\t");
emitoff((bind_foff[b] + k): i64);
emitline("(BX), AX\n");
emitline("\tMOVW\tAX, ");
emitoff((bind_off[b] + k): i64);
emitline("(BP)\n");
k += 2;
};
if (k + 1 <= bind_sz[b]) {
emitline("\tMOVB\t");
emitoff((bind_foff[b] + k): i64);
emitline("(BX), AX\n");
emitline("\tMOVB\tAX, ");
emitoff((bind_off[b] + k): i64);
emitline("(BP)\n");
k += 1;
};
b += 1;
continue;
};
let op: str = loadopsz(bind_signed[b], bind_sz[b]);
emitline("\t");
emitline(op);