Message ID | 20210714152639.947266-1-ppalka@redhat.com |
---|---|
State | New |
Headers | show |
Series | c++: CTAD and forwarding references [PR88252] | expand |
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}; > +} >
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}; +}
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 --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}; +}