[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