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

Jason Merrill jason@redhat.com
Wed Jul 14 16:33:17 GMT 2021


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};
> +}
> 



More information about the Gcc-patches mailing list