diff mbox

C++ PATCH for C++17 class deduction issues US 19/20

Message ID CADzB+2n7A1ukK6DDPvXC4pSXgHocPK7bXLrNSzjkfz50qt-CMQ@mail.gmail.com
State New
Headers show

Commit Message

Jason Merrill Jan. 20, 2017, 4:41 a.m. UTC
wg21.link/p0512r0 proposes resolutions for some issues with class
template argument deduction raised in national body comments against
the C++17 draft; the paper hasn't been accepted yet, but the proposals
seem sensible, so I'm implementing them here.

The first makes deduction guides take priority over constructors, to
make deduction guides more useful.

The second clarifies the "forwarding reference" rules to not apply to
class template argument deduction; we don't want a constructor like
A(T&&) to lead to deduction of A<int&>.

Tested x86_64-pc-linux-gnu, applying to trunk.
commit d3b00e36dc65288897c39cbedb5b570fe937ae23
Author: Jason Merrill <jason@redhat.com>
Date:   Thu Jan 19 14:47:56 2017 -0500

            US 19 - deduction guides and constructors
    
            * call.c (joust): Prefer deduction guides to constructors.
            * pt.c (build_deduction_guide): Set DECL_ARTIFICIAL.
            (deduction_guide_p): Check DECL_P.
diff mbox

Patch

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 88d83dd..0059a39 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -9633,6 +9633,18 @@  joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn,
 	return winner;
     }
 
+  /* F1 is generated from a deduction-guide (13.3.1.8) and F2 is not */
+  if (deduction_guide_p (cand1->fn))
+    {
+      gcc_assert (deduction_guide_p (cand2->fn));
+      /* We distinguish between candidates from an explicit deduction guide and
+	 candidates built from a constructor based on DECL_ARTIFICIAL.  */
+      int art1 = DECL_ARTIFICIAL (cand1->fn);
+      int art2 = DECL_ARTIFICIAL (cand2->fn);
+      if (art1 != art2)
+	return art2 - art1;
+    }
+
   /* or, if not that,
      F1 is a non-template function and F2 is a template function
      specialization.  */
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index c679133..f683727 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -24776,8 +24776,9 @@  dguide_name_p (tree name)
 bool
 deduction_guide_p (tree fn)
 {
-  if (tree name = DECL_NAME (fn))
-    return dguide_name_p (name);
+  if (DECL_P (fn))
+    if (tree name = DECL_NAME (fn))
+      return dguide_name_p (name);
   return false;
 }
 
@@ -24981,7 +24982,9 @@  build_deduction_guide (tree ctor, tree outer_args, tsubst_flags_t complain)
 				     FUNCTION_DECL,
 				     dguide_name (type), fntype);
   DECL_ARGUMENTS (ded_fn) = fargs;
+  DECL_ARTIFICIAL (ded_fn) = true;
   tree ded_tmpl = build_template_decl (ded_fn, tparms, /*member*/false);
+  DECL_ARTIFICIAL (ded_tmpl) = true;
   DECL_TEMPLATE_RESULT (ded_tmpl) = ded_fn;
   TREE_TYPE (ded_tmpl) = TREE_TYPE (ded_fn);
   DECL_TEMPLATE_INFO (ded_fn) = build_template_info (ded_tmpl, targs);
diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction25.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction25.C
new file mode 100644
index 0000000..07ab5f5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction25.C
@@ -0,0 +1,24 @@ 
+// Testcase from P0512R0 for C++17 NB comment US 19
+// { dg-options -std=c++1z }
+
+template<typename> struct remove_ref;
+template<typename _Tp> struct remove_ref { typedef _Tp type; };
+template<typename _Tp> struct remove_ref<_Tp&> { typedef _Tp type; };
+template<typename _Tp> struct remove_ref<_Tp&&> { typedef _Tp type; };
+template<typename _Tp> using remove_ref_t = typename remove_ref<_Tp>::type;
+
+template<class T> struct A {
+ A(T, int*); // #1
+ A(A<T>&, int*); // #2
+ enum { value };
+};
+template<class T, int N = remove_ref_t<T>::value> A(T&&, int*) -> A<T>; //#3
+
+A a{1,0}; // uses #1 to deduce A<int> and initializes with #1
+A b{a,0}; // uses #3 (not #2) to deduce A<A<int>&> and initializes with #1
+
+template <class,class> struct same;
+template <class T> struct same<T,T> {};
+
+same<decltype(a),A<int>> s1;
+same<decltype(b),A<A<int>&>> s2;

commit 0709587b7c94c96b56d3d303e86fc65cee762ce3
Author: Jason Merrill <jason@redhat.com>
Date:   Thu Jan 19 15:17:38 2017 -0500

            US 20 - forwarding references and class template argument deduction
    
            * cp-tree.h (TEMPLATE_TYPE_PARM_FOR_CLASS): New.
            * pt.c (push_template_decl_real): Set it.
            (maybe_adjust_types_for_deduction): Check it.
            (rewrite_template_parm): Copy it.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 9c44367..f7c7a35 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -146,6 +146,7 @@  operator == (const cp_expr &lhs, tree rhs)
       BLOCK_OUTER_CURLY_BRACE_P (in BLOCK)
       FOLD_EXPR_MODOP_P (*_FOLD_EXPR)
       IF_STMT_CONSTEXPR_P (IF_STMT)
