[PATCH] c++: CTAD and forwarding references [PR88252]

Patrick Palka ppalka@redhat.com
Wed Jul 14 17:52:14 GMT 2021


On Wed, 14 Jul 2021, Jason Merrill wrote:

> On 7/14/21 11:26 AM, Patrick Palka wrote:
> > Here we're incorrectly treating T&& as a forwarding reference during
> > CTAD even though T is a template parameter of the class template.
> > 
> > This happens because the template parameter T in the out-of-line
> > definition of the constructor doesn't have the flag
> > TEMPLATE_TYPE_PARM_FOR_CLASS set, and during duplicate_decls the
> > the redeclaration (which is in terms of this unflagged T) prevails.
> > To fix this, we could perhaps be more consistent about setting the flag,
> > but it appears we don't really need the flag to make the determination.
> > 
> > Since the template parameters of an artificial guide consist of the
> > template parameters of the class template followed by those of the
> > constructor (if any), it should suffice to look at the index of the
> > template parameter to determine whether T&& is a forwarding reference or
> > not.  This patch replaces the TEMPLATE_TYPE_PARM_FOR_CLASS flag with
> > this approach.
> > 
> > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
> > trunk?
> > 
> > 	PR c++/88252
> > 
> > gcc/cp/ChangeLog:
> > 
> > 	* cp-tree.h (TEMPLATE_TYPE_PARM_FOR_CLASS): Remove.
> > 	* pt.c (push_template_decl): Remove TEMPLATE_TYPE_PARM_FOR_CLASS
> > 	handling.
> > 	(redeclare_class_template): Likewise.
> > 	(parm_can_form_fwding_ref_p): Define.
> > 	(maybe_adjust_types_for_deduction): Use it instead of
> > 	TEMPLATE_TYPE_PARM_FOR_CLASS.  Add tparms parameter.
> > 	(unify_one_argument): Pass tparms to
> > 	maybe_adjust_types_for_deduction.
> > 	(try_one_overload): Likewise.
> > 	(unify): Likewise.
> > 	(rewrite_template_parm): Remove TEMPLATE_TYPE_PARM_FOR_CLASS
> > 	handling.
> > 
> > gcc/testsuite/ChangeLog:
> > 
> > 	* g++.dg/cpp1z/class-deduction96.C: New test.
> > ---
> >   gcc/cp/cp-tree.h                              |  6 --
> >   gcc/cp/pt.c                                   | 67 ++++++++++++-------
> >   .../g++.dg/cpp1z/class-deduction96.C          | 34 ++++++++++
> >   3 files changed, 75 insertions(+), 32 deletions(-)
> >   create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction96.C
> > 
> > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> > index b1cf44ecdb8..f4bcab5b18d 100644
> > --- a/gcc/cp/cp-tree.h
> > +++ b/gcc/cp/cp-tree.h
> > @@ -443,7 +443,6 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
> >         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)
> >         DECL_NAMESPACE_INLINE_P (in NAMESPACE_DECL)
> >         SWITCH_STMT_ALL_CASES_P (in SWITCH_STMT)
> >         REINTERPRET_CAST_P (in NOP_EXPR)
> > @@ -5863,11 +5862,6 @@ 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 cf0ce770d52..01ef2984f23 100644
> > --- a/gcc/cp/pt.c
> > +++ b/gcc/cp/pt.c
> > @@ -154,8 +154,8 @@ static void tsubst_enum	(tree, tree, tree);
> >   static bool check_instantiated_args (tree, tree, tsubst_flags_t);
> >   static int check_non_deducible_conversion (tree, tree, int, int,
> >   					   struct conversion **, bool);
> > -static int maybe_adjust_types_for_deduction (unification_kind_t, tree*,
> > tree*,
> > -					     tree);
> > +static int maybe_adjust_types_for_deduction (tree, unification_kind_t,
> > +					     tree*, tree*, tree);
> >   static int type_unification_real (tree, tree, tree, const tree *,
> >   				  unsigned int, int, unification_kind_t,
> >   				  vec<deferred_access_check, va_gc> **,
> > @@ -5801,18 +5801,7 @@ push_template_decl (tree decl, bool is_friend)
> >   	}
> >         else if (DECL_IMPLICIT_TYPEDEF_P (decl)
> >   	       && CLASS_TYPE_P (TREE_TYPE (decl)))
> > -	{
> > -	  /* 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;
> > -	    }
> > -	}
> > +	/* Class template.  */;
> >         else if (TREE_CODE (decl) == TYPE_DECL
> >   	       && TYPE_DECL_ALIAS_P (decl))
> >   	/* alias-declaration */
> > @@ -6292,9 +6281,6 @@ redeclare_class_template (tree type, tree parms, tree
> > cons)
> >   	  gcc_assert (DECL_CONTEXT (parm) == NULL_TREE);
> >   	  DECL_CONTEXT (parm) = tmpl;
> >   	}
> > -
> > -      if (TREE_CODE (parm) == TYPE_DECL)
> > -	TEMPLATE_TYPE_PARM_FOR_CLASS (TREE_TYPE (parm)) = true;
> >       }
> >       tree ci = get_constraints (tmpl);
> > @@ -21709,6 +21695,35 @@ fn_type_unification (tree fn,
> >     return r;
> >   }
> >   +/* Return true if the template type parameter PARM for the
> > +   template TMPL can form a forwarding reference.  */
> > +
> > +static bool
> > +parm_can_form_fwding_ref_p (tree tmpl, tree parm)
> > +{
> > +  gcc_assert (!tmpl || TREE_CODE (tmpl) == TEMPLATE_DECL);
> > +  gcc_assert (TREE_CODE (parm) == TEMPLATE_TYPE_PARM);
> > +
> > +  /* As per [temp.deduct.call], all template parameters except those
> > +     that represent a template parameter of a class template during
> > +     CTAD can form a forwarding reference.  */
> > +  if (tmpl
> > +      && deduction_guide_p (tmpl)
> > +      && DECL_ARTIFICIAL (tmpl))
> > +    {
> > +      tree ctmpl = CLASSTYPE_TI_TEMPLATE (TREE_TYPE (TREE_TYPE (tmpl)));
> > +      /* Since the template parameters of an artificial guide consist of
> > +	 the template parameters of the class template followed by those
> > +	 of the constructor (if any), we can tell if PARM represents a
> > template
> > +	 parameter of the class template by comparing its index with the arity
> > +	 of the class template.  */
> > +      if (TEMPLATE_TYPE_IDX (parm)
> > +	  < TREE_VEC_LENGTH (DECL_INNERMOST_TEMPLATE_PARMS (ctmpl)))
> > +	return false;
> > +    }
> > +  return true;
> > +}
> > +
> >   /* Adjust types before performing type deduction, as described in
> >      [temp.deduct.call] and [temp.deduct.conv].  The rules in these two
> >      sections are symmetric.  PARM is the type of a function parameter
> > @@ -21718,7 +21733,8 @@ fn_type_unification (tree fn,
> >      ARG_EXPR is the original argument expression, which may be null.  */
> >     static int
> > -maybe_adjust_types_for_deduction (unification_kind_t strict,
> > +maybe_adjust_types_for_deduction (tree tparms,
> > +				  unification_kind_t strict,
> >   				  tree* parm,
> >   				  tree* arg,
> >   				  tree arg_expr)
> > @@ -21790,7 +21806,8 @@ maybe_adjust_types_for_deduction (unification_kind_t
> > strict,
> >     if (TYPE_REF_P (*parm)
> >         && TYPE_REF_IS_RVALUE (*parm)
> >         && TREE_CODE (TREE_TYPE (*parm)) == TEMPLATE_TYPE_PARM
> > -      && !TEMPLATE_TYPE_PARM_FOR_CLASS (TREE_TYPE (*parm))
> > +      && parm_can_form_fwding_ref_p (TPARMS_PRIMARY_TEMPLATE (tparms),
> > +				     TREE_TYPE (*parm))
> 
> If we're splitting some of this test into a separate function, let's move the
> whole test for whether a parm is a forwarding reference, and use it in the
> DEDUCE_EXACT code as well.

Sounds good, I moved the test into a new predicate "forwarding_reference_p".
How does the following look?  Bootstrap and regtest in progress.

-- >8 --

	PR c++/88252

gcc/cp/ChangeLog:

	* cp-tree.h (TEMPLATE_TYPE_PARM_FOR_CLASS): Remove.
	* pt.c (push_template_decl): Remove TEMPLATE_TYPE_PARM_FOR_CLASS
	handling.
	(redeclare_class_template): Likewise.
	(forwarding_reference_p): Define.
	(maybe_adjust_types_for_deduction): Use it.  Add tparms parameter.
	(unify_one_argument): Pass tparms to
	maybe_adjust_types_for_deduction.
	(try_one_overload): Likewise.
	(unify): Likewise.
	(rewrite_template_parm): Remove TEMPLATE_TYPE_PARM_FOR_CLASS
	handling.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp1z/class-deduction96.C: New test.
---
 gcc/cp/cp-tree.h                              |  6 --
 gcc/cp/pt.c                                   | 89 ++++++++++---------
 .../g++.dg/cpp1z/class-deduction96.C          | 34 +++++++
 3 files changed, 83 insertions(+), 46 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction96.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index b1cf44ecdb8..f4bcab5b18d 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -443,7 +443,6 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
       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)
       DECL_NAMESPACE_INLINE_P (in NAMESPACE_DECL)
       SWITCH_STMT_ALL_CASES_P (in SWITCH_STMT)
       REINTERPRET_CAST_P (in NOP_EXPR)
@@ -5863,11 +5862,6 @@ 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 cf0ce770d52..6d6b7f90985 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -154,8 +154,8 @@ static void tsubst_enum	(tree, tree, tree);
 static bool check_instantiated_args (tree, tree, tsubst_flags_t);
 static int check_non_deducible_conversion (tree, tree, int, int,
 					   struct conversion **, bool);
-static int maybe_adjust_types_for_deduction (unification_kind_t, tree*, tree*,
-					     tree);
+static int maybe_adjust_types_for_deduction (tree, unification_kind_t,
+					     tree*, tree*, tree);
 static int type_unification_real (tree, tree, tree, const tree *,
 				  unsigned int, int, unification_kind_t,
 				  vec<deferred_access_check, va_gc> **,
@@ -5801,18 +5801,7 @@ push_template_decl (tree decl, bool is_friend)
 	}
       else if (DECL_IMPLICIT_TYPEDEF_P (decl)
 	       && CLASS_TYPE_P (TREE_TYPE (decl)))
-	{
-	  /* 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;
-	    }
-	}
+	/* Class template.  */;
       else if (TREE_CODE (decl) == TYPE_DECL
 	       && TYPE_DECL_ALIAS_P (decl))
 	/* alias-declaration */
@@ -6292,9 +6281,6 @@ redeclare_class_template (tree type, tree parms, tree cons)
 	  gcc_assert (DECL_CONTEXT (parm) == NULL_TREE);
 	  DECL_CONTEXT (parm) = tmpl;
 	}
-
-      if (TREE_CODE (parm) == TYPE_DECL)
-	TEMPLATE_TYPE_PARM_FOR_CLASS (TREE_TYPE (parm)) = true;
     }
 
   tree ci = get_constraints (tmpl);
@@ -21709,6 +21695,40 @@ fn_type_unification (tree fn,
   return r;
 }
 
+/* Returns true iff PARM is a forwarding reference in the context of
+   template argument deduction for TMPL.  */
+
+static bool
+forwarding_reference_p (tree parm, tree tmpl)
+{
+  /* [temp.deduct.call], "A forwarding reference is an rvalue reference to a
+     cv-unqualified template parameter ..."  */
+  if (TYPE_REF_P (parm)
+      && TYPE_REF_IS_RVALUE (parm)
+      && TREE_CODE (TREE_TYPE (parm)) == TEMPLATE_TYPE_PARM
+      && cp_type_quals (TREE_TYPE (parm)) == TYPE_UNQUALIFIED)
+    {
+      /* [temp.deduct.call], "... that does not represent a template parameter
+	 of a class template (during class template argument deduction)."  */
+      if (tmpl
+	  && deduction_guide_p (tmpl)
+	  && DECL_ARTIFICIAL (tmpl))
+	{
+	  /* Since the template parameters of an artificial guide consist of
+	     the template parameters of the class template followed by those of
+	     the constructor (if any), we can tell if PARM represents a template
+	     parameter of the class template by comparing its index with the
+	     arity of the class template.  */
+	  tree ctmpl = CLASSTYPE_TI_TEMPLATE (TREE_TYPE (TREE_TYPE (tmpl)));
+	  if (TEMPLATE_TYPE_IDX (TREE_TYPE (parm))
+	      < TREE_VEC_LENGTH (DECL_INNERMOST_TEMPLATE_PARMS (ctmpl)))
+	    return false;
+	}
+      return true;
+    }
+  return false;
+}
+
 /* Adjust types before performing type deduction, as described in
    [temp.deduct.call] and [temp.deduct.conv].  The rules in these two
    sections are symmetric.  PARM is the type of a function parameter
@@ -21718,7 +21738,8 @@ fn_type_unification (tree fn,
    ARG_EXPR is the original argument expression, which may be null.  */
 
 static int
-maybe_adjust_types_for_deduction (unification_kind_t strict,
+maybe_adjust_types_for_deduction (tree tparms,
+				  unification_kind_t strict,
 				  tree* parm,
 				  tree* arg,
 				  tree arg_expr)
@@ -21741,10 +21762,7 @@ maybe_adjust_types_for_deduction (unification_kind_t strict,
       /* Core issue #873: Do the DR606 thing (see below) for these cases,
 	 too, but here handle it by stripping the reference from PARM
 	 rather than by adding it to ARG.  */
-      if (TYPE_REF_P (*parm)
-	  && TYPE_REF_IS_RVALUE (*parm)
-	  && TREE_CODE (TREE_TYPE (*parm)) == TEMPLATE_TYPE_PARM
-	  && cp_type_quals (TREE_TYPE (*parm)) == TYPE_UNQUALIFIED
+      if (forwarding_reference_p (*parm, TPARMS_PRIMARY_TEMPLATE (tparms))
 	  && TYPE_REF_P (*arg)
 	  && !TYPE_REF_IS_RVALUE (*arg))
 	*parm = TREE_TYPE (*parm);
@@ -21781,17 +21799,10 @@ maybe_adjust_types_for_deduction (unification_kind_t strict,
 	*arg = TYPE_MAIN_VARIANT (*arg);
     }
 
-  /* [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 (TYPE_REF_P (*parm)
-      && 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
+  /* [temp.deduct.call], "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 (forwarding_reference_p (*parm, TPARMS_PRIMARY_TEMPLATE (tparms))
       && (arg_expr ? lvalue_p (arg_expr)
 	  /* try_one_overload doesn't provide an arg_expr, but
 	     functions are always lvalues.  */
@@ -22080,8 +22091,8 @@ unify_one_argument (tree tparms, tree targs, tree parm, tree arg,
 	    return unify_invalid (explain_p);
 	}
 
-      arg_strict |=
-	maybe_adjust_types_for_deduction (strict, &parm, &arg, arg_expr);
+      arg_strict |= maybe_adjust_types_for_deduction (tparms, strict,
+						      &parm, &arg, arg_expr);
     }
   else
     if ((TYPE_P (parm) || TREE_CODE (parm) == TEMPLATE_DECL)
@@ -22750,7 +22761,8 @@ try_one_overload (tree tparms,
   else if (addr_p)
     arg = build_pointer_type (arg);
 
-  sub_strict |= maybe_adjust_types_for_deduction (strict, &parm, &arg, NULL);
+  sub_strict |= maybe_adjust_types_for_deduction (tparms, strict,
+						  &parm, &arg, NULL_TREE);
 
   /* We don't copy orig_targs for this because if we have already deduced
      some template args from previous args, unify would complain when we
@@ -23449,7 +23461,7 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
 		/* It should only be possible to get here for a call.  */
 		gcc_assert (elt_strict & UNIFY_ALLOW_OUTER_LEVEL);
 		elt_strict |= maybe_adjust_types_for_deduction
-		  (DEDUCE_CALL, &elttype, &type, elt);
+		  (tparms, DEDUCE_CALL, &elttype, &type, elt);
 		elt = type;
 	      }
 
@@ -28495,9 +28507,6 @@ rewrite_template_parm (tree olddecl, unsigned index, unsigned level,
       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
     {
diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction96.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction96.C
new file mode 100644
index 00000000000..7fa8400830e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction96.C
@@ -0,0 +1,34 @@
+// PR c++/88252
+// { dg-do compile { target c++17 } }
+
+template<class T>
+struct A {
+  A(T&&);
+  template<class U> A(T&&, U&&);
+  template<class U> struct B;
+};
+
+template<class T>
+A<T>::A(T&&) { }
+
+template<class T>
+template<class U>
+A<T>::A(T&&, U&&) { }
+
+template<class T>
+template<class U>
+struct A<T>::B {
+  B(U&&);
+  template<class V> B(U&&, V&&);
+};
+
+int i;
+
+int main() {
+  A{i}; // { dg-error "deduction|no match|rvalue reference" }
+  A{i, 0}; // { dg-error "deduction|no match|rvalue reference" }
+  A{0, i};
+  A<int>::B{i}; // { dg-error "deduction|no match|rvalue reference" }
+  A<int>::B{i, 0}; // { dg-error "deduction|no match|rvalue reference" }
+  A<int>::B{0, i};
+}
-- 
2.32.0.264.g75ae10bc75



More information about the Gcc-patches mailing list