wcc/ww: reject tuple-literal arity mismatch (checktuplearrfits)

A tuple literal with the wrong arity was silently accepted and the
extra/missing elements mis-stored. Mirror cstage type.c:416 +
check.c:2386.
This commit is contained in:
2026-06-12 11:21:39 +09:00
parent bd86fa5f46
commit 6c380a53ea
3 changed files with 75 additions and 0 deletions

View File

@@ -15503,6 +15503,31 @@ fn checkarrlitfits(c: *checker, arrtn: *node, rhs: *node) void = {
fn checktuplearrfits(c: *checker, ttn: *node, tup: *node) void = {
if (ttn == nil) { return; };
if (tup == nil) { return; };
// #38/F2 (review item 10): a tuple literal whose arity differs from the
// declared tuple is LOUD. cstage type_assignable's tuple arm requires
// both param chains to end together (cmd/wcc/type.c:416, consumed as
// "init not assignable" at check.c:2386-2391); wwstage's isassignable
// has NO tuple arm, so an over/short literal fell to the lenient
// catch-all and the lockstep walk below silently ignored the leftovers.
// The short direction was the live miscompile (cgen stored a stale,
// never-popped register into the missing slot). Count both chains and
// reject a mismatch before the per-element fits walk. Recursion through
// this one choke point gates every nesting depth.
let dn: u64 = 0u64;
let dc: *node = ttn.list;
for (dc != nil) { dn += 1u64; dc = dc.next; };
let vn: u64 = 0u64;
let vc: *node = tup.list;
for (vc != nil) { vn += 1u64; vc = vc.next; };
if (dn != vn) {
cerr("tuple literal has ");
cerr(strconv.u64tos(vn, strconv.base.DEC));
cerr(" elements but declared tuple holds ");
cerr(strconv.u64tos(dn, strconv.base.DEC));
cerr("\n");
c.errs += 1;
return;
};
let dt: *node = ttn.list;
let vt: *node = tup.list;
for (dt != nil && vt != nil) {

View File

@@ -5083,6 +5083,31 @@ fn checkarrlitfits(c: *checker, arrtn: *node, rhs: *node) void = {
fn checktuplearrfits(c: *checker, ttn: *node, tup: *node) void = {
if (ttn == nil) { return; };
if (tup == nil) { return; };
// #38/F2 (review item 10): a tuple literal whose arity differs from the
// declared tuple is LOUD. cstage type_assignable's tuple arm requires
// both param chains to end together (cmd/wcc/type.c:416, consumed as
// "init not assignable" at check.c:2386-2391); wwstage's isassignable
// has NO tuple arm, so an over/short literal fell to the lenient
// catch-all and the lockstep walk below silently ignored the leftovers.
// The short direction was the live miscompile (cgen stored a stale,
// never-popped register into the missing slot). Count both chains and
// reject a mismatch before the per-element fits walk. Recursion through
// this one choke point gates every nesting depth.
let dn: u64 = 0u64;
let dc: *node = ttn.list;
for (dc != nil) { dn += 1u64; dc = dc.next; };
let vn: u64 = 0u64;
let vc: *node = tup.list;
for (vc != nil) { vn += 1u64; vc = vc.next; };
if (dn != vn) {
cerr("tuple literal has ");
cerr(strconv.u64tos(vn, strconv.base.DEC));
cerr(" elements but declared tuple holds ");
cerr(strconv.u64tos(dn, strconv.base.DEC));
cerr("\n");
c.errs += 1;
return;
};
let dt: *node = ttn.list;
let vt: *node = tup.list;
for (dt != nil && vt != nil) {

View File

@@ -15503,6 +15503,31 @@ fn checkarrlitfits(c: *checker, arrtn: *node, rhs: *node) void = {
fn checktuplearrfits(c: *checker, ttn: *node, tup: *node) void = {
if (ttn == nil) { return; };
if (tup == nil) { return; };
// #38/F2 (review item 10): a tuple literal whose arity differs from the
// declared tuple is LOUD. cstage type_assignable's tuple arm requires
// both param chains to end together (cmd/wcc/type.c:416, consumed as
// "init not assignable" at check.c:2386-2391); wwstage's isassignable
// has NO tuple arm, so an over/short literal fell to the lenient
// catch-all and the lockstep walk below silently ignored the leftovers.
// The short direction was the live miscompile (cgen stored a stale,
// never-popped register into the missing slot). Count both chains and
// reject a mismatch before the per-element fits walk. Recursion through
// this one choke point gates every nesting depth.
let dn: u64 = 0u64;
let dc: *node = ttn.list;
for (dc != nil) { dn += 1u64; dc = dc.next; };
let vn: u64 = 0u64;
let vc: *node = tup.list;
for (vc != nil) { vn += 1u64; vc = vc.next; };
if (dn != vn) {
cerr("tuple literal has ");
cerr(strconv.u64tos(vn, strconv.base.DEC));
cerr(" elements but declared tuple holds ");
cerr(strconv.u64tos(dn, strconv.base.DEC));
cerr("\n");
c.errs += 1;
return;
};
let dt: *node = ttn.list;
let vt: *node = tup.list;
for (dt != nil && vt != nil) {