wcc/check: #26 recurse over-fill walk into nested tuple element (wwstage)

extracts the shared checktuplearrfits helper (also used by #25); wwstage-only checker reject-align, cstage already louds.
This commit is contained in:
2026-06-09 15:00:03 +09:00
parent 46e8354056
commit 89f3e58458
4 changed files with 144 additions and 56 deletions

View File

@@ -14649,6 +14649,35 @@ fn checkarrlitfits(c: *checker, arrtn: *node, rhs: *node) void = {
};
};
// checktuplearrfits — #20/#25/#26: walk a declared tuple type's
// element types (ttn.list, each N_TPARAM-wrapped → type on .lhs)
// lockstep with an N_TUPLE rhs's values (tup.list, chained directly).
// An array-literal element runs the alias-aware over-fill
// checkarrlitfits; a nested-tuple element (declared N_TTUPLE vs rhs
// N_TUPLE) recurses. Shared by checkletassign (let) and
// checkretassign (return). Mirrors cstage's element-wise
// type_assignable count-reject; no-ops on scalar elements.
fn checktuplearrfits(c: *checker, ttn: *node, tup: *node) void = {
if (ttn == nil) { return; };
if (tup == nil) { return; };
let dt: *node = ttn.list;
let vt: *node = tup.list;
for (dt != nil && vt != nil) {
if (vt.kind == nkind.N_ARRLIT) {
checkarrlitfits(c, dt.lhs, vt);
} else {
if (vt.kind == nkind.N_TUPLE) {
let drt: *node = resolvealias(c, unwrapbang(dt.lhs));
if (drt != nil && drt.kind == nkind.N_TTUPLE) {
checktuplearrfits(c, drt, vt);
};
};
};
dt = dt.next;
vt = vt.next;
};
};
// desugararrayslice — #258. The single shared lowering for the implicit
// [N]T -> []T borrow. isassignable already admits an array with a defined
// length into a matching []T slot (see isassignable's #258 arm); here we
@@ -15005,24 +15034,16 @@ fn checkletassign(c: *checker, n: *node) void = {
// #20: array-typed TUPLE element with an overlong array literal —
// `let t:([2]int,i32) = ([1,2,3],5)` was silently accepted (the tuple
// position wasn't wired to checkarrlitfits, unlike the direct-array
// let above). Walk declared-tuple element types vs rhs values; over-
// fill-check each (checkarrlitfits chases aliases #106, recurses
// nested arrays #251, and no-ops on non-array elements). Mirror
// cstage's tuple element-wise reject. No early return — the rest of
// checkletassign still runs for the tuple. N_TTUPLE elements wrap
// their type on .lhs (N_TPARAM chain, stamptuplebinds:311); the
// N_TUPLE rhs values chain directly on .list.
// let above). #26: the walk now lives in checktuplearrfits, which also
// recurses into a nested-tuple element (checkarrlitfits chases aliases
// #106, recurses nested arrays #251; the helper no-ops on non-array,
// non-tuple elements). Mirror cstage's tuple element-wise reject. No
// early return — the rest of checkletassign still runs for the tuple.
// N_TTUPLE elements wrap their type on .lhs (N_TPARAM chain,
// stamptuplebinds:311); the N_TUPLE rhs values chain directly on .list.
if (llhs != nil && llhs.kind == nkind.N_TTUPLE
&& n.rhs.kind == nkind.N_TUPLE) {
let dt: *node = llhs.list;
let vt: *node = n.rhs.list;
for (dt != nil && vt != nil) {
if (vt.kind == nkind.N_ARRLIT) {
checkarrlitfits(c, dt.lhs, vt);
};
dt = dt.next;
vt = vt.next;
};
checktuplearrfits(c, llhs, n.rhs);
};
// #25/#31: an array literal initialising a SLICE local. Re-stamp the
// literal as [count]T (the slice element) so the #258 borrow's exact-

View File

@@ -4368,6 +4368,35 @@ fn checkarrlitfits(c: *checker, arrtn: *node, rhs: *node) void = {
};
};
// checktuplearrfits — #20/#25/#26: walk a declared tuple type's
// element types (ttn.list, each N_TPARAM-wrapped → type on .lhs)
// lockstep with an N_TUPLE rhs's values (tup.list, chained directly).
// An array-literal element runs the alias-aware over-fill
// checkarrlitfits; a nested-tuple element (declared N_TTUPLE vs rhs
// N_TUPLE) recurses. Shared by checkletassign (let) and
// checkretassign (return). Mirrors cstage's element-wise
// type_assignable count-reject; no-ops on scalar elements.
fn checktuplearrfits(c: *checker, ttn: *node, tup: *node) void = {
if (ttn == nil) { return; };
if (tup == nil) { return; };
let dt: *node = ttn.list;
let vt: *node = tup.list;
for (dt != nil && vt != nil) {
if (vt.kind == nkind.N_ARRLIT) {
checkarrlitfits(c, dt.lhs, vt);
} else {
if (vt.kind == nkind.N_TUPLE) {
let drt: *node = resolvealias(c, unwrapbang(dt.lhs));
if (drt != nil && drt.kind == nkind.N_TTUPLE) {
checktuplearrfits(c, drt, vt);
};
};
};
dt = dt.next;
vt = vt.next;
};
};
// desugararrayslice — #258. The single shared lowering for the implicit
// [N]T -> []T borrow. isassignable already admits an array with a defined
// length into a matching []T slot (see isassignable's #258 arm); here we
@@ -4724,24 +4753,16 @@ fn checkletassign(c: *checker, n: *node) void = {
// #20: array-typed TUPLE element with an overlong array literal —
// `let t:([2]int,i32) = ([1,2,3],5)` was silently accepted (the tuple
// position wasn't wired to checkarrlitfits, unlike the direct-array
// let above). Walk declared-tuple element types vs rhs values; over-
// fill-check each (checkarrlitfits chases aliases #106, recurses
// nested arrays #251, and no-ops on non-array elements). Mirror
// cstage's tuple element-wise reject. No early return — the rest of
// checkletassign still runs for the tuple. N_TTUPLE elements wrap
// their type on .lhs (N_TPARAM chain, stamptuplebinds:311); the
// N_TUPLE rhs values chain directly on .list.
// let above). #26: the walk now lives in checktuplearrfits, which also
// recurses into a nested-tuple element (checkarrlitfits chases aliases
// #106, recurses nested arrays #251; the helper no-ops on non-array,
// non-tuple elements). Mirror cstage's tuple element-wise reject. No
// early return — the rest of checkletassign still runs for the tuple.
// N_TTUPLE elements wrap their type on .lhs (N_TPARAM chain,
// stamptuplebinds:311); the N_TUPLE rhs values chain directly on .list.
if (llhs != nil && llhs.kind == nkind.N_TTUPLE
&& n.rhs.kind == nkind.N_TUPLE) {
let dt: *node = llhs.list;
let vt: *node = n.rhs.list;
for (dt != nil && vt != nil) {
if (vt.kind == nkind.N_ARRLIT) {
checkarrlitfits(c, dt.lhs, vt);
};
dt = dt.next;
vt = vt.next;
};
checktuplearrfits(c, llhs, n.rhs);
};
// #25/#31: an array literal initialising a SLICE local. Re-stamp the
// literal as [count]T (the slice element) so the #258 borrow's exact-

View File

@@ -14649,6 +14649,35 @@ fn checkarrlitfits(c: *checker, arrtn: *node, rhs: *node) void = {
};
};
// checktuplearrfits — #20/#25/#26: walk a declared tuple type's
// element types (ttn.list, each N_TPARAM-wrapped → type on .lhs)
// lockstep with an N_TUPLE rhs's values (tup.list, chained directly).
// An array-literal element runs the alias-aware over-fill
// checkarrlitfits; a nested-tuple element (declared N_TTUPLE vs rhs
// N_TUPLE) recurses. Shared by checkletassign (let) and
// checkretassign (return). Mirrors cstage's element-wise
// type_assignable count-reject; no-ops on scalar elements.
fn checktuplearrfits(c: *checker, ttn: *node, tup: *node) void = {
if (ttn == nil) { return; };
if (tup == nil) { return; };
let dt: *node = ttn.list;
let vt: *node = tup.list;
for (dt != nil && vt != nil) {
if (vt.kind == nkind.N_ARRLIT) {
checkarrlitfits(c, dt.lhs, vt);
} else {
if (vt.kind == nkind.N_TUPLE) {
let drt: *node = resolvealias(c, unwrapbang(dt.lhs));
if (drt != nil && drt.kind == nkind.N_TTUPLE) {
checktuplearrfits(c, drt, vt);
};
};
};
dt = dt.next;
vt = vt.next;
};
};
// desugararrayslice — #258. The single shared lowering for the implicit
// [N]T -> []T borrow. isassignable already admits an array with a defined
// length into a matching []T slot (see isassignable's #258 arm); here we
@@ -15005,24 +15034,16 @@ fn checkletassign(c: *checker, n: *node) void = {
// #20: array-typed TUPLE element with an overlong array literal —
// `let t:([2]int,i32) = ([1,2,3],5)` was silently accepted (the tuple
// position wasn't wired to checkarrlitfits, unlike the direct-array
// let above). Walk declared-tuple element types vs rhs values; over-
// fill-check each (checkarrlitfits chases aliases #106, recurses
// nested arrays #251, and no-ops on non-array elements). Mirror
// cstage's tuple element-wise reject. No early return — the rest of
// checkletassign still runs for the tuple. N_TTUPLE elements wrap
// their type on .lhs (N_TPARAM chain, stamptuplebinds:311); the
// N_TUPLE rhs values chain directly on .list.
// let above). #26: the walk now lives in checktuplearrfits, which also
// recurses into a nested-tuple element (checkarrlitfits chases aliases
// #106, recurses nested arrays #251; the helper no-ops on non-array,
// non-tuple elements). Mirror cstage's tuple element-wise reject. No
// early return — the rest of checkletassign still runs for the tuple.
// N_TTUPLE elements wrap their type on .lhs (N_TPARAM chain,
// stamptuplebinds:311); the N_TUPLE rhs values chain directly on .list.
if (llhs != nil && llhs.kind == nkind.N_TTUPLE
&& n.rhs.kind == nkind.N_TUPLE) {
let dt: *node = llhs.list;
let vt: *node = n.rhs.list;
for (dt != nil && vt != nil) {
if (vt.kind == nkind.N_ARRLIT) {
checkarrlitfits(c, dt.lhs, vt);
};
dt = dt.next;
vt = vt.next;
};
checktuplearrfits(c, llhs, n.rhs);
};
// #25/#31: an array literal initialising a SLICE local. Re-stamp the
// literal as [count]T (the slice element) so the #258 borrow's exact-