From 9b41ebbcdf9e33285a0eebeb7c841afe20e4a7c1 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Fri, 15 Nov 2019 09:51:05 -0500 Subject: [PATCH] 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 --- gcc/cp/ChangeLog | 11 ++ gcc/cp/class.c | 14 +- gcc/cp/cp-tree.h | 2 +- gcc/cp/pt.c | 172 ++++++++++++++---- .../g++.dg/cpp1z/class-deduction43.C | 2 +- .../g++.dg/cpp2a/class-deduction-aggr1.C | 36 ++++ .../g++.dg/cpp2a/class-deduction-aggr2.C | 52 ++++++ 7 files changed, 254 insertions(+), 35 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr1.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr2.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index f45b59b8061..0129731503b 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,14 @@ +2019-11-14 Jason Merrill + + 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 * call.c (build_conditional_expr_1): Use truth_type_for instead diff --git a/gcc/cp/class.c b/gcc/cp/class.c index a9aa5e77171..ef1d5136963 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -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)) { diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 42afe1bd5cb..56b75ca51f5 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -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. */ diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 84db3f9c663..8f7734a7a41 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -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 *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 *&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); diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction43.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction43.C index 55a79b327f4..2585eb6194a 100644 --- a/gcc/testsuite/g++.dg/cpp1z/class-deduction43.C +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction43.C @@ -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 "" } diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr1.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr1.C new file mode 100644 index 00000000000..61ba65a1333 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr1.C @@ -0,0 +1,36 @@ +// Testcase from P1816R0 +// { dg-do compile { target c++2a } } + +template +struct S { + T x; + T y; +}; + +S s = { 1, 2 }; + +template +struct C { + S s; + T t; +}; +template +struct D { + S 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 deduced" } +D d1 = {1, 2}; // { dg-error "" "deduction failed" } +D d2 = {1, 2, 3}; // { dg-bogus "" "OK, braces elided, D deduced" } +template +struct I { + using type = T; +}; +template +struct E { + typename I::type i; + T t; +}; +E e1 = {1, 2}; // { dg-bogus "" "OK, E deduced" } diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr2.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr2.C new file mode 100644 index 00000000000..896554f610a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr2.C @@ -0,0 +1,52 @@ +// 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 +struct S1 { + T x; +}; + +S1 s1 = {1}; // OK + +template +struct S2 { + S2 (); + T x; +}; + +S2 s2 = {1}; // { dg-error "deduction failed" } + +template +struct S3 { +private: + T x; +}; + +S3 s3 = {1}; // { dg-error "deduction failed" } + +template +struct S4 { + virtual void f(); + T x; +}; + +S4 s4 = {1}; // { dg-error "deduction failed" } + +template +struct S5: public A { + using A::A; + T x; +}; + +S5 s5 = {1}; // { dg-error "deduction failed" } + +template +struct S6: virtual A { + T x; +}; + +S6 s6 = {1}; // { dg-error "deduction failed" } + -- 2.30.2