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

Reply via email to