This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[C++ PATCH] Fix unification of template template parameters (PRs 32565, 33943, 33965)


This patch fixes three PRs related to the unification (template
argument deduction) of template template parameters. While the three
PRs are ice-on-valid issues with variadic templates, working with
Jakub's previous patch to fix these issues uncovered some problems in
the way we were checking whether template template parameters could be
bound to certain template arguments, so it brings us until better
conformance with C++98/03 as well.
Consider the following:

  template<typename T, template<T> class C>  void f(C<5>, T);

  template<int N> struct X {};
  void g() {
    f(X<5>(), 5l);
  }

C++98/03 says that 'C' cannot be deduced to 'X', because the template
argument list of 'C' ends up containing a 'long' parameter while 'X'
has an 'int' parameter. However, we were permitting the binding of 'C'
to 'X', and silently accepting this ill-formed code. With a little
magic, I could of course turn this into an example where we select the
wrong partial specialization or function template.

To fix this problem, I've added a routine that checks the bindings of
template template parameters *after* template argument deduction has
completed, since we need to know the deduced type of 'T' before we can
check the binding of 'C' to 'X'. There's some more analysis (and a
sketch of the solution, now implemented in this patch) in
http://gcc.gnu.org/ml/gcc-patches/2007-11/msg01033.html, and extensive
comments in the patch itself to describe when/how this happens.

