Commit 9b41ebbc by Jason Merrill Committed by Jason Merrill

Implement P1816R0, class template argument deduction for aggregates.

Rather than reimplement brace elision here, we call reshape_init and then
discard the result.  We needed to set CLASSTYPE_NON_AGGREGATE a bit more in
this patch, since outside a template it's set in check_bases_and_members.

	* pt.c (maybe_aggr_guide, collect_ctor_idx_types): New.
	(is_spec_or_derived): Split out from do_class_deduction.
	(build_deduction_guide): Handle aggregate guide.
	* class.c (finish_struct): Set CLASSTYPE_NON_AGGREGATE in a
	template.
	* cp-tree.h (CP_AGGREGATE_TYPE_P): An incomplete class is not an
	aggregate.

From-SVN: r278298
parent f6e20012
2019-11-14 Jason Merrill <jason@redhat.com>
Implement P1816R0, class template argument deduction for aggregates.
* pt.c (maybe_aggr_guide, collect_ctor_idx_types): New.
(is_spec_or_derived): Split out from do_class_deduction.
(build_deduction_guide): Handle aggregate guide.
* class.c (finish_struct): Set CLASSTYPE_NON_AGGREGATE in a
template.
* cp-tree.h (CP_AGGREGATE_TYPE_P): An incomplete class is not an
aggregate.
2019-11-14 Richard Sandiford <richard.sandiford@arm.com>
* call.c (build_conditional_expr_1): Use truth_type_for instead
......
......@@ -7349,7 +7349,16 @@ finish_struct (tree t, tree attributes)
add_method (t, *iter, true);
}
else if (DECL_DECLARES_FUNCTION_P (x))
DECL_IN_AGGR_P (x) = false;
{
DECL_IN_AGGR_P (x) = false;
if (DECL_VIRTUAL_P (x))
CLASSTYPE_NON_AGGREGATE (t) = true;
}
else if (TREE_CODE (x) == FIELD_DECL)
{
if (TREE_PROTECTED (x) || TREE_PRIVATE (x))
CLASSTYPE_NON_AGGREGATE (t) = true;
}
/* Also add a USING_DECL for operator=. We know there'll be (at
least) one, but we don't know the signature(s). We want name
......@@ -7387,6 +7396,9 @@ finish_struct (tree t, tree attributes)
/* Remember current #pragma pack value. */
TYPE_PRECISION (t) = maximum_field_alignment;
if (TYPE_HAS_USER_CONSTRUCTOR (t))
CLASSTYPE_NON_AGGREGATE (t) = 1;
/* Fix up any variants we've already built. */
for (x = TYPE_NEXT_VARIANT (t); x; x = TYPE_NEXT_VARIANT (x))
{
......
......@@ -4251,7 +4251,7 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
#define CP_AGGREGATE_TYPE_P(TYPE) \
(TREE_CODE (TYPE) == VECTOR_TYPE \
|| TREE_CODE (TYPE) == ARRAY_TYPE \
|| (CLASS_TYPE_P (TYPE) && !CLASSTYPE_NON_AGGREGATE (TYPE)))
|| (CLASS_TYPE_P (TYPE) && COMPLETE_TYPE_P (TYPE) && !CLASSTYPE_NON_AGGREGATE (TYPE)))
/* Nonzero for a class type means that the class type has a
user-declared constructor. */
......
......@@ -27724,28 +27724,39 @@ rewrite_template_parm (tree olddecl, unsigned index, unsigned level,
/* Returns a C++17 class deduction guide template based on the constructor
CTOR. As a special case, CTOR can be a RECORD_TYPE for an implicit default
guide, or REFERENCE_TYPE for an implicit copy/move guide. */
guide, REFERENCE_TYPE for an implicit copy/move guide, or TREE_LIST for an
aggregate initialization guide. */
static tree
build_deduction_guide (tree ctor, tree outer_args, tsubst_flags_t complain)
build_deduction_guide (tree type, tree ctor, tree outer_args, tsubst_flags_t complain)
{
tree type, tparms, targs, fparms, fargs, ci;
tree tparms, targs, fparms, fargs, ci;
bool memtmpl = false;
bool explicit_p;
location_t loc;
tree fn_tmpl = NULL_TREE;
if (TYPE_P (ctor))
if (outer_args)
{
type = ctor;
bool copy_p = TYPE_REF_P (type);
if (copy_p)
++processing_template_decl;
type = tsubst (type, outer_args, complain, CLASSTYPE_TI_TEMPLATE (type));
--processing_template_decl;
}
if (!DECL_DECLARES_FUNCTION_P (ctor))
{
if (TYPE_P (ctor))
{
type = TREE_TYPE (type);
fparms = tree_cons (NULL_TREE, type, void_list_node);
bool copy_p = TYPE_REF_P (ctor);
if (copy_p)
fparms = tree_cons (NULL_TREE, type, void_list_node);
else
fparms = void_list_node;
}
else if (TREE_CODE (ctor) == TREE_LIST)
fparms = ctor;
else
fparms = void_list_node;
gcc_unreachable ();
tree ctmpl = CLASSTYPE_TI_TEMPLATE (type);
tparms = DECL_TEMPLATE_PARMS (ctmpl);
......@@ -27767,8 +27778,6 @@ build_deduction_guide (tree ctor, tree outer_args, tsubst_flags_t complain)
fn_tmpl = tsubst (fn_tmpl, outer_args, complain, ctor);
ctor = DECL_TEMPLATE_RESULT (fn_tmpl);
type = DECL_CONTEXT (ctor);
tparms = DECL_TEMPLATE_PARMS (fn_tmpl);
/* If type is a member class template, DECL_TI_ARGS (ctor) will have
fully specialized args for the enclosing class. Strip those off, as
......@@ -27889,6 +27898,103 @@ build_deduction_guide (tree ctor, tree outer_args, tsubst_flags_t complain)
return ded_tmpl;
}
/* Add to LIST the member types for the reshaped initializer CTOR. */
static tree
collect_ctor_idx_types (tree ctor, tree list)
{
vec<constructor_elt, va_gc> *v = CONSTRUCTOR_ELTS (ctor);
tree idx, val; unsigned i;
FOR_EACH_CONSTRUCTOR_ELT (v, i, idx, val)
{
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);
}
return list;
}
/* Return a C++20 aggregate deduction candidate for TYPE initialized from
INIT. */
static tree
maybe_aggr_guide (tree type, tree init)
{
if (cxx_dialect < cxx2a)
return NULL_TREE;
if (init == NULL_TREE)
return NULL_TREE;
if (!CP_AGGREGATE_TYPE_P (type))
return NULL_TREE;
/* If we encounter a problem, we just won't add the candidate. */
tsubst_flags_t complain = tf_none;
tree parms = NULL_TREE;
if (TREE_CODE (init) == CONSTRUCTOR)
{
init = reshape_init (type, init, complain);
if (init == error_mark_node)
return NULL_TREE;
parms = collect_ctor_idx_types (init, parms);
}
else if (TREE_CODE (init) == TREE_LIST)
{
int len = list_length (init);
for (tree field = TYPE_FIELDS (type);
len;
--len, field = DECL_CHAIN (field))
{
field = next_initializable_field (field);
if (!field)
return NULL_TREE;
tree ftype = finish_decltype_type (field, true, complain);
parms = tree_cons (NULL_TREE, ftype, parms);
}
}
else
/* Aggregate initialization doesn't apply to an initializer expression. */
return NULL_TREE;
if (parms)
{
tree last = parms;
parms = nreverse (parms);
TREE_CHAIN (last) = void_list_node;
tree guide = build_deduction_guide (type, parms, NULL_TREE, complain);
return guide;
}
return NULL_TREE;
}
/* Return whether ETYPE is, or is derived from, a specialization of TMPL. */
static bool
is_spec_or_derived (tree etype, tree tmpl)
{
if (!etype || !CLASS_TYPE_P (etype))
return false;
tree type = TREE_TYPE (tmpl);
tree tparms = (INNERMOST_TEMPLATE_PARMS
(DECL_TEMPLATE_PARMS (tmpl)));
tree targs = make_tree_vec (TREE_VEC_LENGTH (tparms));
int err = unify (tparms, targs, type, etype,
UNIFY_ALLOW_DERIVED, /*explain*/false);
ggc_free (targs);
return !err;
}
/* Deduce template arguments for the class template placeholder PTYPE for
template TMPL based on the initializer INIT, and return the resulting
type. */
......@@ -27913,16 +28019,15 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags,
tree type = TREE_TYPE (tmpl);
bool try_list_ctor = false;
bool copy_init = false;
releasing_vec rv_args = NULL;
vec<tree,va_gc> *&args = *&rv_args;
if (init == NULL_TREE
|| TREE_CODE (init) == TREE_LIST)
args = make_tree_vector_from_list (init);
if (init == NULL_TREE)
args = make_tree_vector ();
else if (BRACE_ENCLOSED_INITIALIZER_P (init))
{
try_list_ctor = TYPE_HAS_LIST_CTOR (type);
if (try_list_ctor && CONSTRUCTOR_NELTS (init) == 1)
if (CONSTRUCTOR_NELTS (init) == 1)
{
/* As an exception, the first phase in 16.3.1.7 (considering the
initializer list as a single argument) is omitted if the
......@@ -27930,26 +28035,24 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags,
where U is a specialization of C or a class derived from a
specialization of C. */
tree elt = CONSTRUCTOR_ELT (init, 0)->value;
if (!BRACE_ENCLOSED_INITIALIZER_P (elt))
{
tree etype = TREE_TYPE (elt);
tree tparms = (INNERMOST_TEMPLATE_PARMS
(DECL_TEMPLATE_PARMS (tmpl)));
tree targs = make_tree_vec (TREE_VEC_LENGTH (tparms));
int err = unify (tparms, targs, type, etype,
UNIFY_ALLOW_DERIVED, /*explain*/false);
if (err == 0)
try_list_ctor = false;
ggc_free (targs);
}
copy_init = is_spec_or_derived (TREE_TYPE (elt), tmpl);
}
try_list_ctor = !copy_init && TYPE_HAS_LIST_CTOR (type);
if (try_list_ctor || is_std_init_list (type))
args = make_tree_vector_single (init);
else
args = make_tree_vector_from_ctor (init);
}
else
args = make_tree_vector_single (init);
{
if (TREE_CODE (init) == TREE_LIST)
args = make_tree_vector_from_list (init);
else
args = make_tree_vector_single (init);
if (args->length() == 1)
copy_init = is_spec_or_derived (TREE_TYPE ((*args)[0]), tmpl);
}
tree dname = dguide_name (tmpl);
tree cands = lookup_qualified_name (CP_DECL_CONTEXT (tmpl), dname,
......@@ -27994,7 +28097,7 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags,
if (iter.using_p ())
continue;
tree guide = build_deduction_guide (*iter, outer_args, complain);
tree guide = build_deduction_guide (type, *iter, outer_args, complain);
if (guide == error_mark_node)
return error_mark_node;
if ((flags & LOOKUP_ONLYCONVERTING)
......@@ -28006,6 +28109,10 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags,
saw_ctor = true;
}
if (!copy_init)
if (tree guide = maybe_aggr_guide (type, init))
cands = lookup_add (guide, cands);
tree call = error_mark_node;
/* If this is list-initialization and the class has a list constructor, first
......@@ -28047,7 +28154,8 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags,
if (gtype)
{
tree guide = build_deduction_guide (gtype, outer_args, complain);
tree guide = build_deduction_guide (type, gtype, outer_args,
complain);
if (guide == error_mark_node)
return error_mark_node;
cands = lookup_add (guide, cands);
......
......@@ -7,4 +7,4 @@ struct array
int a [N];
};
array a = { 1, 2, 3 }; // { dg-error "cannot deduce" }
array a = { 1, 2, 3 }; // { dg-error "" }
// Testcase from P1816R0
// { dg-do compile { target c++2a } }
template <typename T>
struct S {
T x;
T y;
};
S s = { 1, 2 };
template <typename T>
struct C {
S<T> s;
T t;
};
template <typename T>
struct D {
S<int> s;
T t;
};
C c1 = {1, 2}; // { dg-error "" "deduction failed" }
C c2 = {1, 2, 3}; // { dg-error "" "deduction failed" }
C c3 = {{1u, 2u}, 3}; // { dg-bogus "" "OK, C<int> deduced" }
D d1 = {1, 2}; // { dg-error "" "deduction failed" }
D d2 = {1, 2, 3}; // { dg-bogus "" "OK, braces elided, D<int> deduced" }
template <typename T>
struct I {
using type = T;
};
template <typename T>
struct E {
typename I<T>::type i;
T t;
};
E e1 = {1, 2}; // { dg-bogus "" "OK, E<int> deduced" }
// Test that non-aggregates don't get the aggregate deduction.
// { dg-do compile { target c++2a } }
// { dg-prune-output "no matching function" }
struct A { A(); };
template <typename T>
struct S1 {
T x;
};
S1 s1 = {1}; // OK
template <typename T>
struct S2 {
S2 ();
T x;
};
S2 s2 = {1}; // { dg-error "deduction failed" }
template <typename T>
struct S3 {
private:
T x;
};
S3 s3 = {1}; // { dg-error "deduction failed" }
template <typename T>
struct S4 {
virtual void f();
T x;
};
S4 s4 = {1}; // { dg-error "deduction failed" }
template <typename T>
struct S5: public A {
using A::A;
T x;
};
S5 s5 = {1}; // { dg-error "deduction failed" }
template <typename T>
struct S6: virtual A {
T x;
};
S6 s6 = {1}; // { dg-error "deduction failed" }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment