PR c++/42329 - deducing base template for template template arg
authorJason Merrill <jason@redhat.com>
Wed, 21 Dec 2016 19:38:50 +0000 (14:38 -0500)
committerJason Merrill <jason@gcc.gnu.org>
Wed, 21 Dec 2016 19:38:50 +0000 (14:38 -0500)
* pt.c (unify_bound_ttp_args): Split out from unify.
(try_class_unification): Handle BOUND_TEMPLATE_TEMPLATE_PARM.
(unify): Check for type/non-type mismatch early.
[BOUND_TEMPLATE_TEMPLATE_PARM]: Try get_template_base.

From-SVN: r243870

gcc/cp/ChangeLog
gcc/cp/pt.c
gcc/testsuite/g++.dg/cpp0x/alias-decl-2.C
gcc/testsuite/g++.dg/cpp0x/variadic-ttp6.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/ttp-derived1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/ttp-derived2.C [new file with mode: 0644]

index 05d6dba39c32cddf4a9cf655f4bad7b57a8d9d9b..ffda8e161004f94a8666c3432c289cbdb08852ac 100644 (file)
@@ -1,5 +1,11 @@
 2016-12-21  Jason Merrill  <jason@redhat.com>
 
+       PR c++/42329
+       * pt.c (unify_bound_ttp_args): Split out from unify.
+       (try_class_unification): Handle BOUND_TEMPLATE_TEMPLATE_PARM.
+       (unify): Check for type/non-type mismatch early.
+       [BOUND_TEMPLATE_TEMPLATE_PARM]: Try get_template_base.
+
        * pt.c (coerce_template_parms): Consider variadic_args_p before
        complaining about too many template arguments.
 
index f839c5384879fdc7c0a7de3085e134f0498518f5..6abb639e0812b4f88d39af0793256ad191eed253 100644 (file)
@@ -6863,6 +6863,27 @@ coerce_template_template_parm (tree parm,
   return 1;
 }
 
+/* Subroutine of unify for the case when PARM is a
+   BOUND_TEMPLATE_TEMPLATE_PARM.  */
+
+static int
+unify_bound_ttp_args (tree tparms, tree targs, tree parm, tree arg,
+                     bool explain_p)
+{
+  tree parmvec = TYPE_TI_ARGS (parm);
+  tree argvec = INNERMOST_TEMPLATE_ARGS (TYPE_TI_ARGS (arg));
+
+  /* The template template parm might be variadic and the argument
+     not, so flatten both argument lists.  */
+  parmvec = expand_template_argument_pack (parmvec);
+  argvec = expand_template_argument_pack (argvec);
+
+  if (unify (tparms, targs, parmvec, argvec,
+            UNIFY_ALLOW_NONE, explain_p))
+    return 1;
+
+  return 0;
+}
 
 /* Return 1 if PARM_PARMS and ARG_PARMS matches using rule for
    template template parameters.  Both PARM_PARMS and ARG_PARMS are
@@ -19391,9 +19412,12 @@ try_class_unification (tree tparms, tree targs, tree parm, tree arg,
 {
   tree copy_of_targs;
 
-  if (!CLASSTYPE_TEMPLATE_INFO (arg)
-      || (most_general_template (CLASSTYPE_TI_TEMPLATE (arg))
-         != most_general_template (CLASSTYPE_TI_TEMPLATE (parm))))
+  if (!CLASSTYPE_SPECIALIZATION_OF_PRIMARY_TEMPLATE_P (arg))
+    return NULL_TREE;
+  else if (TREE_CODE (parm) == BOUND_TEMPLATE_TEMPLATE_PARM)
+    /* Matches anything.  */;
+  else if (most_general_template (CLASSTYPE_TI_TEMPLATE (arg))
+          != most_general_template (CLASSTYPE_TI_TEMPLATE (parm)))
     return NULL_TREE;
 
   /* We need to make a new template argument vector for the call to
@@ -19428,6 +19452,13 @@ try_class_unification (tree tparms, tree targs, tree parm, tree arg,
      would reject the possibility I=1.  */
   copy_of_targs = make_tree_vec (TREE_VEC_LENGTH (targs));
 
+  if (TREE_CODE (parm) == BOUND_TEMPLATE_TEMPLATE_PARM)
+    {
+      if (unify_bound_ttp_args (tparms, copy_of_targs, parm, arg, explain_p))
+       return NULL_TREE;
+      return arg;
+    }
+
   /* If unification failed, we're done.  */
   if (unify (tparms, copy_of_targs, CLASSTYPE_TI_ARGS (parm),
             CLASSTYPE_TI_ARGS (arg), UNIFY_ALLOW_NONE, explain_p))
