wcc/ww: reject non-tuple multi-assign rhs (resolvewalk N_MASSIGN)

ww accepted a non-tuple rhs in a destructuring assignment that cstage
rejects (check.c:2562); the destructure then read garbage words.
This commit is contained in:
2026-06-12 11:24:58 +09:00
parent 6c380a53ea
commit 6f2ed0cc57
3 changed files with 42 additions and 6 deletions

View File

@@ -11096,9 +11096,21 @@ fn resolvewalk(c: *checker, n: *node) void = {
// #242: consume the rhs tuple type for ANY rhs (see N_MLET).
if (n.rhs != nil) {
let rty: *node = exprtype(c, n.rhs, nil);
if (rty != nil) { if (rty.kind == nkind.N_TTUPLE) {
if (rty != nil && rty.kind == nkind.N_TTUPLE) {
pt = rty.list;
}; };
} else {
// #38/F2 (review item 39): the multi-assign rhs must be a
// tuple. cstage check.c:2562-2568 errors "multi-assign rhs
// is not a tuple (got %s)" when cexpr(rhs)->kind != TY_TUPLE
// — and, like ww's exprtype (N_CALL returns the decl's bare
// return tnode, :3319), it does NOT chase the NAMED wrapper,
// so a tuple-ALIAS return (`fn f() pair`) is rejected too.
// Without this ww distributed nil element widths and
// cgmassign silently dropped the str len/cap stores. ww's
// piecewise cerr can't splice the type spelling, so the
// "(got %s)" tail is omitted.
deffolderr(c, n, "multi-assign rhs is not a tuple");
};
};
let l: *node = n.list;
for (l != nil) { resolvewalk(c, l); l = l.next; };

View File

@@ -676,9 +676,21 @@ fn resolvewalk(c: *checker, n: *node) void = {
// #242: consume the rhs tuple type for ANY rhs (see N_MLET).
if (n.rhs != nil) {
let rty: *node = exprtype(c, n.rhs, nil);
if (rty != nil) { if (rty.kind == nkind.N_TTUPLE) {
if (rty != nil && rty.kind == nkind.N_TTUPLE) {
pt = rty.list;
}; };
} else {
// #38/F2 (review item 39): the multi-assign rhs must be a
// tuple. cstage check.c:2562-2568 errors "multi-assign rhs
// is not a tuple (got %s)" when cexpr(rhs)->kind != TY_TUPLE
// — and, like ww's exprtype (N_CALL returns the decl's bare
// return tnode, :3319), it does NOT chase the NAMED wrapper,
// so a tuple-ALIAS return (`fn f() pair`) is rejected too.
// Without this ww distributed nil element widths and
// cgmassign silently dropped the str len/cap stores. ww's
// piecewise cerr can't splice the type spelling, so the
// "(got %s)" tail is omitted.
deffolderr(c, n, "multi-assign rhs is not a tuple");
};
};
let l: *node = n.list;
for (l != nil) { resolvewalk(c, l); l = l.next; };

View File

@@ -11096,9 +11096,21 @@ fn resolvewalk(c: *checker, n: *node) void = {
// #242: consume the rhs tuple type for ANY rhs (see N_MLET).
if (n.rhs != nil) {
let rty: *node = exprtype(c, n.rhs, nil);
if (rty != nil) { if (rty.kind == nkind.N_TTUPLE) {
if (rty != nil && rty.kind == nkind.N_TTUPLE) {
pt = rty.list;
}; };
} else {
// #38/F2 (review item 39): the multi-assign rhs must be a
// tuple. cstage check.c:2562-2568 errors "multi-assign rhs
// is not a tuple (got %s)" when cexpr(rhs)->kind != TY_TUPLE
// — and, like ww's exprtype (N_CALL returns the decl's bare
// return tnode, :3319), it does NOT chase the NAMED wrapper,
// so a tuple-ALIAS return (`fn f() pair`) is rejected too.
// Without this ww distributed nil element widths and
// cgmassign silently dropped the str len/cap stores. ww's
// piecewise cerr can't splice the type spelling, so the
// "(got %s)" tail is omitted.
deffolderr(c, n, "multi-assign rhs is not a tuple");
};
};
let l: *node = n.list;
for (l != nil) { resolvewalk(c, l); l = l.next; };