wcc/check: #24-sib reject array-payload tagged-union construction, both stages

Constructing an array-typed payload into a tagged-union variant silently
dropped it (cstage MOVQ $0 -> returns 0; wwstage match-loud only). Reject
the construct when the selected variant chases to TY_ARRAY (target
TY_TAGGED, non-tagged source). tagged-struct/slice/scalar/str variants and
the array TYPE-decl stay legal. Faithful array-into-box block-store
deferred (#6). test/wcc/834 (new) + Makefile.
This commit is contained in:
2026-06-09 17:30:34 +09:00
parent 785fe342fa
commit d1ac836fb9
6 changed files with 502 additions and 0 deletions

View File

@@ -14526,6 +14526,45 @@ fn errnotassign(c: *checker, dst: *node, src: *node, where: str) void = {
c.errs += 1;
};
// taggedarrayvariantctor — #5/#60: true when `src` is boxed into the
// tagged union `dst` via a variant that chases to TY_ARRAY. The array-
// payload box is unwired in cgen (cstage zero-filled the slot at box
// materialization — a silent MOVQ $0 payload drop; wwstage only loud at
// the dead match arm, errbadcase:4107). Reject the CONSTRUCT until the
// faithful array-block-store lands (deferred task #6). The tagged TYPE-
// decl with an array variant stays legal — test 944 declares
// (void|size|[5]size) and boxes only the narrow `size` variant — only
// the array-variant box is refused. Mirrors the concrete→tagged variant
// select in isassignable (:3859) and cstage tagged_array_variant so the
// variant chosen here is the one the box would materialize.
fn taggedarrayvariantctor(c: *checker, dst: *node, src: *node) bool = {
if (dst == nil) { return false; };
if (src == nil) { return false; };
let du: *node = resolvealias(c, unwrapbang(dst));
let su: *node = resolvealias(c, unwrapbang(src));
if (du == nil) { return false; };
if (du.kind != nkind.N_TTAGGED) { return false; };
if (su != nil) { if (su.kind == nkind.N_TTAGGED) { return false; }; };
let v: *node = du.list;
for (v != nil) {
let vspread: bool = (v.op == tkind.TK_ELLIPSIS);
let vu: *node = resolvealias(c, unwrapbang(v));
let vtagged: bool = false;
if (vu != nil) { if (vu.kind == nkind.N_TTAGGED) { vtagged = true; }; };
if (vtagged && !vspread) {
if (typeeqast(v, src)) { return false; };
} else {
let innerconf: bool = false;
if (isassignable(c, v, src, &innerconf)) {
if (vu != nil) { if (vu.kind == nkind.N_TARRAY) { return true; }; };
return false;
};
};
v = v.next;
};
return false;
};
// checkarrlitfits — #130/#251 array-init accept-if-fits, shared by the
// let / def / struct-field init sites. arrtn is the declared [N]T type
// node, rhs the N_ARRLIT. Per element: foldable int/rune literal →
@@ -15098,6 +15137,13 @@ fn checkletassign(c: *checker, n: *node) void = {
if (!ok) { if (assignableaddrfn(c, n.lhs, n.rhs)) { ok = true; }; };
if (!conf) { return; };
if (!ok) { errnotassign(c, n.lhs, src, "let"); };
// #5/#60: reject boxing an array payload into a tagged variant. cerr
// alone does NOT fail the build — bump c.errs (errnotassign:4245 idiom).
if (taggedarrayvariantctor(c, n.lhs, src)) {
cerr("array-typed tagged-union variant construction unwired — reject (task #5 / #60)\n");
c.errs += 1;
return;
};
// #258: `let s: []T = arr` borrows the array as a full slice. The
// desugar lowers to a runtime `arr[0:len]` N_SLICE, so it only
// applies to LOCAL lets (a fn body executes the borrow). A MODULE-
@@ -15156,6 +15202,13 @@ fn checkretassign(c: *checker, n: *node) void = {
if (!ok) { if (assignableaddrfn(c, c.fnret, n.lhs)) { ok = true; }; };
if (!conf) { return; };
if (!ok) { errnotassign(c, c.fnret, src, "return"); };
// #5/#60: reject returning an array payload into a tagged variant. cerr
// alone does NOT fail the build — bump c.errs (errnotassign:4245 idiom).
if (taggedarrayvariantctor(c, c.fnret, src)) {
cerr("array-typed tagged-union variant construction unwired — reject (task #5 / #60)\n");
c.errs += 1;
return;
};
// #258: `return arr` borrows the array as a full slice.
// #31/#33: bare array-literal has no backing — loud-reject
// (supported only at a `let`).

View File

@@ -4245,6 +4245,45 @@ fn errnotassign(c: *checker, dst: *node, src: *node, where: str) void = {
c.errs += 1;
};
// taggedarrayvariantctor — #5/#60: true when `src` is boxed into the
// tagged union `dst` via a variant that chases to TY_ARRAY. The array-
// payload box is unwired in cgen (cstage zero-filled the slot at box
// materialization — a silent MOVQ $0 payload drop; wwstage only loud at
// the dead match arm, errbadcase:4107). Reject the CONSTRUCT until the
// faithful array-block-store lands (deferred task #6). The tagged TYPE-
// decl with an array variant stays legal — test 944 declares
// (void|size|[5]size) and boxes only the narrow `size` variant — only
// the array-variant box is refused. Mirrors the concrete→tagged variant
// select in isassignable (:3859) and cstage tagged_array_variant so the
// variant chosen here is the one the box would materialize.
fn taggedarrayvariantctor(c: *checker, dst: *node, src: *node) bool = {
if (dst == nil) { return false; };
if (src == nil) { return false; };
let du: *node = resolvealias(c, unwrapbang(dst));
let su: *node = resolvealias(c, unwrapbang(src));
if (du == nil) { return false; };
if (du.kind != nkind.N_TTAGGED) { return false; };
if (su != nil) { if (su.kind == nkind.N_TTAGGED) { return false; }; };
let v: *node = du.list;
for (v != nil) {
let vspread: bool = (v.op == tkind.TK_ELLIPSIS);
let vu: *node = resolvealias(c, unwrapbang(v));
let vtagged: bool = false;
if (vu != nil) { if (vu.kind == nkind.N_TTAGGED) { vtagged = true; }; };
if (vtagged && !vspread) {
if (typeeqast(v, src)) { return false; };
} else {
let innerconf: bool = false;
if (isassignable(c, v, src, &innerconf)) {
if (vu != nil) { if (vu.kind == nkind.N_TARRAY) { return true; }; };
return false;
};
};
v = v.next;
};
return false;
};
// checkarrlitfits — #130/#251 array-init accept-if-fits, shared by the
// let / def / struct-field init sites. arrtn is the declared [N]T type
// node, rhs the N_ARRLIT. Per element: foldable int/rune literal →
@@ -4817,6 +4856,13 @@ fn checkletassign(c: *checker, n: *node) void = {
if (!ok) { if (assignableaddrfn(c, n.lhs, n.rhs)) { ok = true; }; };
if (!conf) { return; };
if (!ok) { errnotassign(c, n.lhs, src, "let"); };
// #5/#60: reject boxing an array payload into a tagged variant. cerr
// alone does NOT fail the build — bump c.errs (errnotassign:4245 idiom).
if (taggedarrayvariantctor(c, n.lhs, src)) {
cerr("array-typed tagged-union variant construction unwired — reject (task #5 / #60)\n");
c.errs += 1;
return;
};
// #258: `let s: []T = arr` borrows the array as a full slice. The
// desugar lowers to a runtime `arr[0:len]` N_SLICE, so it only
// applies to LOCAL lets (a fn body executes the borrow). A MODULE-
@@ -4875,6 +4921,13 @@ fn checkretassign(c: *checker, n: *node) void = {
if (!ok) { if (assignableaddrfn(c, c.fnret, n.lhs)) { ok = true; }; };
if (!conf) { return; };
if (!ok) { errnotassign(c, c.fnret, src, "return"); };
// #5/#60: reject returning an array payload into a tagged variant. cerr
// alone does NOT fail the build — bump c.errs (errnotassign:4245 idiom).
if (taggedarrayvariantctor(c, c.fnret, src)) {
cerr("array-typed tagged-union variant construction unwired — reject (task #5 / #60)\n");
c.errs += 1;
return;
};
// #258: `return arr` borrows the array as a full slice.
// #31/#33: bare array-literal has no backing — loud-reject
// (supported only at a `let`).

View File

@@ -14526,6 +14526,45 @@ fn errnotassign(c: *checker, dst: *node, src: *node, where: str) void = {
c.errs += 1;
};
// taggedarrayvariantctor — #5/#60: true when `src` is boxed into the
// tagged union `dst` via a variant that chases to TY_ARRAY. The array-
// payload box is unwired in cgen (cstage zero-filled the slot at box
// materialization — a silent MOVQ $0 payload drop; wwstage only loud at
// the dead match arm, errbadcase:4107). Reject the CONSTRUCT until the
// faithful array-block-store lands (deferred task #6). The tagged TYPE-
// decl with an array variant stays legal — test 944 declares
// (void|size|[5]size) and boxes only the narrow `size` variant — only
// the array-variant box is refused. Mirrors the concrete→tagged variant
// select in isassignable (:3859) and cstage tagged_array_variant so the
// variant chosen here is the one the box would materialize.
fn taggedarrayvariantctor(c: *checker, dst: *node, src: *node) bool = {
if (dst == nil) { return false; };
if (src == nil) { return false; };
let du: *node = resolvealias(c, unwrapbang(dst));
let su: *node = resolvealias(c, unwrapbang(src));
if (du == nil) { return false; };
if (du.kind != nkind.N_TTAGGED) { return false; };
if (su != nil) { if (su.kind == nkind.N_TTAGGED) { return false; }; };
let v: *node = du.list;
for (v != nil) {
let vspread: bool = (v.op == tkind.TK_ELLIPSIS);
let vu: *node = resolvealias(c, unwrapbang(v));
let vtagged: bool = false;
if (vu != nil) { if (vu.kind == nkind.N_TTAGGED) { vtagged = true; }; };
if (vtagged && !vspread) {
if (typeeqast(v, src)) { return false; };
} else {
let innerconf: bool = false;
if (isassignable(c, v, src, &innerconf)) {
if (vu != nil) { if (vu.kind == nkind.N_TARRAY) { return true; }; };
return false;
};
};
v = v.next;
};
return false;
};
// checkarrlitfits — #130/#251 array-init accept-if-fits, shared by the
// let / def / struct-field init sites. arrtn is the declared [N]T type
// node, rhs the N_ARRLIT. Per element: foldable int/rune literal →
@@ -15098,6 +15137,13 @@ fn checkletassign(c: *checker, n: *node) void = {
if (!ok) { if (assignableaddrfn(c, n.lhs, n.rhs)) { ok = true; }; };
if (!conf) { return; };
if (!ok) { errnotassign(c, n.lhs, src, "let"); };
// #5/#60: reject boxing an array payload into a tagged variant. cerr
// alone does NOT fail the build — bump c.errs (errnotassign:4245 idiom).
if (taggedarrayvariantctor(c, n.lhs, src)) {
cerr("array-typed tagged-union variant construction unwired — reject (task #5 / #60)\n");
c.errs += 1;
return;
};
// #258: `let s: []T = arr` borrows the array as a full slice. The
// desugar lowers to a runtime `arr[0:len]` N_SLICE, so it only
// applies to LOCAL lets (a fn body executes the borrow). A MODULE-
@@ -15156,6 +15202,13 @@ fn checkretassign(c: *checker, n: *node) void = {
if (!ok) { if (assignableaddrfn(c, c.fnret, n.lhs)) { ok = true; }; };
if (!conf) { return; };
if (!ok) { errnotassign(c, c.fnret, src, "return"); };
// #5/#60: reject returning an array payload into a tagged variant. cerr
// alone does NOT fail the build — bump c.errs (errnotassign:4245 idiom).
if (taggedarrayvariantctor(c, c.fnret, src)) {
cerr("array-typed tagged-union variant construction unwired — reject (task #5 / #60)\n");
c.errs += 1;
return;
};
// #258: `return arr` borrows the array as a full slice.
// #31/#33: bare array-literal has no backing — loud-reject
// (supported only at a `let`).