w6c+selfhost+lib: zero-init multi-word no-rhs lets

`let x: T;` for str/slice/tuple/struct/tagged previously left the slot
holding stack garbage — only 8B-primitive slots were zeroed. This bit
`expectbindname` in lib/ww/parse: `let empty: str; *into = empty;` was
copying stack bytes (often a recently-vacated str descriptor) into the
caller's `id`, so wwstage emitted `_` discard nodes carrying random
text instead of "". Both stages now zero the full slot on no-rhs lets;
`[N]T` arrays keep the per-index-write contract.

Also tightens the two known buggy sites: parse.ww `expectbindname`
writes `*into = ""` directly, expr.ww `_` primary returns the bare
newnode (amalloc already zeroes).
This commit is contained in:
2026-05-13 12:46:02 +09:00
parent b6cf68f2b8
commit 956a20701b
7 changed files with 155 additions and 43 deletions

View File

@@ -544,16 +544,46 @@ fn cglet(c: *cgen, n: *node) void = {
};
} else {
// Bare `let x: T;` with no initializer. C cgen
// (cmd/w6c/cgen.c:3317) zero-inits whenever the raw type size
// is 8: scalar primitives, pointers, fn/chan handles, plus 8B
// composites like `[8]bool`, `[2]i32`, `[4]i16`, `[1]i64`.
// Larger composites and `[N]T` with size != 8 are left for
// per-field writes.
// (cmd/w6c/cgen.c N_LET no-rhs branch) zero-inits in two
// shapes:
// - 8B primitives (scalar/ptr/fn/chan/`[8]bool` etc.):
// single `MOVQ $0, off(BP)`.
// - multi-word composites (str/slice/tuple/struct/tagged):
// `XORQ AX,AX` + a run of `MOVQ AX, ...` over the slot
// so reads after the bare let see {0...} rather than
// stack garbage.
// `[N]T` arrays of size != 8 keep the per-index-write
// contract — they're left uninit.
let isarr: bool = false;
if (n.lhs != nil) {
if (n.lhs.kind == nkind.N_TARRAY) { isarr = true; };
};
if (typeis8byteprimitive(c, n.lhs)) {
emitline("\tMOVQ\t$0, ");
emitoff(off: i64);
emitline("(BP)\n");
};
} else { if (!isarr) { if (sz > 8) {
emitline("\tXORQ\tAX, AX\n");
let zi: i32 = 0;
for (zi + 8 <= sz) {
emitline("\tMOVQ\tAX, ");
emitoff((off + zi): i64);
emitline("(BP)\n");
zi += 8;
};
for (zi + 4 <= sz) {
emitline("\tMOVL\tAX, ");
emitoff((off + zi): i64);
emitline("(BP)\n");
zi += 4;
};
for (zi < sz) {
emitline("\tMOVB\tAX, ");
emitoff((off + zi): i64);
emitline("(BP)\n");
zi += 1;
};
}; }; };
};
c.lastwasreturn = 0;
return;