Jakub's patch for PRs 32565 and 33943 handled the variadic templates
portion of this issue (see
http://gcc.gnu.org/ml/gcc-patches/2007-11/msg00843.html). The patch
below includes his changes, along with my changes to properly verify
the bindings of template template parameters.

Tested i686-pc-linux-gnu; no regressions. Okay for mainline?

  - Doug

2007-12-15  Douglas Gregor  <doug.gregor@gmail.com>
            Jakub Jelinek  <jakub@redhat.com>
	
	PR c++/32565
	PR c++/33943
	PR c++/33965
	* pt.c (template_template_parm_bindings_ok_p): New; verifies
	bindings of template template parameters after all template
	arguments have been deduced.
	(coerce_template_parms): Don't complain when COMPLAIN doesn't
	include tf_error.
	(fn_type_unification): Use template_template_parm_bindings_ok_p.
	(unify): Deal with variadic, bound template template parameters.
	(get_class_bindings): Use template_template_parm_bindings_ok_p.
	
	
2007-12-15  Douglas Gregor  <doug.gregor@gmail.com>
	    Jakub Jelinek  <jakub@redhat.com>
	
	PR c++/32565
	PR c++/33943
	PR c++/33965
	* g++.dg/cpp0x/variadic86.C: New.
	* g++.dg/cpp0x/variadic87.C: New.
	* g++.dg/cpp0x/variadic84.C: New.
	* g++.dg/cpp0x/variadic85.C: New.
	* g++.dg/template/ttp25.C: New.
	* g++.dg/template/ttp26.C: New.
	* g++.dg/template/ttp27.C: New.
Index: cp/pt.c
===================================================================
--- cp/pt.c	(revision 130951)
+++ cp/pt.c	(working copy)
@@ -158,6 +158,7 @@ static tree get_template_base (tree, tre
 static tree try_class_unification (tree, tree, tree, tree);
 static int coerce_template_template_parms (tree, tree, tsubst_flags_t,
 					   tree, tree);
+static bool template_template_parm_bindings_ok_p (tree, tree);
 static int template_args_equal (tree, tree);
 static void tsubst_default_arguments (tree);
 static tree for_each_template_parm_r (tree *, int *, void *);
@@ -4750,6 +4751,104 @@ coerce_template_template_parms (tree par
   return 1;
 }
 
+/* Verifies that the deduced template arguments (in TARGS) for the
+   template template parameters (in TPARMS) represent valid bindings,
+   by comparing the template parameter list of each template argument
+   to the template parameter list of its corresponding template
+   template parameter, in accordance with DR150 (see below). This
+   routine can only be called after all template arguments have been
+   deduced. It will return TRUE if all of the template template
+   parameter bindings are okay, FALSE otherwise.
+
+   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.  */
+bool 
+template_template_parm_bindings_ok_p (tree tparms, tree targs)
+{
+  int i, ntparms = TREE_VEC_LENGTH (tparms);
+
+  targs = INNERMOST_TEMPLATE_ARGS (targs);
+
+  for (i = 0; i < ntparms; ++i)
+    {
+      tree tparm = TREE_VALUE (TREE_VEC_ELT (tparms, i));
+      tree targ = TREE_VEC_ELT (targs, i);
+
+      if (TREE_CODE (tparm) == TEMPLATE_DECL && targ)
+	{
+	  tree packed_args = NULL_TREE;
+	  int idx, len = 1;
+
+	  if (ARGUMENT_PACK_P (targ))
+	    {
+	      /* Look inside the argument pack.  */
+	      packed_args = ARGUMENT_PACK_ARGS (targ);
+	      len = TREE_VEC_LENGTH (packed_args);
+	    }
+
+	  for (idx = 0; idx < len; ++idx)
+	    {
+	      tree targ_parms = NULL_TREE;
+
+	      if (packed_args)
+		/* Extract the next argument from the argument
+		   pack.  */
+		targ = TREE_VEC_ELT (packed_args, idx);
+
+	      if (PACK_EXPANSION_P (targ))
+		/* Look at the pattern of the pack expansion.  */
+		targ = PACK_EXPANSION_PATTERN (targ);
+
+	      /* Extract the template parameters from the template
+		 argument.  */
+	      if (TREE_CODE (targ) == TEMPLATE_DECL)
+		targ_parms = DECL_INNERMOST_TEMPLATE_PARMS (targ);
+	      else if (TREE_CODE (targ) == TEMPLATE_TEMPLATE_PARM)
+		targ_parms = DECL_INNERMOST_TEMPLATE_PARMS (TYPE_NAME (targ));
+
+	      /* Verify that we can coerce the template template
+		 parameters from the template argument to the template
+		 parameter.  This requires an exact match.  */
+	      if (targ_parms
+		  && !coerce_template_template_parms
+		       (DECL_INNERMOST_TEMPLATE_PARMS (tparm),
+			targ_parms,
+			tf_none,
+			tparm,
+			targs))
+		return false;
+	    }
+	}
+    }
+
+  /* Everything is okay.  */
+  return true;
+}
+
 /* Convert the indicated template ARG as necessary to match the
    indicated template PARM.  Returns the converted ARG, or
    error_mark_node if the conversion was unsuccessful.  Error and
@@ -5183,16 +5282,19 @@ coerce_template_parms (tree parms,
 
           if (arg && PACK_EXPANSION_P (arg))
             {
-              /* If ARG is a pack expansion, but PARM is not a
-                 template parameter pack (if it were, we would have
-                 handled it above), we're trying to expand into a
-                 fixed-length argument list.  */
-              if (TREE_CODE (arg) == EXPR_PACK_EXPANSION)
-                error ("cannot expand %<%E%> into a fixed-length "
-                       "argument list", arg);
-              else
-                error ("cannot expand %<%T%> into a fixed-length "
-                       "argument list", arg);
+	      if (complain & tf_error)
+		{
+		  /* If ARG is a pack expansion, but PARM is not a
+		     template parameter pack (if it were, we would have
+		     handled it above), we're trying to expand into a
+		     fixed-length argument list.  */
+		  if (TREE_CODE (arg) == EXPR_PACK_EXPANSION)
+		    error ("cannot expand %<%E%> into a fixed-length "
+			   "argument list", arg);
+		  else
+		    error ("cannot expand %<%T%> into a fixed-length "
+			   "argument list", arg);
+		}
 	      return error_mark_node;
             }
         }
@@ -11627,6 +11729,32 @@ fn_type_unification (tree fn,
         }
     }
 
