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:
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user