Implement P1816R0, class template argument deduction for aggregates.
authorJason Merrill <jason@redhat.com>
Fri, 15 Nov 2019 14:51:05 +0000 (09:51 -0500)
committerJason Merrill <jason@gcc.gnu.org>
Fri, 15 Nov 2019 14:51:05 +0000 (09:51 -0500)
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
gcc/cp/class.c
gcc/cp/cp-tree.h
gcc/cp/pt.c
gcc/testsuite/g++.dg/cpp1z/class-deduction43.C
gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr2.C [new file with mode: 0644]

index f45b59b..0129731 100644 (file)
@@ -1,3 +1,14 @@
+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
index a9aa5e7..ef1d513 100644 (file)
@@ -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))
        {
index 42afe1b..56b75ca 100644 (file)
@@ -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.  */
index 84db3f9..8f7734a 100644 (file)
@@ -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);
index 55a79b3..2585eb6 100644 (file)
@@ -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 (file)
index 0000000..61ba65a
--- /dev/null
@@ -0,0 +1,36 @@
+// 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" }
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 (file)
index 0000000..896554f
--- /dev/null
@@ -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 <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" }
+