wcc/check+wcc_ww/check: reject overlong array literal — frame-smash class (#71)
An array literal with more elements than the declared [N] passed the per-element accept-if-fits checks in both stages and cgen then stored every element at its natural offset, writing past the slot: local frames smashed silently (the repeat form [1,2,3...] into [2]int wrote at the saved BP), module DATA corrupted neighbours. All four declaration contexts (local let, module let, def, struct-field literal) funnel through one choke point per stage — arrlit_init_fits (check.c) / checkarrlitfits (check.ww) — which now pre-counts the literal (skipping the ... marker) and rejects count > N naming both counts. cstage clet's blanket has_arr_repeat bypass is narrowed to non-array declared targets: repeat literals into arrays now run the same overlong + #130 range checks wwstage's checkletassign always ran (the bypass let [2]u8 = [999...] dodge the range check cstage-only). checkarrlitfits also recurses into NESTED array-literal elements (declared elem node N_TARRAY): cstage catches the nested shape through its typed-literal assignability net, which wwstage's untyped elements have no analog of — [2][2]int = [[1,2,3],[4,5]] at module scope silently emitted corrupted DATA (1,2,4,5) and the struct-field twin likewise. Recursion through the one choke point closes any depth; a named-alias element type still bypasses — task #16. alen==0/nil-length stays exempt ([0]/[_] sentinel conflation and un-inferred [_] in def/struct-field — task #11); a non-INTLIT length child (def-named [N]) is exempt in wwstage — task #13; under-long literals keep their current accept (Hare rejects — task #10); wwstage's overlong accept at assign/call-arg/return position (cstage already rejects) is task #12; exact-fit bare-int nested cs-reject/ ww-accept divergence is pre-existing — task #17.
This commit is contained in:
@@ -14325,6 +14325,39 @@ fn checkarrlitfits(c: *checker, arrtn: *node, rhs: *node) void = {
|
||||
if (rhs == nil) { return; };
|
||||
if (arrtn.kind != nkind.N_TARRAY) { return; };
|
||||
if (rhs.kind != nkind.N_ARRLIT) { return; };
|
||||
// #71: more elements than the declared [N] passed every per-element
|
||||
// check below and then smashed the frame at cgen (each element is
|
||||
// stored at its natural offset — the overflow clobbered neighbours
|
||||
// and even the saved BP). Reject loud before the element walk.
|
||||
// A nil or zero length child stays exempt: nil is the un-inferred
|
||||
// [_] sentinel in def/struct-field contexts and 0 doubles as both
|
||||
// [0] and the cstage [_] sentinel (conflation: task #11); the let
|
||||
// paths stamp the real length before reaching here. A non-INTLIT
|
||||
// length child (def-named [N]) is also exempt — task #13.
|
||||
// Under-long (count < N, no `...`) stays accepted as before; Hare
|
||||
// rejects it — task #10.
|
||||
if (arrtn.rhs != nil && arrtn.rhs.kind == nkind.N_INTLIT
|
||||
&& arrtn.rhs.uval > 0u64) {
|
||||
let cnt: u64 = 0u64;
|
||||
let ce: *node = rhs.list;
|
||||
for (ce != nil) {
|
||||
let cskip: bool = false;
|
||||
if (ce.kind == nkind.N_FIELD) {
|
||||
if (streq(ce.str, "...")) { cskip = true; };
|
||||
};
|
||||
if (!cskip) { cnt += 1u64; };
|
||||
ce = ce.next;
|
||||
};
|
||||
if (cnt > arrtn.rhs.uval) {
|
||||
cerr("array literal has ");
|
||||
cerr(strconv.u64tos(cnt, strconv.base.DEC));
|
||||
cerr(" elements but declared array holds ");
|
||||
cerr(strconv.u64tos(arrtn.rhs.uval, strconv.base.DEC));
|
||||
cerr("\n");
|
||||
c.errs += 1;
|
||||
return;
|
||||
};
|
||||
};
|
||||
let elemtn: *node = arrtn.lhs;
|
||||
let at: *tinfo = tinfofornode(c, arrtn);
|
||||
let et: *tinfo = nil;
|
||||
@@ -14339,6 +14372,21 @@ fn checkarrlitfits(c: *checker, arrtn: *node, rhs: *node) void = {
|
||||
if (!skip) {
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
// #71: a NESTED array-literal element must run the same
|
||||
// count check against the inner [N] — cstage catches the
|
||||
// nested shape through its typed-literal assignability net
|
||||
// (the literal's stamped [2][3]T fails type_assignable),
|
||||
// which wwstage's untyped elements have no analog of; the
|
||||
// silent accept emitted corrupted DATA / smashed frames.
|
||||
// Recursion through the one choke point closes any depth.
|
||||
// A named-alias element type ([2]row) still bypasses — the
|
||||
// elemtn node is N_IDENT, not N_TARRAY — task #16.
|
||||
if (elemtn != nil && elemtn.kind == nkind.N_TARRAY
|
||||
&& ev != nil && ev.kind == nkind.N_ARRLIT) {
|
||||
checkarrlitfits(c, elemtn, ev);
|
||||
e = e.next;
|
||||
continue;
|
||||
};
|
||||
let v: u64 = 0u64;
|
||||
let folded: bool = false;
|
||||
if (et != nil) {
|
||||
|
||||
@@ -4008,6 +4008,39 @@ fn checkarrlitfits(c: *checker, arrtn: *node, rhs: *node) void = {
|
||||
if (rhs == nil) { return; };
|
||||
if (arrtn.kind != nkind.N_TARRAY) { return; };
|
||||
if (rhs.kind != nkind.N_ARRLIT) { return; };
|
||||
// #71: more elements than the declared [N] passed every per-element
|
||||
// check below and then smashed the frame at cgen (each element is
|
||||
// stored at its natural offset — the overflow clobbered neighbours
|
||||
// and even the saved BP). Reject loud before the element walk.
|
||||
// A nil or zero length child stays exempt: nil is the un-inferred
|
||||
// [_] sentinel in def/struct-field contexts and 0 doubles as both
|
||||
// [0] and the cstage [_] sentinel (conflation: task #11); the let
|
||||
// paths stamp the real length before reaching here. A non-INTLIT
|
||||
// length child (def-named [N]) is also exempt — task #13.
|
||||
// Under-long (count < N, no `...`) stays accepted as before; Hare
|
||||
// rejects it — task #10.
|
||||
if (arrtn.rhs != nil && arrtn.rhs.kind == nkind.N_INTLIT
|
||||
&& arrtn.rhs.uval > 0u64) {
|
||||
let cnt: u64 = 0u64;
|
||||
let ce: *node = rhs.list;
|
||||
for (ce != nil) {
|
||||
let cskip: bool = false;
|
||||
if (ce.kind == nkind.N_FIELD) {
|
||||
if (streq(ce.str, "...")) { cskip = true; };
|
||||
};
|
||||
if (!cskip) { cnt += 1u64; };
|
||||
ce = ce.next;
|
||||
};
|
||||
if (cnt > arrtn.rhs.uval) {
|
||||
cerr("array literal has ");
|
||||
cerr(strconv.u64tos(cnt, strconv.base.DEC));
|
||||
cerr(" elements but declared array holds ");
|
||||
cerr(strconv.u64tos(arrtn.rhs.uval, strconv.base.DEC));
|
||||
cerr("\n");
|
||||
c.errs += 1;
|
||||
return;
|
||||
};
|
||||
};
|
||||
let elemtn: *node = arrtn.lhs;
|
||||
let at: *tinfo = tinfofornode(c, arrtn);
|
||||
let et: *tinfo = nil;
|
||||
@@ -4022,6 +4055,21 @@ fn checkarrlitfits(c: *checker, arrtn: *node, rhs: *node) void = {
|
||||
if (!skip) {
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
// #71: a NESTED array-literal element must run the same
|
||||
// count check against the inner [N] — cstage catches the
|
||||
// nested shape through its typed-literal assignability net
|
||||
// (the literal's stamped [2][3]T fails type_assignable),
|
||||
// which wwstage's untyped elements have no analog of; the
|
||||
// silent accept emitted corrupted DATA / smashed frames.
|
||||
// Recursion through the one choke point closes any depth.
|
||||
// A named-alias element type ([2]row) still bypasses — the
|
||||
// elemtn node is N_IDENT, not N_TARRAY — task #16.
|
||||
if (elemtn != nil && elemtn.kind == nkind.N_TARRAY
|
||||
&& ev != nil && ev.kind == nkind.N_ARRLIT) {
|
||||
checkarrlitfits(c, elemtn, ev);
|
||||
e = e.next;
|
||||
continue;
|
||||
};
|
||||
let v: u64 = 0u64;
|
||||
let folded: bool = false;
|
||||
if (et != nil) {
|
||||
|
||||
@@ -14325,6 +14325,39 @@ fn checkarrlitfits(c: *checker, arrtn: *node, rhs: *node) void = {
|
||||
if (rhs == nil) { return; };
|
||||
if (arrtn.kind != nkind.N_TARRAY) { return; };
|
||||
if (rhs.kind != nkind.N_ARRLIT) { return; };
|
||||
// #71: more elements than the declared [N] passed every per-element
|
||||
// check below and then smashed the frame at cgen (each element is
|
||||
// stored at its natural offset — the overflow clobbered neighbours
|
||||
// and even the saved BP). Reject loud before the element walk.
|
||||
// A nil or zero length child stays exempt: nil is the un-inferred
|
||||
// [_] sentinel in def/struct-field contexts and 0 doubles as both
|
||||
// [0] and the cstage [_] sentinel (conflation: task #11); the let
|
||||
// paths stamp the real length before reaching here. A non-INTLIT
|
||||
// length child (def-named [N]) is also exempt — task #13.
|
||||
// Under-long (count < N, no `...`) stays accepted as before; Hare
|
||||
// rejects it — task #10.
|
||||
if (arrtn.rhs != nil && arrtn.rhs.kind == nkind.N_INTLIT
|
||||
&& arrtn.rhs.uval > 0u64) {
|
||||
let cnt: u64 = 0u64;
|
||||
let ce: *node = rhs.list;
|
||||
for (ce != nil) {
|
||||
let cskip: bool = false;
|
||||
if (ce.kind == nkind.N_FIELD) {
|
||||
if (streq(ce.str, "...")) { cskip = true; };
|
||||
};
|
||||
if (!cskip) { cnt += 1u64; };
|
||||
ce = ce.next;
|
||||
};
|
||||
if (cnt > arrtn.rhs.uval) {
|
||||
cerr("array literal has ");
|
||||
cerr(strconv.u64tos(cnt, strconv.base.DEC));
|
||||
cerr(" elements but declared array holds ");
|
||||
cerr(strconv.u64tos(arrtn.rhs.uval, strconv.base.DEC));
|
||||
cerr("\n");
|
||||
c.errs += 1;
|
||||
return;
|
||||
};
|
||||
};
|
||||
let elemtn: *node = arrtn.lhs;
|
||||
let at: *tinfo = tinfofornode(c, arrtn);
|
||||
let et: *tinfo = nil;
|
||||
@@ -14339,6 +14372,21 @@ fn checkarrlitfits(c: *checker, arrtn: *node, rhs: *node) void = {
|
||||
if (!skip) {
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
// #71: a NESTED array-literal element must run the same
|
||||
// count check against the inner [N] — cstage catches the
|
||||
// nested shape through its typed-literal assignability net
|
||||
// (the literal's stamped [2][3]T fails type_assignable),
|
||||
// which wwstage's untyped elements have no analog of; the
|
||||
// silent accept emitted corrupted DATA / smashed frames.
|
||||
// Recursion through the one choke point closes any depth.
|
||||
// A named-alias element type ([2]row) still bypasses — the
|
||||
// elemtn node is N_IDENT, not N_TARRAY — task #16.
|
||||
if (elemtn != nil && elemtn.kind == nkind.N_TARRAY
|
||||
&& ev != nil && ev.kind == nkind.N_ARRLIT) {
|
||||
checkarrlitfits(c, elemtn, ev);
|
||||
e = e.next;
|
||||
continue;
|
||||
};
|
||||
let v: u64 = 0u64;
|
||||
let folded: bool = false;
|
||||
if (et != nil) {
|
||||
|
||||
Reference in New Issue
Block a user