wcc/check: #12+#106 reject overlong array-literal in return/call-arg/alias positions (wwstage)
An overlong array literal (more initializers than the declared length) is invalid -- cstage loud-rejects it everywhere -- but wwstage silently accepted (and truncated) it in several positions; #9 wired only the decl position. This folds the remaining three (one class: checkarrlitfits over-fill coverage), all wwstage-only reject-align: - #12a return fn f() [2]int = [1,2,3] -- silently accepted. - #12b call-arg g([1,2,3]) -- louded only late via cgen #271. - #106 alias type A=[2]int; let g: A = [1,2,3] -- silently truncated; checkarrlitfits bailed on the N_TNAME alias without chasing. Four inserts in check.ww: an alias-chase (resolvealias) at the top of checkarrlitfits (makes all callers alias-aware), the over-fill check wired into checkretassign (hoisted above the isassignable short-circuit) and desugarcallargs, and the alias-let-global guard made alias-aware. cstage unchanged (w6c md5 unchanged); reject-only, so no asm moves -- 990-997 8/8, no lib byte-id pin flips. A 5th position (tuple-element overlong) is a separate pre-existing hole, filed (#20). test/wcc/828 table-driven.
This commit is contained in:
@@ -4226,6 +4226,14 @@ fn errnotassign(c: *checker, dst: *node, src: *node, where: str) void = {
|
||||
fn checkarrlitfits(c: *checker, arrtn: *node, rhs: *node) void = {
|
||||
if (arrtn == nil) { return; };
|
||||
if (rhs == nil) { return; };
|
||||
// #106: chase a TY_NAMED alias (`type A=[2]int`) to the underlying
|
||||
// [N]T before the kind gate — else an alias declared type bails
|
||||
// here and the over-fill (#9) never fires (silent DATA-truncate).
|
||||
// Idempotent on a non-alias (resolvealias returns the node), so a
|
||||
// direct N_TARRAY is untouched. Makes EVERY caller (decl/let/def/
|
||||
// struct/return/call-arg) alias-aware by construction.
|
||||
arrtn = resolvealias(c, unwrapbang(arrtn));
|
||||
if (arrtn == 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
|
||||
@@ -4464,6 +4472,14 @@ fn desugarcallargs(c: *checker, n: *node) void = {
|
||||
};
|
||||
}; };
|
||||
}; };
|
||||
// #12: overlong array-lit CALL-ARG — `g([1,2,3])`.
|
||||
// Reject at CHECK time (clean over-fill msg) instead
|
||||
// of falling to cgen #271's late aggregate-arg loud.
|
||||
// param.lhs is the declared param type (alias-aware
|
||||
// via the resolvealias chase in checkarrlitfits).
|
||||
if (a.kind == nkind.N_ARRLIT) {
|
||||
checkarrlitfits(c, param.lhs, a);
|
||||
};
|
||||
// #31/#33: bare array-literal arg has no backing
|
||||
// — loud-reject (supported only at a `let`).
|
||||
if (!rejectarrlitborrow(c, param.lhs, a)) {
|
||||
@@ -4672,7 +4688,16 @@ fn checkletassign(c: *checker, n: *node) void = {
|
||||
// (#146 merged). Mirrors cstage check.c arrlit_init_fits. Scoped
|
||||
// to the array path; scalar-init range-check is a separate
|
||||
// language-wide gap (#148).
|
||||
if (n.lhs.kind == nkind.N_TARRAY && n.rhs.kind == nkind.N_ARRLIT) {
|
||||
// #106: an alias-of-array lhs (`type A=[2]int; let g: A = […]`) arrives
|
||||
// as N_TNAME, so the bare N_TARRAY gate skipped it and the over-fill
|
||||
// never fired (silent DATA-truncate). The let path is the one caller
|
||||
// that pre-gates on the declared node's kind (def/return/call-arg pass
|
||||
// it straight to the alias-aware checkarrlitfits); resolve the alias
|
||||
// here too so an alias-of-array routes through the same over-fill. A
|
||||
// direct N_TARRAY is unchanged (resolvealias is idempotent), and the
|
||||
// early return matches the direct-array branch's pre-existing return.
|
||||
let llhs: *node = resolvealias(c, unwrapbang(n.lhs));
|
||||
if (llhs != nil && llhs.kind == nkind.N_TARRAY && n.rhs.kind == nkind.N_ARRLIT) {
|
||||
checkarrlitfits(c, n.lhs, n.rhs);
|
||||
return;
|
||||
};
|
||||
@@ -4744,6 +4769,16 @@ fn checkretassign(c: *checker, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
if (c.fnret == nil) { return; };
|
||||
// #12: overlong array-lit RETURN — `fn f() [2]int = { return [1,2,3]; }`.
|
||||
// #9 wired the over-fill at DECL only; the return position was lenient.
|
||||
// Fire BEFORE the isassignable/conf guards below — a direct [N]T return
|
||||
// type drives isassignable to conf=false (array-length leniency), which
|
||||
// would short-circuit the check (the alias path set conf=true, so neg3
|
||||
// caught it but the direct neg0 slipped). Alias-aware via the
|
||||
// resolvealias chase at the top of checkarrlitfits.
|
||||
if (n.lhs.kind == nkind.N_ARRLIT) {
|
||||
checkarrlitfits(c, c.fnret, n.lhs);
|
||||
};
|
||||
let src: *node = exprtype(c, n.lhs, nil);
|
||||
if (src == nil) { return; };
|
||||
let conf: bool = false;
|
||||
|
||||
Reference in New Issue
Block a user