diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index daf43f20..482057a8 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -15123,6 +15123,19 @@ fn checkretassign(c: *checker, n: *node) void = { if (n.lhs.kind == nkind.N_ARRLIT) { checkarrlitfits(c, c.fnret, n.lhs); }; + // #25: array-typed element of a TUPLE RETURN with an overlong array + // literal — `fn f() ([2]int,i32) = { return ([1,2,3],5); }`. #20 + // wired the tuple over-fill walk at the LET position only; the + // return path runs through checkretassign which never routed tuple + // elements through checkarrlitfits. Reuse the #26 helper. Fire + // BEFORE the isassignable/conf guards (a tuple return drives + // conf=false → short-circuit), same reason as the #12 array arm + // above. Alias/!-aware on the fnret tuple type. + let rrt: *node = resolvealias(c, unwrapbang(c.fnret)); + if (rrt != nil && rrt.kind == nkind.N_TTUPLE + && n.lhs.kind == nkind.N_TUPLE) { + checktuplearrfits(c, rrt, n.lhs); + }; let src: *node = exprtype(c, n.lhs, nil); if (src == nil) { return; }; let conf: bool = false; diff --git a/selfhost/cmd/wcc/check.ww b/selfhost/cmd/wcc/check.ww index 07a06efa..ecc792ae 100644 --- a/selfhost/cmd/wcc/check.ww +++ b/selfhost/cmd/wcc/check.ww @@ -4842,6 +4842,19 @@ fn checkretassign(c: *checker, n: *node) void = { if (n.lhs.kind == nkind.N_ARRLIT) { checkarrlitfits(c, c.fnret, n.lhs); }; + // #25: array-typed element of a TUPLE RETURN with an overlong array + // literal — `fn f() ([2]int,i32) = { return ([1,2,3],5); }`. #20 + // wired the tuple over-fill walk at the LET position only; the + // return path runs through checkretassign which never routed tuple + // elements through checkarrlitfits. Reuse the #26 helper. Fire + // BEFORE the isassignable/conf guards (a tuple return drives + // conf=false → short-circuit), same reason as the #12 array arm + // above. Alias/!-aware on the fnret tuple type. + let rrt: *node = resolvealias(c, unwrapbang(c.fnret)); + if (rrt != nil && rrt.kind == nkind.N_TTUPLE + && n.lhs.kind == nkind.N_TUPLE) { + checktuplearrfits(c, rrt, n.lhs); + }; let src: *node = exprtype(c, n.lhs, nil); if (src == nil) { return; }; let conf: bool = false; diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 866e94a4..0cad6de7 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -15123,6 +15123,19 @@ fn checkretassign(c: *checker, n: *node) void = { if (n.lhs.kind == nkind.N_ARRLIT) { checkarrlitfits(c, c.fnret, n.lhs); }; + // #25: array-typed element of a TUPLE RETURN with an overlong array + // literal — `fn f() ([2]int,i32) = { return ([1,2,3],5); }`. #20 + // wired the tuple over-fill walk at the LET position only; the + // return path runs through checkretassign which never routed tuple + // elements through checkarrlitfits. Reuse the #26 helper. Fire + // BEFORE the isassignable/conf guards (a tuple return drives + // conf=false → short-circuit), same reason as the #12 array arm + // above. Alias/!-aware on the fnret tuple type. + let rrt: *node = resolvealias(c, unwrapbang(c.fnret)); + if (rrt != nil && rrt.kind == nkind.N_TTUPLE + && n.lhs.kind == nkind.N_TUPLE) { + checktuplearrfits(c, rrt, n.lhs); + }; let src: *node = exprtype(c, n.lhs, nil); if (src == nil) { return; }; let conf: bool = false; diff --git a/test/wcc/832_tuple_elem_overlong.c b/test/wcc/832_tuple_elem_overlong.c index ed7e4376..ab327477 100644 --- a/test/wcc/832_tuple_elem_overlong.c +++ b/test/wcc/832_tuple_elem_overlong.c @@ -25,6 +25,8 @@ * tuple_arr_over | let t:([2]int,i32)=([1,2,3],5) | b. FAIL * tuple_nested_arr | let t:([2][3]int,i32)=([[..],[..],[..]],5) | b. FAIL * tuple_in_tuple | let t:([2]int,([2]int,i32))=([..],([1,2,3],.))| #26 FAIL + * tuple_return_over | fn()([2]int,i32){return([1,2,3],5)} | #25 FAIL + * tuple_return_nested| fn()([2]int,([2]int,i32)){return(..,([..3],.))| #25 FAIL * * pos row | shape | want * -------------------+----------------------------------------------+------ @@ -116,6 +118,25 @@ static const char *neg[] = { "\tlet t: ([2]int, ([2]int, i32)) = ([1, 2], ([3, 4, 5], 6));\n" "\treturn t.0[0]: i32;\n" "};\n", + /* tuple_return_over (#25) — overlong [2]int in a tuple RETURN. */ + "package main;\n" + "fn f() ([2]int, i32) = {\n" + "\treturn ([1, 2, 3], 5);\n" + "};\n" + "export fn main() i32 = {\n" + "\tlet t = f();\n" + "\treturn t.1;\n" + "};\n", + /* tuple_return_nested (#25 path × #26 recursion) — nested tuple in a + * RETURN, inner [2]int over-filled. */ + "package main;\n" + "fn f() ([2]int, ([2]int, i32)) = {\n" + "\treturn ([1, 2], ([3, 4, 5], 6));\n" + "};\n" + "export fn main() i32 = {\n" + "\tlet t = f();\n" + "\treturn t.0[0]: i32;\n" + "};\n", }; static int