+  /* Now that we have bindings for all of the template arguments,
+     ensure that the arguments deduced for the template template
+     parameters have compatible template parameter lists.  We cannot
+     check this property before we have deduced all template
+     arguments, because the template parameter types of a template
+     template parameter might depend on prior template parameters
+     deduced after the template template parameter.  The following
+     ill-formed example illustrates this issue:
+
+       template<typename T, template<T> class C> void f(C<5>, T);
+
+       template<int N> struct X {};
+
+       void g() {
+         f(X<5>(), 5l); // error: template argument deduction fails
+       }
+
+     The template parameter list of 'C' depends on the template type
+     parameter 'T', but 'C' is deduced to 'X' before 'T' is deduced to
+     'long'.  Thus, we can't check that 'C' cannot bind to 'X' at the
+     time that we deduce 'C'.  */
+  if (result == 0
+      && !template_template_parm_bindings_ok_p 
+           (DECL_INNERMOST_TEMPLATE_PARMS (fn), targs))
+    return 1;
+
   if (result == 0)
     /* All is well so far.  Now, check:
 
@@ -12711,40 +12839,26 @@ unify (tree tparms, tree targs, tree par
 	    tree argvec = INNERMOST_TEMPLATE_ARGS (TYPE_TI_ARGS (arg));
 	    tree argtmplvec
 	      = DECL_INNERMOST_TEMPLATE_PARMS (TYPE_TI_TEMPLATE (arg));
-	    int i;
-
-	    /* 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;
-		  }
+	    int i, len;
+	    int parm_variadic_p = 0;
 
-	      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.  */
+	    /* The template parameter is something like TT<T> and the
+	       template argument is something like X<A>.  Verify that
+	       the template argument list of the template argument
+	       (<T>) is compatible with the template argument list of
+	       the template parameter (<A>).  This check is only an
+	       initial check to determine feasibility of the
+	       arguments; an exact check to determine whether it is
+	       permitted to bind the template template parameter
+	       (e.g., 'TT') to the template in the template argument
+	       (e.g., 'X') will be handled by
+	       template_template_parm_bindings_ok_p after all template
+	       arguments have been deduced.  */
 	    if (coerce_template_parms (argtmplvec, parmvec,
 				       TYPE_TI_TEMPLATE (parm),
 				       tf_none,
-				       /*require_all_args=*/true,
-				       /*use_default_args=*/false)
+				       /*require_all_args=*/false,
+				       /*use_default_args=*/true)
 		== error_mark_node)
 	      return 1;
 
@@ -12753,7 +12867,19 @@ unify (tree tparms, tree targs, tree par
 	       rather than the whole TREE_VEC since they can have
 	       different number of elements.  */
 
-	    for (i = 0; i < TREE_VEC_LENGTH (parmvec); ++i)
+            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;
+	    
+            if (TREE_VEC_LENGTH (argvec) < len - parm_variadic_p)
+              return 1;
+ 
+            for (i = 0; i < len - parm_variadic_p; ++i)
 	      {
 		if (unify (tparms, targs,
 			   TREE_VEC_ELT (parmvec, i),
@@ -12761,6 +12887,14 @@ unify (tree tparms, tree targs, tree par
 			   UNIFY_ALLOW_NONE))
 		  return 1;
 	      }
+
+	    if (parm_variadic_p
+		&& unify_pack_expansion (tparms, targs,
+					 parmvec, argvec,
+					 UNIFY_ALLOW_NONE,
+					 /*call_args_p=*/false,
+					 /*subr=*/false))
+	      return 1;
 	  }
 	  arg = TYPE_TI_TEMPLATE (arg);
 
@@ -13783,6 +13917,14 @@ get_class_bindings (tree tparms, tree sp
 			      INNERMOST_TEMPLATE_ARGS (args)))
     return NULL_TREE;
 
+  /* Now that we have bindings for all of the template arguments,
+     ensure that the arguments deduced for the template template
+     parameters have compatible template parameter lists.  See the use
+     of template_template_parm_bindings_ok_p in fn_type_unification
+     for more information.  */
+  if (!template_template_parm_bindings_ok_p (tparms, deduced_args))
+    return NULL_TREE;
+
   return deduced_args;
 }
 
