From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 1888) id 576E1388B806; Wed, 18 Aug 2021 12:39:26 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 576E1388B806 MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Patrick Palka To: gcc-cvs@gcc.gnu.org Subject: [gcc r12-2991] c++: aggregate CTAD and brace elision [PR101344] X-Act-Checkin: gcc X-Git-Author: Patrick Palka X-Git-Refname: refs/heads/master X-Git-Oldrev: a6b3db3e8625a3cba1240f0b5e1a29bd6c68b8ca X-Git-Newrev: be4a4fb516688d7cfe28a80a4aa333f4ecf0b518 Message-Id: <20210818123926.576E1388B806@sourceware.org> Date: Wed, 18 Aug 2021 12:39:26 +0000 (GMT) X-BeenThere: gcc-cvs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 18 Aug 2021 12:39:26 -0000 https://gcc.gnu.org/g:be4a4fb516688d7cfe28a80a4aa333f4ecf0b518 commit r12-2991-gbe4a4fb516688d7cfe28a80a4aa333f4ecf0b518 Author: Patrick Palka Date: Wed Aug 18 08:37:45 2021 -0400 c++: aggregate CTAD and brace elision [PR101344] Here the problem is ultimately that collect_ctor_idx_types always recurses into an eligible sub-CONSTRUCTOR regardless of whether the corresponding pair of braces was elided in the original initializer. This causes us to reject some completely-braced forms of aggregate CTAD as in the first testcase below, because collect_ctor_idx_types effectively assumes that the original initializer is always minimally braced (and so the aggregate deduction candidate is given a function type that's incompatible with the original completely-braced initializer). In order to fix this, collect_ctor_idx_types needs to somehow know the shape of the original initializer when iterating over the reshaped initializer. To that end this patch makes reshape_init flag sub-ctors that were built to undo brace elision in the original ctor, so that collect_ctor_idx_types that determine whether to recurse into a sub-ctor by simply inspecting this flag. This happens to also fix PR101820, which is about aggregate CTAD using designated initializers, for much the same reasons. A curious case is the "intermediately-braced" initialization of 'e3' (which we reject) in the first testcase below. It seems to me we're behaving as specified here (according to [over.match.class.deduct]/1) because the initializer element x_1={1, 2, 3, 4} corresponds to the subobject e_1=E::t, hence the type T_1 of the first function parameter of the aggregate deduction candidate is T(&&)[2][2], but T can't be deduced from x_1 using this parameter type (as opposed to say T(&&)[4]). PR c++/101344 PR c++/101820 gcc/cp/ChangeLog: * cp-tree.h (CONSTRUCTOR_BRACES_ELIDED_P): Define. * decl.c (reshape_init_r): Set it. * pt.c (collect_ctor_idx_types): Recurse into a sub-CONSTRUCTOR iff CONSTRUCTOR_BRACES_ELIDED_P. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/class-deduction-aggr11.C: New test. * g++.dg/cpp2a/class-deduction-aggr12.C: New test. Diff: --- gcc/cp/cp-tree.h | 6 +++++ gcc/cp/decl.c | 18 +++++++++++--- gcc/cp/pt.c | 7 +----- .../g++.dg/cpp2a/class-deduction-aggr11.C | 29 ++++++++++++++++++++++ .../g++.dg/cpp2a/class-deduction-aggr12.C | 15 +++++++++++ 5 files changed, 65 insertions(+), 10 deletions(-) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 14e2db26f77..7ba02bed0fb 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -4503,6 +4503,12 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter) #define CONSTRUCTOR_IS_PAREN_INIT(NODE) \ (CONSTRUCTOR_CHECK(NODE)->base.private_flag) +/* True if reshape_init built this CONSTRUCTOR to undo the brace elision + of another CONSTRUCTOR. This flag is used during C++20 aggregate + CTAD. */ +#define CONSTRUCTOR_BRACES_ELIDED_P(NODE) \ + (CONSTRUCTOR_CHECK (NODE)->base.protected_flag) + /* True if NODE represents a conversion for direct-initialization in a template. Set by perform_implicit_conversion_flags. */ #define IMPLICIT_CONV_EXPR_DIRECT_INIT(NODE) \ diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 32d07babf43..3414cbdc876 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -6657,7 +6657,8 @@ reshape_init_r (tree type, reshape_iter *d, tree first_initializer_p, /* A non-aggregate type is always initialized with a single initializer. */ if (!CP_AGGREGATE_TYPE_P (type) - /* As is an array with dependent bound. */ + /* As is an array with dependent bound, which we can see + during C++20 aggregate CTAD. */ || (cxx_dialect >= cxx20 && TREE_CODE (type) == ARRAY_TYPE && uses_template_parms (TYPE_DOMAIN (type)))) @@ -6774,6 +6775,7 @@ reshape_init_r (tree type, reshape_iter *d, tree first_initializer_p, initializer already, and there is not a CONSTRUCTOR, it means that there is a missing set of braces (that is, we are processing the case for which reshape_init exists). */ + bool braces_elided_p = false; if (!first_initializer_p) { if (TREE_CODE (stripped_init) == CONSTRUCTOR) @@ -6809,17 +6811,25 @@ reshape_init_r (tree type, reshape_iter *d, tree first_initializer_p, warning (OPT_Wmissing_braces, "missing braces around initializer for %qT", type); + braces_elided_p = true; } /* Dispatch to specialized routines. */ + tree new_init; if (CLASS_TYPE_P (type)) - return reshape_init_class (type, d, first_initializer_p, complain); + new_init = reshape_init_class (type, d, first_initializer_p, complain); else if (TREE_CODE (type) == ARRAY_TYPE) - return reshape_init_array (type, d, first_initializer_p, complain); + new_init = reshape_init_array (type, d, first_initializer_p, complain); else if (VECTOR_TYPE_P (type)) - return reshape_init_vector (type, d, complain); + new_init = reshape_init_vector (type, d, complain); else gcc_unreachable(); + + if (braces_elided_p + && TREE_CODE (new_init) == CONSTRUCTOR) + CONSTRUCTOR_BRACES_ELIDED_P (new_init) = true; + + return new_init; } /* Undo the brace-elision allowed by [dcl.init.aggr] in a diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 0c1496693e2..020a4bf2f6d 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -28837,12 +28837,7 @@ collect_ctor_idx_types (tree ctor, tree list, tree elt = NULL_TREE) { tree ftype = elt ? elt : TREE_TYPE (idx); if (BRACE_ENCLOSED_INITIALIZER_P (val) - && CONSTRUCTOR_NELTS (val) - /* As in reshape_init_r, a non-aggregate or array-of-dependent-bound - type gets a single initializer. */ - && CP_AGGREGATE_TYPE_P (ftype) - && !(TREE_CODE (ftype) == ARRAY_TYPE - && uses_template_parms (TYPE_DOMAIN (ftype)))) + && CONSTRUCTOR_BRACES_ELIDED_P (val)) { tree subelt = NULL_TREE; if (TREE_CODE (ftype) == ARRAY_TYPE) diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr11.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr11.C new file mode 100644 index 00000000000..c4806de56af --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr11.C @@ -0,0 +1,29 @@ +// PR c++/101344 +// { dg-do compile { target c++20 } } + +template +struct A { int m; int t[2]; }; + +A a1{1, {2, 3}}; // previously rejected +A a2{1, 2, 3}; + +struct B { int x, y; }; + +template +struct C { int m; struct { int x, y; } t; }; + +A b1{1, {2, 3}}; // previously rejected +A b2{1, 2, 3}; + +template +struct D { T t[2]; }; + +D d1{1, 2}; +D d2{{1, 2}}; // previously rejected + +template +struct E { T t[2][2]; }; + +E e1{1, 2, 3, 4}; +E e2{{{1, 2}, {3, 4}}}; // previously rejected +E e3{{1, 2, 3, 4}}; // { dg-error "deduction|no match" } diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr12.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr12.C new file mode 100644 index 00000000000..ebe73c1d817 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr12.C @@ -0,0 +1,15 @@ +// PR c++/101820 +// { dg-do compile { target c++20 } } + +struct Inner { int i = 0; }; + +template +struct Outer { Inner s{}; }; + +Outer o1{ .s = {} }; // works +Outer o2{ .s = Inner{ .i = 1} }; // works +Outer o3{ .s = { .i = 1} }; // does not + +Outer o4{ .s{} }; // works +Outer o5{ .s{Inner{ .i = 1} } }; // works +Outer o6{ .s{ .i = 1} }; // does not