diff mbox series

c++: CTAD and forwarding references [PR88252]

Message ID 20210714152639.947266-1-ppalka@redhat.com
State New
Headers show
Series c++: CTAD and forwarding references [PR88252] | expand

Commit Message

Patrick Palka July 14, 2021, 3:26 p.m. UTC
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

Comments

Jason Merrill July 14, 2021, 4:33 p.m. UTC | #1
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.

>         && cp_type_quals (TREE_TYPE (*parm)) == TYPE_UNQUALIFIED
>         && (arg_expr ? lvalue_p (arg_expr)
>   	  /* try_one_overload doesn't provide an arg_expr, but
> @@ -22080,8 +22097,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 +22767,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 +23467,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 +28513,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};
> +}
>
Patrick Palka July 14, 2021, 5:52 p.m. UTC | #2
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};
+}
Jason Merrill July 14, 2021, 6:17 p.m. UTC | #3
On 7/14/21 1:52 PM, Patrick Palka wrote:
> 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.

OK.

> -- >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};
> +}
>
diff mbox series

Patch

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))
       && cp_type_quals (TREE_TYPE (*parm)) == TYPE_UNQUALIFIED
       && (arg_expr ? lvalue_p (arg_expr)
 	  /* try_one_overload doesn't provide an arg_expr, but
@@ -22080,8 +22097,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 +22767,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 +23467,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 +28513,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};
+}