From: Jason Merrill Date: Mon, 22 Jun 2020 19:44:45 +0000 (-0400) Subject: c++: Improve CTAD for aggregates [PR93976] X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=6b161257f9f8c7a26b7d119ebc32cbbc54d2e508;p=gcc.git c++: Improve CTAD for aggregates [PR93976] P2082R1 adjusted the rules for class template argument deduction for an aggregate to better handle arrays and pack expansions. gcc/cp/ChangeLog: PR c++/93976 Implement C++20 P2082R1, Fixing CTAD for aggregates. * cp-tree.h (TPARMS_PRIMARY_TEMPLATE): Split out from... (DECL_PRIMARY_TEMPLATE): ...here. (builtin_guide_p): Declare. * decl.c (reshape_init_class): Handle bases of a template. (reshape_init_r): An array with dependent bound takes a single initializer. * pt.c (tsubst_default_argument): Shortcut {}. (unify_pack_expansion): Allow omitted arguments to trailing pack. (builtin_guide_p): New. (collect_ctor_idx_types): Give a trailing pack a {} default argument. Handle arrays better. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/class-deduction-aggr3.C: New test. * g++.dg/cpp2a/class-deduction-aggr4.C: New test. --- diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index c1396686968..78e8ca4150a 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -4815,8 +4815,10 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter) templates are primary, too. */ /* Returns the primary template corresponding to these parameters. */ +#define TPARMS_PRIMARY_TEMPLATE(NODE) (TREE_TYPE (NODE)) + #define DECL_PRIMARY_TEMPLATE(NODE) \ - (TREE_TYPE (DECL_INNERMOST_TEMPLATE_PARMS (NODE))) + (TPARMS_PRIMARY_TEMPLATE (DECL_INNERMOST_TEMPLATE_PARMS (NODE))) /* Returns nonzero if NODE is a primary template. */ #define PRIMARY_TEMPLATE_P(NODE) (DECL_PRIMARY_TEMPLATE (NODE) == (NODE)) @@ -7024,6 +7026,7 @@ extern bool dguide_name_p (tree); extern bool deduction_guide_p (const_tree); extern bool copy_guide_p (const_tree); extern bool template_guide_p (const_tree); +extern bool builtin_guide_p (const_tree); extern void store_explicit_specifier (tree, tree); extern tree add_outermost_template_args (tree, tree); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 1d960be1ee6..3afad5ca805 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -6153,7 +6153,22 @@ reshape_init_class (tree type, reshape_iter *d, bool first_initializer_p, /* The initializer for a class is always a CONSTRUCTOR. */ new_init = build_constructor (init_list_type_node, NULL); - field = next_initializable_field (TYPE_FIELDS (type)); + + int binfo_idx = -1; + tree binfo = TYPE_BINFO (type); + tree base_binfo = NULL_TREE; + if (cxx_dialect >= cxx17 && uses_template_parms (type)) + { + /* We get here from maybe_aggr_guide for C++20 class template argument + deduction. In this case we need to look through the binfo because a + template doesn't have base fields. */ + binfo_idx = 0; + BINFO_BASE_ITERATE (binfo, binfo_idx, base_binfo); + } + if (base_binfo) + field = base_binfo; + else + field = next_initializable_field (TYPE_FIELDS (type)); if (!field) { @@ -6171,6 +6186,9 @@ reshape_init_class (tree type, reshape_iter *d, bool first_initializer_p, return new_init; } + /* For C++20 CTAD, handle pack expansions in the base list. */ + tree last_was_pack_expansion = NULL_TREE; + /* Loop through the initializable fields, gathering initializers. */ while (d->cur != d->end) { @@ -6218,6 +6236,13 @@ reshape_init_class (tree type, reshape_iter *d, bool first_initializer_p, if (!field) break; + last_was_pack_expansion = (PACK_EXPANSION_P (TREE_TYPE (field)) + ? field : NULL_TREE); + if (last_was_pack_expansion) + /* Each non-trailing aggregate element that is a pack expansion is + assumed to correspond to no elements of the initializer list. */ + goto continue_; + field_init = reshape_init_r (TREE_TYPE (field), d, /*first_initializer_p=*/NULL_TREE, complain); @@ -6243,7 +6268,27 @@ reshape_init_class (tree type, reshape_iter *d, bool first_initializer_p, if (TREE_CODE (type) == UNION_TYPE) break; - field = next_initializable_field (DECL_CHAIN (field)); + continue_: + if (base_binfo) + { + BINFO_BASE_ITERATE (binfo, ++binfo_idx, base_binfo); + if (base_binfo) + field = base_binfo; + else + field = next_initializable_field (TYPE_FIELDS (type)); + } + else + field = next_initializable_field (DECL_CHAIN (field)); + } + + /* A trailing aggregate element that is a pack expansion is assumed to + correspond to all remaining elements of the initializer list (if any). */ + if (last_was_pack_expansion) + { + CONSTRUCTOR_APPEND_ELT (CONSTRUCTOR_ELTS (new_init), + last_was_pack_expansion, d->cur->value); + while (d->cur != d->end) + d->cur++; } return new_init; @@ -6319,7 +6364,11 @@ 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)) + if (!CP_AGGREGATE_TYPE_P (type) + /* As is an array with dependent bound. */ + || (cxx_dialect >= cxx20 + && TREE_CODE (type) == ARRAY_TYPE + && uses_template_parms (TYPE_DOMAIN (type)))) { /* It is invalid to initialize a non-aggregate type with a brace-enclosed initializer before C++0x. diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index efaadf73772..53a64c3a15e 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -13406,6 +13406,11 @@ tsubst_default_argument (tree fn, int parmnum, tree type, tree arg, if (TREE_CODE (arg) == DEFERRED_PARSE) return arg; + /* Shortcut {}. */ + if (BRACE_ENCLOSED_INITIALIZER_P (arg) + && CONSTRUCTOR_NELTS (arg) == 0) + return arg; + tree parm = FUNCTION_FIRST_USER_PARM (fn); parm = chain_index (parmnum, parm); tree parmtype = TREE_TYPE (parm); @@ -22769,7 +22774,15 @@ unify_pack_expansion (tree tparms, tree targs, tree packed_parms, { tree bad_old_arg = NULL_TREE, bad_new_arg = NULL_TREE; tree old_args = ARGUMENT_PACK_ARGS (old_pack); - + temp_override ovl (TREE_VEC_LENGTH (old_args)); + /* During template argument deduction for the aggregate deduction + candidate, the number of elements in a trailing parameter pack + is only deduced from the number of remaining function + arguments if it is not otherwise deduced. */ + if (cxx_dialect >= cxx20 + && TREE_VEC_LENGTH (new_args) < TREE_VEC_LENGTH (old_args) + && builtin_guide_p (TPARMS_PRIMARY_TEMPLATE (tparms))) + TREE_VEC_LENGTH (old_args) = TREE_VEC_LENGTH (new_args); if (!comp_template_args (old_args, new_args, &bad_old_arg, &bad_new_arg)) /* Inconsistent unification of this parameter pack. */ @@ -27982,6 +27995,23 @@ template_guide_p (const_tree fn) return false; } +/* True if FN is an aggregate initialization guide or the copy deduction + guide. */ + +bool +builtin_guide_p (const_tree fn) +{ + if (!deduction_guide_p (fn)) + return false; + if (!DECL_ARTIFICIAL (fn)) + /* Explicitly declared. */ + return false; + if (DECL_ABSTRACT_ORIGIN (fn)) + /* Derived from a constructor. */ + return false; + return true; +} + /* OLDDECL is a _DECL for a template parameter. Return a similar parameter at LEVEL:INDEX, using tsubst_args and complain for substitution into non-type template parameter types. Note that the handling of template template @@ -28293,22 +28323,43 @@ build_deduction_guide (tree type, tree ctor, tree outer_args, tsubst_flags_t com /* Add to LIST the member types for the reshaped initializer CTOR. */ static tree -collect_ctor_idx_types (tree ctor, tree list) +collect_ctor_idx_types (tree ctor, tree list, tree elt = NULL_TREE) { vec *v = CONSTRUCTOR_ELTS (ctor); tree idx, val; unsigned i; FOR_EACH_CONSTRUCTOR_ELT (v, i, idx, val) { + tree ftype = elt ? elt : finish_decltype_type (idx, true, tf_none); if (BRACE_ENCLOSED_INITIALIZER_P (val) - && CONSTRUCTOR_NELTS (val)) - if (tree subidx = CONSTRUCTOR_ELT (val, 0)->index) - if (TREE_CODE (subidx) == FIELD_DECL) - { - list = collect_ctor_idx_types (val, list); - continue; - } - tree ftype = finish_decltype_type (idx, true, tf_none); - list = tree_cons (NULL_TREE, ftype, list); + && 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)))) + { + tree subelt = NULL_TREE; + if (TREE_CODE (ftype) == ARRAY_TYPE) + subelt = TREE_TYPE (ftype); + list = collect_ctor_idx_types (val, list, subelt); + continue; + } + tree arg = NULL_TREE; + if (i == v->length() - 1 + && PACK_EXPANSION_P (ftype)) + /* Give the trailing pack expansion parameter a default argument to + match aggregate initialization behavior, even if we deduce the + length of the pack separately to more than we have initializers. */ + arg = build_constructor (init_list_type_node, NULL); + /* if ei is of array type and xi is a braced-init-list or string literal, + Ti is an rvalue reference to the declared type of ei */ + STRIP_ANY_LOCATION_WRAPPER (val); + if (TREE_CODE (ftype) == ARRAY_TYPE + && (BRACE_ENCLOSED_INITIALIZER_P (val) + || TREE_CODE (val) == STRING_CST)) + ftype = (cp_build_reference_type + (ftype, BRACE_ENCLOSED_INITIALIZER_P (val))); + list = tree_cons (arg, ftype, list); } return list; diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr3.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr3.C new file mode 100644 index 00000000000..13d7ec9b57d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr3.C @@ -0,0 +1,24 @@ +// Pack expansion testcases from P2082R1 +// { dg-do compile { target c++20 } } + +template +struct C2 : T... { + U a; + static constexpr int len = sizeof...(T); +}; +C2 c2 = { + []{ return 1; }, +}; +static_assert (c2.len == 0); + +template +struct Types {}; +template +struct F : Types, T... {}; +struct X {}; +struct Y {}; +struct Z {}; +struct W { operator Y(); }; +F f1 = {Types{}, {}, {}}; // OK, F deduced +F f2 = {Types{}, X{}, Y{}}; // OK, F deduced +F f3 = {Types{}, X{}, W{}}; // { dg-error "" } conflicting types deduced; operator Y not considered diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr4.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr4.C new file mode 100644 index 00000000000..0debbb2443f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr4.C @@ -0,0 +1,29 @@ +// Other testcases from P2082R1 +// { dg-do compile { target c++20 } } + +template +struct X {}; +int main() { + X x1; + X x2 {x1}; +} + +template +struct A { + T array[N]; +}; +A a1 = {{1, 2, 3}}; // should deduce A +A a2 = {"meow"}; // should deduce A + +template +struct B { + T array[2]; +}; +B b = {0, 1}; + +template +struct C : T... {}; +C c = { + []{ return 1; }, + []{ return 2; } +};