This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[C++ 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.

Tested x86_64-pc-linux-gnu, applying to trunk.

	* 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.
---
 gcc/cp/cp-tree.h                              |   2 +-
 gcc/cp/class.c                                |  14 +-
 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 ++++++
 6 files changed, 243 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/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/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/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<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);
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 <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
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 <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" }
+

base-commit: 555283de0b688b79240719e4bd78b18e8fbc99f3
-- 
2.18.1


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]