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.
This commit is contained in:
parent
6f609029c7
commit
6b161257f9
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
73
gcc/cp/pt.c
73
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<int> 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<constructor_elt, va_gc> *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;
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
// Pack expansion testcases from P2082R1
|
||||
// { dg-do compile { target c++20 } }
|
||||
|
||||
template<typename U, typename... T>
|
||||
struct C2 : T... {
|
||||
U a;
|
||||
static constexpr int len = sizeof...(T);
|
||||
};
|
||||
C2 c2 = {
|
||||
[]{ return 1; },
|
||||
};
|
||||
static_assert (c2.len == 0);
|
||||
|
||||
template <typename... T>
|
||||
struct Types {};
|
||||
template <typename... T>
|
||||
struct F : Types<T...>, T... {};
|
||||
struct X {};
|
||||
struct Y {};
|
||||
struct Z {};
|
||||
struct W { operator Y(); };
|
||||
F f1 = {Types<X, Y, Z>{}, {}, {}}; // OK, F<X, Y, Z> deduced
|
||||
F f2 = {Types<X, Y, Z>{}, X{}, Y{}}; // OK, F<X, Y, Z> deduced
|
||||
F f3 = {Types<X, Y, Z>{}, X{}, W{}}; // { dg-error "" } conflicting types deduced; operator Y not considered
|
|
@ -0,0 +1,29 @@
|
|||
// Other testcases from P2082R1
|
||||
// { dg-do compile { target c++20 } }
|
||||
|
||||
template <typename T>
|
||||
struct X {};
|
||||
int main() {
|
||||
X<int> x1;
|
||||
X x2 {x1};
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
struct A {
|
||||
T array[N];
|
||||
};
|
||||
A a1 = {{1, 2, 3}}; // should deduce A<int, 3>
|
||||
A a2 = {"meow"}; // should deduce A<const char, 5>
|
||||
|
||||
template <typename T>
|
||||
struct B {
|
||||
T array[2];
|
||||
};
|
||||
B b = {0, 1};
|
||||
|
||||
template<typename... T>
|
||||
struct C : T... {};
|
||||
C c = {
|
||||
[]{ return 1; },
|
||||
[]{ return 2; }
|
||||
};
|
Loading…
Reference in New Issue