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:
@@ -68,14 +68,12 @@ fn parseprimary(p: *parser) *node = {
|
||||
return newnode(p.a, nkind.N_VOIDLIT, pf, pl, pc);
|
||||
};
|
||||
if (p.curkind == tkind.TK_UNDER) {
|
||||
// Bare `_` — valid only as a discard lvalue. Emit an nkind.N_IDENT
|
||||
// with empty str; the checker rejects it outside lvalue
|
||||
// Bare `_` — valid only as a discard lvalue. Emit an N_IDENT
|
||||
// with empty str (newnode zeroes the node, so str.len is
|
||||
// already 0); the checker rejects it outside lvalue
|
||||
// positions.
|
||||
advance(p);
|
||||
let n: *node = newnode(p.a, nkind.N_IDENT, pf, pl, pc);
|
||||
let empty: str;
|
||||
n.str = empty;
|
||||
return n;
|
||||
return newnode(p.a, nkind.N_IDENT, pf, pl, pc);
|
||||
};
|
||||
if (p.curkind == tkind.TK_LBRACK) {
|
||||
// Array literal `[a, b, c]` or `[v, w...]` (repeat suffix).
|
||||
|
||||
@@ -92,8 +92,7 @@ fn expectident(p: *parser, into: *str) bool = {
|
||||
// scope_define for the binding.
|
||||
fn expectbindname(p: *parser, into: *str) bool = {
|
||||
if (p.curkind == tkind.TK_UNDER) {
|
||||
let empty: str;
|
||||
*into = empty;
|
||||
*into = "";
|
||||
advance(p);
|
||||
return true;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user