@@ -19832,6 +19863,25 @@ unify_array_domain (tree tparms, tree targs,
                UNIFY_ALLOW_INTEGER, explain_p);
 }
 
+/* Returns whether T, a P or A in unify, is a type, template or expression.  */
+
+enum pa_kind_t { pa_type, pa_tmpl, pa_expr };
+
+static pa_kind_t
+pa_kind (tree t)
+{
+  if (PACK_EXPANSION_P (t))
+    t = PACK_EXPANSION_PATTERN (t);
+  if (TREE_CODE (t) == TEMPLATE_TEMPLATE_PARM
+      || TREE_CODE (t) == UNBOUND_CLASS_TEMPLATE
+      || DECL_TYPE_TEMPLATE_P (t))
+    return pa_tmpl;
+  else if (TYPE_P (t))
+    return pa_type;
+  else
+    return pa_expr;
+}
+
 /* Deduce the value of template parameters.  TPARMS is the (innermost)
    set of template parameters to a template.  TARGS is the bindings
    for those template parameters, as determined thus far; TARGS may
@@ -19985,6 +20035,11 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
       return unify_success (explain_p);
     }
 
+  /* If parm and arg aren't the same kind of thing (template, type, or
+     expression), fail early.  */
+  if (pa_kind (parm) != pa_kind (arg))
+    return unify_invalid (explain_p);
+
   /* Immediately reject some pairs that won't unify because of
      cv-qualification mismatches.  */
   if (TREE_CODE (arg) == TREE_CODE (parm)
@@ -20053,100 +20108,32 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
 
       if (TREE_CODE (parm) == BOUND_TEMPLATE_TEMPLATE_PARM)
        {
+         if (strict_in & UNIFY_ALLOW_DERIVED)
+           {
+             /* First try to match ARG directly.  */
+             tree t = try_class_unification (tparms, targs, parm, arg,
+                                             explain_p);
+             if (!t)
+               {
+                 /* Otherwise, look for a suitable base of ARG, as below.  */
+                 enum template_base_result r;
+                 r = get_template_base (tparms, targs, parm, arg,
+                                        explain_p, &t);
+                 if (!t)
+                   return unify_no_common_base (explain_p, r, parm, arg);
+                 arg = t;
+               }
+           }
          /* ARG must be constructed from a template class or a template
             template parameter.  */
-         if (TREE_CODE (arg) != BOUND_TEMPLATE_TEMPLATE_PARM
-             && !CLASSTYPE_SPECIALIZATION_OF_PRIMARY_TEMPLATE_P (arg))
+         else if (TREE_CODE (arg) != BOUND_TEMPLATE_TEMPLATE_PARM
+                  && !CLASSTYPE_SPECIALIZATION_OF_PRIMARY_TEMPLATE_P (arg))
            return unify_template_deduction_failure (explain_p, parm, arg);
-         {
-           tree parmvec = TYPE_TI_ARGS (parm);
-           tree argvec = INNERMOST_TEMPLATE_ARGS (TYPE_TI_ARGS (arg));
-           tree full_argvec = add_to_template_args (targs, argvec);
-           tree parm_parms 
-              = DECL_INNERMOST_TEMPLATE_PARMS
-                 (TEMPLATE_TEMPLATE_PARM_TEMPLATE_DECL (parm));
-           int i, len;
-            int parm_variadic_p = 0;
-
-           /* The resolution to DR150 makes clear that default
-              arguments for an N-argument may not be used to bind T
-              to a template template parameter with fewer than N
-              parameters.  It is not safe to permit the binding of
-              default arguments as an extension, as that may change
-              the meaning of a conforming program.  Consider:
-
-                 struct Dense { static const unsigned int dim = 1; };
-
-                 template <template <typename> class View,
-                           typename Block>
-                 void operator+(float, View<Block> const&);
-
-                 template <typename Block,
-                           unsigned int Dim = Block::dim>
-                 struct Lvalue_proxy { operator float() const; };
-
-                 void
-                 test_1d (void) {
-                   Lvalue_proxy<Dense> p;
-                   float b;
-                   b + p;
-                 }
 
-             Here, if Lvalue_proxy is permitted to bind to View, then
-             the global operator+ will be used; if they are not, the
-             Lvalue_proxy will be converted to float.  */
-           if (coerce_template_parms (parm_parms,
-                                       full_argvec,
-                                      TYPE_TI_TEMPLATE (parm),
-                                      complain,
-                                      /*require_all_args=*/true,
-                                      /*use_default_args=*/false)
-               == error_mark_node)
-             return 1;
-
-           /* Deduce arguments T, i from TT<T> or TT<i>.
-              We check each element of PARMVEC and ARGVEC individually
-              rather than the whole TREE_VEC since they can have
-              different number of elements.  */
-
-            parmvec = expand_template_argument_pack (parmvec);
-            argvec = expand_template_argument_pack (argvec);
-
-            len = TREE_VEC_LENGTH (parmvec);
-
-            /* Check if the parameters end in a pack, making them
-               variadic.  */
-            if (len > 0
-                && PACK_EXPANSION_P (TREE_VEC_ELT (parmvec, len - 1)))
-              parm_variadic_p = 1;
-            
-             for (i = 0; i < len - parm_variadic_p; ++i)
-              /* If the template argument list of P contains a pack
-                 expansion that is not the last template argument, the
-                 entire template argument list is a non-deduced
-                 context.  */
-              if (PACK_EXPANSION_P (TREE_VEC_ELT (parmvec, i)))
-                return unify_success (explain_p);
-
-            if (TREE_VEC_LENGTH (argvec) < len - parm_variadic_p)
-              return unify_too_few_arguments (explain_p,
-                                             TREE_VEC_LENGTH (argvec), len);
-
-             for (i = 0; i < len - parm_variadic_p; ++i)
-             {
-               RECUR_AND_CHECK_FAILURE (tparms, targs,
-                                        TREE_VEC_ELT (parmvec, i),
-                                        TREE_VEC_ELT (argvec, i),
-                                        UNIFY_ALLOW_NONE, explain_p);
-             }
+         /* Deduce arguments T, i from TT<T> or TT<i>.  */
+         if (unify_bound_ttp_args (tparms, targs, parm, arg, explain_p))
+           return 1;
 
-           if (parm_variadic_p
-               && unify_pack_expansion (tparms, targs,
-                                        parmvec, argvec,
-                                        DEDUCE_EXACT,
-                                        /*subr=*/true, explain_p))
-             return 1;
-         }
          arg = TYPE_TI_TEMPLATE (arg);
 
          /* Fall through to deduce template name.  */
index cf59a55eea160ca05fd3111fa7389a1af45535d0..b70b7d4d68bf2f63d2383ab728af9d153bf63641 100644 (file)
@@ -22,7 +22,7 @@ template<class T> using Vec = Vector<T, Alloc<T> >;
 
 template<class T> void g(Vector<T, Alloc<T> >);
 
-template<template<class T> class TT> void h(TT<int>); // { dg-message "provided for" }
+template<template<class T> class TT> void h(TT<int>); // { dg-message "" }
 
 void
 bar()
diff --git a/gcc/testsuite/g++.dg/cpp0x/variadic-ttp6.C b/gcc/testsuite/g++.dg/cpp0x/variadic-ttp6.C
new file mode 100644 (file)
index 0000000..cf70a7a
--- /dev/null
@@ -0,0 +1,10 @@
+// { dg-do compile { target c++11 } }
+
+template <class T> struct A { using type = T; };
+template <template <class...> class C, class... Ts>
+struct A<C<Ts...>> { };
+
+template <class T2, template <class> class TT> struct B { };
+template <class T3> struct C { };
+
+A<B<int,C>>::type a;
diff --git a/gcc/testsuite/g++.dg/template/ttp-derived1.C b/gcc/testsuite/g++.dg/template/ttp-derived1.C
new file mode 100644 (file)
index 0000000..4238eef
--- /dev/null
@@ -0,0 +1,12 @@
+// PR c++/60177
+
+template<class> struct Base { };
+
+struct Derived : Base<void> { };
+
+template<template<typename> class TT, typename T>
+void func (TT<T>) { }
+
+int main () {
+  func (Derived ());
+}
diff --git a/gcc/testsuite/g++.dg/template/ttp-derived2.C b/gcc/testsuite/g++.dg/template/ttp-derived2.C
new file mode 100644 (file)
index 0000000..b2fe36f
--- /dev/null
@@ -0,0 +1,16 @@
+// PR c++/42329
+
+template <typename T1, typename T2>
+class B {};
+
+template <typename T>
+class D : public B<T, T> {};
+
+template <template <typename, typename> class U, typename T1, typename T2>
+void g(U<T1, T2>*) {}
+
+int main()
+{
+  D<long> dl;
+  g(&dl); // error: no matching function for call to ‘g(D<long int>*)’
+}