Index: testsuite/g++.dg/cpp0x/variadic86.C
===================================================================
--- testsuite/g++.dg/cpp0x/variadic86.C	(revision 0)
+++ testsuite/g++.dg/cpp0x/variadic86.C	(revision 0)
@@ -0,0 +1,19 @@
+// PR c++/33943
+// { dg-do compile }
+// { dg-options "-std=c++0x" }
+
+template<typename... A> struct foo {};
+
+template<typename A0, typename... A1> struct bar {};
+
+template<typename U> struct baz;
+
+template<template<typename...> class T, typename... U> struct baz< T<U...> >
+{};
+
+template<template<typename, typename...> class T, typename U, typename... V>
+struct baz< T<U, V...> >
+{};
+
+baz< foo<int, short> > b1;
+baz< bar<int, short> > b2;
Index: testsuite/g++.dg/cpp0x/variadic87.C
===================================================================
--- testsuite/g++.dg/cpp0x/variadic87.C	(revision 0)
+++ testsuite/g++.dg/cpp0x/variadic87.C	(revision 0)
@@ -0,0 +1,23 @@
+// PR c++/33965
+// { dg-options -std=c++0x }
+template<typename T>
+struct foo
+{
+    static bool const value = false;
+};
+
+template<template<typename...> class T, typename... Args>
+struct foo<T<Args...> >
+{
+    static bool const value = true;
+};
+
+template<int I>
+struct int_
+{};
+
+int main()
+{
+  static_assert(foo<int_<0> >::value == false, 
+		"picked up partial specialization, but should not have");
+}
Index: testsuite/g++.dg/cpp0x/variadic84.C
===================================================================
--- testsuite/g++.dg/cpp0x/variadic84.C	(revision 0)
+++ testsuite/g++.dg/cpp0x/variadic84.C	(revision 0)
@@ -0,0 +1,26 @@
+// PR c++/32565
+// { dg-do compile }
+// { dg-options "-std=c++0x" }
+
+template<typename...> struct A1;
+template<template<int...> class T> struct A1<T<0> > {};
+template<typename...> struct A2;
+template<template<int...> class T> struct A2<T<0, 1> > {};
+template<typename...> struct A3;
+template<template<int, int...> class T> struct A3<T<0, 1> > {};
+template<typename...> struct A4;
+template<template<typename...> class T> struct A4<T<int> > {};
+template<typename...> struct A5;
+template<template<typename...> class T> struct A5<T<int, long> > {};
+template<typename...> struct A6;
+template<template<typename, typename...> class T> struct A6<T<int, long> > {};
+template<int> struct B1 {};
+template<int, int> struct B2 {};
+template<typename> struct B3 {};
+template<typename, typename> struct B4 {};
+A1<B1<0> > a1; // { dg-error "incomplete type" }
+A2<B2<0, 1> > a2; // { dg-error "incomplete type" }
+A3<B2<0, 1> > a3; // { dg-error "incomplete type" }
+A4<B3<int> > a4; // { dg-error "incomplete type" }
+A5<B4<int, long> > a5; // { dg-error "incomplete type" }
+A6<B4<int, long> > a6; // { dg-error "incomplete type" }
Index: testsuite/g++.dg/cpp0x/variadic85.C
===================================================================
--- testsuite/g++.dg/cpp0x/variadic85.C	(revision 0)
+++ testsuite/g++.dg/cpp0x/variadic85.C	(revision 0)
@@ -0,0 +1,10 @@
+// PR c++/32565
+// { dg-do compile }
+// { dg-options "-std=c++0x" }
+
+template<typename...> struct A1;
+template<template<int, int...> class T> struct A1<T<0, 1> > {};
+template<int, int, int...> struct B1 {};
+A1<B1<0, 1> > a1; // { dg-error "incomplete type" }
+template<int...> struct B2 {};
+A1<B2<0, 1> > a2; // { dg-error "incomplete type" }
Index: testsuite/g++.dg/template/ttp25.C
===================================================================
--- testsuite/g++.dg/template/ttp25.C	(revision 0)
+++ testsuite/g++.dg/template/ttp25.C	(revision 0)
@@ -0,0 +1,8 @@
+// { dg-do compile }
+template<typename T, template<T> class C>
+void f(T, C<5>);
+
+template<int N> struct X {};
+void g() {
+  f(5l, X<5>()); // { dg-error "no matching" }
+}
Index: testsuite/g++.dg/template/ttp26.C
===================================================================
--- testsuite/g++.dg/template/ttp26.C	(revision 0)
+++ testsuite/g++.dg/template/ttp26.C	(revision 0)
@@ -0,0 +1,8 @@
+// { dg-do compile }
+template<typename T, template<T> class C>
+void f(C<5>, T);
+
+template<int N> struct X {};
+void g() {
+  f(X<5>(), 5);
+}
Index: testsuite/g++.dg/template/ttp27.C
===================================================================
--- testsuite/g++.dg/template/ttp27.C	(revision 0)
+++ testsuite/g++.dg/template/ttp27.C	(revision 0)
@@ -0,0 +1,8 @@
+// { dg-do compile }
+template<typename T, template<T> class C>
+void f(C<5>, T);
+
+template<int N> struct X {};
+void g() {
+  f(X<5>(), 5l); // { dg-error "no matching" }
+}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]