+      TEMPLATE_TYPE_PARM_FOR_CLASS (TEMPLATE_TYPE_PARM)
    1: IDENTIFIER_VIRTUAL_P (in IDENTIFIER_NODE)
       TI_PENDING_TEMPLATE_FLAG.
       TEMPLATE_PARMS_FOR_INLINE.
@@ -5207,6 +5208,11 @@  enum auto_deduction_context
   adc_decomp_type    /* Decomposition declaration initializer deduction */
 };
 
+/* True if this type-parameter belongs to a class template, used by C++17
+   class template argument deduction.  */
+#define TEMPLATE_TYPE_PARM_FOR_CLASS(NODE) \
+  (TREE_LANG_FLAG_0 (TEMPLATE_TYPE_PARM_CHECK (NODE)))
+
 /* True iff this TEMPLATE_TYPE_PARM represents decltype(auto).  */
 #define AUTO_IS_DECLTYPE(NODE) \
   (TYPE_LANG_FLAG_5 (TEMPLATE_TYPE_PARM_CHECK (NODE)))
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index f683727..8c920c3 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -5263,7 +5263,18 @@  push_template_decl_real (tree decl, bool is_friend)
 	}
       else if (DECL_IMPLICIT_TYPEDEF_P (decl)
 	       && CLASS_TYPE_P (TREE_TYPE (decl)))
-	/* OK */;
+	{
+	  /* Class template, set TEMPLATE_TYPE_PARM_FOR_CLASS.  */
+	  tree parms = INNERMOST_TEMPLATE_PARMS (current_template_parms);
+	  for (int i = 0; i < TREE_VEC_LENGTH (parms); ++i)
+	    {
+	      tree t = TREE_VALUE (TREE_VEC_ELT (parms, i));
+	      if (TREE_CODE (t) == TYPE_DECL)
+		t = TREE_TYPE (t);
+	      if (TREE_CODE (t) == TEMPLATE_TYPE_PARM)
+		TEMPLATE_TYPE_PARM_FOR_CLASS (t) = true;
+	    }
+	}
       else if (TREE_CODE (decl) == TYPE_DECL
 	       && TYPE_DECL_ALIAS_P (decl))
 	/* alias-declaration */
@@ -18649,12 +18660,16 @@  maybe_adjust_types_for_deduction (unification_kind_t strict,
 	*arg = TYPE_MAIN_VARIANT (*arg);
     }
 
-  /* From C++0x [14.8.2.1/3 temp.deduct.call] (after DR606), "If P is
-     of the form T&&, where T is a template parameter, and the argument
-     is an lvalue, T is deduced as A& */
+  /* [14.8.2.1/3 temp.deduct.call], "A forwarding reference is an rvalue
+     reference to a cv-unqualified template parameter that does not represent a
+     template parameter of a class template (during class template argument
+     deduction (13.3.1.8)). If P is a forwarding reference and the argument is
+     an lvalue, the type "lvalue reference to A" is used in place of A for type
+     deduction. */
   if (TREE_CODE (*parm) == REFERENCE_TYPE
       && TYPE_REF_IS_RVALUE (*parm)
       && TREE_CODE (TREE_TYPE (*parm)) == TEMPLATE_TYPE_PARM
+      && !TEMPLATE_TYPE_PARM_FOR_CLASS (TREE_TYPE (*parm))
       && cp_type_quals (TREE_TYPE (*parm)) == TYPE_UNQUALIFIED
       && (arg_expr ? lvalue_p (arg_expr)
 	  /* try_one_overload doesn't provide an arg_expr, but
@@ -24798,8 +24813,12 @@  rewrite_template_parm (tree olddecl, unsigned index, unsigned level,
   if (TREE_CODE (olddecl) == TYPE_DECL
       || TREE_CODE (olddecl) == TEMPLATE_DECL)
     {
-      newtype = cxx_make_type (TREE_CODE (TREE_TYPE (olddecl)));
+      tree oldtype = TREE_TYPE (olddecl);
+      newtype = cxx_make_type (TREE_CODE (oldtype));
       TYPE_MAIN_VARIANT (newtype) = newtype;
+      if (TREE_CODE (oldtype) == TEMPLATE_TYPE_PARM)
+	TEMPLATE_TYPE_PARM_FOR_CLASS (newtype)
+	  = TEMPLATE_TYPE_PARM_FOR_CLASS (oldtype);
     }
   else
     newtype = tsubst (TREE_TYPE (olddecl), tsubst_args,
diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction26.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction26.C
new file mode 100644
index 0000000..ea58af7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction26.C
@@ -0,0 +1,23 @@ 
+// Testcase from P0512R0 for C++17 NB comment US 20
+// { dg-options -std=c++1z }
+
+template <class,class> struct same;
+template <class T> struct same<T,T> {};
+
+template<class T> struct A {
+  template<class U>
+  A(T&&, U&&, int*); // #1: T&& is not a forwarding reference
+                     //     U&& is a forwarding reference
+  A(T&&, int*);	     // #2
+};
+template<class T>
+A(T&&, int*) -> A<T>; // #3: T&& is a forwarding reference
+
+int i;
+int *ip;
+A a0{0, 0, ip}; // uses #1 to deduce A<int> and #1 to initialize
+same<decltype(a0),A<int>> s1;
+A a2{i, ip};    // uses #3 to deduce A<int&> and #2 to initialize
+same<decltype(a2),A<int&>> s2;
+
+A a{i, 0, ip};  // { dg-error "" } cannot deduce from #1