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