[PATCH] c++: CTAD and forwarding references [PR88252]
Patrick Palka
ppalka@redhat.com
Wed Jul 14 17:52:14 GMT 2021
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
More information about the Gcc-patches
mailing list