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++0x PATCH] Extending variadic template template parameters (N2488)


This patch, to be applied on top of my fixes for template template
parameters (http://gcc.gnu.org/ml/gcc-patches/2007-12/msg00709.html),
implements the extension to variadic template template parameters
described in N2488, available here:

  http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2488.pdf

The extension is a simple one, relaxing the restrictions on which
template template parameters can bind to which template arguments when
the template template parameter's template parameter list contains a
parameter pack. Read the paper for the details; the implementation
itself is very straightforward, and the patch only looks non-trivial
because I separated out the loop body from
coerce_template_template_parms into its own function
(coerce_template_template_parm).

This is one of those patches that pushes on the rules a bit. There are
obvious reasons not to accept it: we're in stage 3, so we shouldn't be
changing anything that isn't a bug fix. The extension in question
hasn't yet been accepted into C++0x (it will be discussed in Bellevue
this February), so it could be viewed as a GNU extension (if one is
being pedantic).

That said, rejecting this patch is absolutely the wrong thing to do :)

>From the perspective of C++0x, it's bad to have a feature implemented
in a released compiler that is known to have different semantics than
what we expect in C++0x. People trying out the feature will
misunderstand the details of the feature, misinformation will be
distributed, rebuked, reiterated, etc. Yes, features will evolve, and
inevitably we'll ship a C++0x feature with the wrong semantics. But
there's a difference between knowingly shipping a wrong compiler and
accidentally shipping a wrong compiler; we know this is coming, and
it's easy to deal with now.

So, putting this into mainline now puts us a little ahead of the
curve, because it hasn't been accepted yet. However, wording for this
small extension is available (see N2488), the extension itself is
strongly motivated by actual use cases (again, see N2488) and
extensively tested with those same use cases, and there has been a
significant amount of support for this extension already (with no
disagreements about it, at least thus far). Given these factors, I
would be shocked---shocked!---if it did not go into C++0x this
February at the Bellevue meeting. Remember: variadic templates
themselves went through the C++ committee in a single meeting. It
would be a shame if the entire GCC 4.3 series missed out on this
change (thereby hobbling C++0x-enabled libraries like the updated
Boost.MPL and Proto) by a few weeks.

Tested i686-pc-linux-gnu.

  - Doug

2007-12-15  Douglas Gregor  <doug.gregor@gmail.com>

	* pt.c (coerce_template_template_parm): Moved the body of the loop
	of coerce_template_template_parms here, to make iteration over a
	template argument pack simpler.
	Also, allow matching of a template parameter pack in the template
	template parameter to a template parameter in the template
	template argument.
	(coerce_template_template_parms): Deal with variadic template
	template parameters. Use coerce_template_template_parm.
	(unify): Make sure we coerce the template template argument's
	template arguments to the template template parameter's template
	parameters, not the other way around.

2007-12-15  Douglas Gregor  <doug.gregor@gmail.com>

	* g++.dg/cpp0x/variadic84.C: Update to reflect the change in
	variadic template template parameter binding semantics.
	* g++.dg/cpp0x/variadic85.C: Ditto.
	* g++.dg/cpp0x/variadic88.C: New.
	* g++.dg/cpp0x/variadic89.C: New.
	* g++.dg/cpp0x/variadic90.C: New.
	* g++.dg/cpp0x/variadic-ex14.C: Update to reflect the change in
	variadic template template parameter binding semantics.
	* g++.dg/cpp0x/variadic-lambda.C: New.
diff -up cp/pt.c /home/dgregor/Projects/mainlinegcc/gcc/cp/pt.c
--- cp/pt.c	2007-12-15 04:11:50.000000000 -0500
+++ /home/dgregor/Projects/mainlinegcc/gcc/cp/pt.c	2007-12-15 04:02:14.000000000 -0500
@@ -4653,6 +4653,77 @@ convert_nontype_argument (tree type, tre
   return expr;
 }
 
+/* Subroutine of coerce_template_template_parms, which returns 1 if
+   PARM_PARM and ARG_PARM match using the rule for the template
+   parameters of template template parameters. Both PARM and ARG are
+   template parameters; the rest of the arguments are the same as for
+   coerce_template_template_parms.
+ */
+static int
+coerce_template_template_parm (tree parm,
+                              tree arg,
+                              tsubst_flags_t complain,
+                              tree in_decl,
+                              tree outer_args)
+{
+  if (arg == NULL_TREE || arg == error_mark_node
+      || parm == NULL_TREE || parm == error_mark_node)
+    return 0;
+  
+  if (TREE_CODE (arg) != TREE_CODE (parm))
+    return 0;
+  
+  switch (TREE_CODE (parm))
+    {
+    case TEMPLATE_DECL:
+      /* We encounter instantiations of templates like
+	 template <template <template <class> class> class TT>
+	 class C;  */
+      {
+	tree parmparm = DECL_INNERMOST_TEMPLATE_PARMS (parm);
+	tree argparm = DECL_INNERMOST_TEMPLATE_PARMS (arg);
+	
+	if (!coerce_template_template_parms
+	    (parmparm, argparm, complain, in_decl, outer_args))
+	  return 0;
+      }
+      /* Fall through.  */
+      
+    case TYPE_DECL:
+      if (TEMPLATE_TYPE_PARAMETER_PACK (TREE_TYPE (arg))
+	  && !TEMPLATE_TYPE_PARAMETER_PACK (TREE_TYPE (parm)))
+	/* Argument is a parameter pack but parameter is not.  */
+	return 0;
+      break;
+      
+    case PARM_DECL:
+      /* The tsubst call is used to handle cases such as
+	 
+           template <int> class C {};
+	   template <class T, template <T> class TT> class D {};
+	   D<int, C> d;
+
+	 i.e. the parameter list of TT depends on earlier parameters.  */
+      if (!dependent_type_p (TREE_TYPE (arg))
+	  && !same_type_p
+	        (tsubst (TREE_TYPE (parm), outer_args, complain, in_decl),
+		 TREE_TYPE (arg)))
+	return 0;
+      
+      if (TEMPLATE_PARM_PARAMETER_PACK (DECL_INITIAL (arg))
+	  && !TEMPLATE_PARM_PARAMETER_PACK (DECL_INITIAL (parm)))
+	/* Argument is a parameter pack but parameter is not.  */
+	return 0;
+      
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  return 1;
+}
+
 
 /* Return 1 if PARM_PARMS and ARG_PARMS matches using rule for
    template template parameters.  Both PARM_PARMS and ARG_PARMS are
@@ -4675,6 +4746,7 @@ coerce_template_template_parms (tree par
 {
   int nparms, nargs, i;
   tree parm, arg;
+  int variadic_p = 0;
 
   gcc_assert (TREE_CODE (parm_parms) == TREE_VEC);
   gcc_assert (TREE_CODE (arg_parms) == TREE_VEC);
@@ -4682,10 +4754,37 @@ coerce_template_template_parms (tree par
   nparms = TREE_VEC_LENGTH (parm_parms);
   nargs = TREE_VEC_LENGTH (arg_parms);
 
-  if (nargs != nparms)
+  /* Determine whether we have a parameter pack at the end of the
+     template template parameter's template parameter list.  */
+  if (TREE_VEC_ELT (parm_parms, nparms - 1) != error_mark_node)
+    {
+      parm = TREE_VALUE (TREE_VEC_ELT (parm_parms, nparms - 1));
+      
+      switch (TREE_CODE (parm))
+        {
+        case TEMPLATE_DECL:
+        case TYPE_DECL:
+          if (TEMPLATE_TYPE_PARAMETER_PACK (TREE_TYPE (parm)))
+            variadic_p = 1;
+          break;
+	  
+        case PARM_DECL:
+          if (TEMPLATE_PARM_PARAMETER_PACK (DECL_INITIAL (parm)))
+            variadic_p = 1;
+          break;
+	  
+        default:
+          gcc_unreachable ();
+        }
+    }
+ 
+  if (nargs != nparms
+      && !(variadic_p && nargs >= nparms - 1))
     return 0;
 
-  for (i = 0; i < nparms; ++i)
+  /* Check all of the template parameters except the parameter pack at
+     the end (if any).  */
+  for (i = 0; i < nparms - variadic_p; ++i)
     {
       if (TREE_VEC_ELT (parm_parms, i) == error_mark_node
           || TREE_VEC_ELT (arg_parms, i) == error_mark_node)
@@ -4694,60 +4793,35 @@ coerce_template_template_parms (tree par
       parm = TREE_VALUE (TREE_VEC_ELT (parm_parms, i));
       arg = TREE_VALUE (TREE_VEC_ELT (arg_parms, i));
 
-      if (arg == NULL_TREE || arg == error_mark_node
-	  || parm == NULL_TREE || parm == error_mark_node)
-	return 0;
-
-      if (TREE_CODE (arg) != TREE_CODE (parm))
+      if (!coerce_template_template_parm (parm, arg, complain, in_decl,
+                                          outer_args))
 	return 0;
 
-      switch (TREE_CODE (parm))
-	{
-	case TEMPLATE_DECL:
-	  /* We encounter instantiations of templates like
-	       template <template <template <class> class> class TT>
-	       class C;  */
-	  {
-	    tree parmparm = DECL_INNERMOST_TEMPLATE_PARMS (parm);
-	    tree argparm = DECL_INNERMOST_TEMPLATE_PARMS (arg);
-
-	    if (!coerce_template_template_parms
-		(parmparm, argparm, complain, in_decl, outer_args))
-	      return 0;
-	  }
-	  /* Fall through.  */
-
-	case TYPE_DECL:
-	  if (TEMPLATE_TYPE_PARAMETER_PACK (TREE_TYPE (parm))
-	      != TEMPLATE_TYPE_PARAMETER_PACK (TREE_TYPE (arg)))
-	    /* One is a parameter pack, the other is not.  */
-	    return 0;
-	  break;
-
-	case PARM_DECL:
-	  /* The tsubst call is used to handle cases such as
+    }
 
-	       template <int> class C {};
-	       template <class T, template <T> class TT> class D {};
-	       D<int, C> d;
-
-	     i.e. the parameter list of TT depends on earlier parameters.  */
-	  if (!dependent_type_p (TREE_TYPE (arg))
-	      && !same_type_p
-		    (tsubst (TREE_TYPE (parm), outer_args, complain, in_decl),
-			     TREE_TYPE (arg)))
-	    return 0;
+  if (variadic_p)
+    {
+      /* Check each of the template parameters in the template
+	 argument against the template parameter pack at the end of
+	 the template template parameter.  */
+      if (TREE_VEC_ELT (parm_parms, i) == error_mark_node)
+	return 0;
 
-	  if (TEMPLATE_PARM_PARAMETER_PACK (DECL_INITIAL (parm))
-	      != TEMPLATE_PARM_PARAMETER_PACK (DECL_INITIAL (arg)))
-	    /* One is a parameter pack, the other is not.  */
-	    return 0;
-	  break;
+      parm = TREE_VALUE (TREE_VEC_ELT (parm_parms, i));
 
-	default:
-	  gcc_unreachable ();
-	}
+      for (; i < nargs; ++i)
+        {
+          if (TREE_VEC_ELT (arg_parms, i) == error_mark_node)
+            continue;
+ 
+          arg = TREE_VALUE (TREE_VEC_ELT (arg_parms, i));
+ 
+          if (!coerce_template_template_parm (parm, arg, complain, in_decl,
+                                              outer_args))
+            return 0;
+        }
     }
+
   return 1;
 }
 
@@ -12837,8 +12911,9 @@ unify (tree tparms, tree targs, tree par
 	  {
 	    tree parmvec = TYPE_TI_ARGS (parm);
 	    tree argvec = INNERMOST_TEMPLATE_ARGS (TYPE_TI_ARGS (arg));
-	    tree argtmplvec
-	      = DECL_INNERMOST_TEMPLATE_PARMS (TYPE_TI_TEMPLATE (arg));
+	    tree parm_parms 
+	      = DECL_INNERMOST_TEMPLATE_PARMS 
+	          (TEMPLATE_TEMPLATE_PARM_TEMPLATE_DECL (parm));
 	    int i, len;
 	    int parm_variadic_p = 0;
 
@@ -12854,7 +12929,8 @@ unify (tree tparms, tree targs, tree par
 	       (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,
+	    if (coerce_template_parms (parm_parms, 
+				       argvec,
 				       TYPE_TI_TEMPLATE (parm),
 				       tf_none,
 				       /*require_all_args=*/false,
diff -up -N testsuite/g++.dg/cpp0x/variadic84.C /home/dgregor/Projects/mainlinegcc/gcc/testsuite/g++.dg/cpp0x/variadic84.C
--- testsuite/g++.dg/cpp0x/variadic84.C	2007-12-15 04:11:50.000000000 -0500
+++ /home/dgregor/Projects/mainlinegcc/gcc/testsuite/g++.dg/cpp0x/variadic84.C	2007-12-15 04:02:56.000000000 -0500
@@ -18,9 +18,9 @@ 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" }
+A1<B1<0> > a1;
+A2<B2<0, 1> > a2;
+A3<B2<0, 1> > a3;
+A4<B3<int> > a4;
+A5<B4<int, long> > a5;
+A6<B4<int, long> > a6;
diff -up -N testsuite/g++.dg/cpp0x/variadic85.C /home/dgregor/Projects/mainlinegcc/gcc/testsuite/g++.dg/cpp0x/variadic85.C
--- testsuite/g++.dg/cpp0x/variadic85.C	2007-12-15 04:11:50.000000000 -0500
+++ /home/dgregor/Projects/mainlinegcc/gcc/testsuite/g++.dg/cpp0x/variadic85.C	2007-12-15 04:05:50.000000000 -0500
@@ -5,6 +5,6 @@
 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" }
+A1<B1<0, 1> > a1;
 template<int...> struct B2 {};
 A1<B2<0, 1> > a2; // { dg-error "incomplete type" }
diff -up -N testsuite/g++.dg/cpp0x/variadic88.C /home/dgregor/Projects/mainlinegcc/gcc/testsuite/g++.dg/cpp0x/variadic88.C
--- testsuite/g++.dg/cpp0x/variadic88.C	1969-12-31 19:00:00.000000000 -0500
+++ /home/dgregor/Projects/mainlinegcc/gcc/testsuite/g++.dg/cpp0x/variadic88.C	2007-12-15 03:47:22.000000000 -0500
@@ -0,0 +1,23 @@
+// { dg-options "-std=c++0x" }
+
+template<template<typename...> class TT>
+TT<int, float, double> foo(TT<int, float>)
+{
+  return TT<int, float, double>();
+}
+
+template<typename T>
+int& foo(T) 
+{ 
+  static int i = 0; return i; 
+}
+
+template<typename T, typename U>
+struct pair {};
+
+void bar()
+{
+  pair<int, float> p;
+  int& i = foo(p);
+}
+
diff -up -N testsuite/g++.dg/cpp0x/variadic89.C /home/dgregor/Projects/mainlinegcc/gcc/testsuite/g++.dg/cpp0x/variadic89.C
--- testsuite/g++.dg/cpp0x/variadic89.C	1969-12-31 19:00:00.000000000 -0500
+++ /home/dgregor/Projects/mainlinegcc/gcc/testsuite/g++.dg/cpp0x/variadic89.C	2007-12-15 03:47:22.000000000 -0500
@@ -0,0 +1,14 @@
+// { dg-options "-std=c++0x" }
+// Contributed by Eric Niebler
+template<typename T, typename U>
+struct pair
+{};
+
+template<typename T>
+struct test;
+
+template<template<typename...> class T, typename... Args>
+struct test<T<Args...> >
+{};
+
+test<pair<int, double> > t;
diff -up -N testsuite/g++.dg/cpp0x/variadic90.C /home/dgregor/Projects/mainlinegcc/gcc/testsuite/g++.dg/cpp0x/variadic90.C
--- testsuite/g++.dg/cpp0x/variadic90.C	1969-12-31 19:00:00.000000000 -0500
+++ /home/dgregor/Projects/mainlinegcc/gcc/testsuite/g++.dg/cpp0x/variadic90.C	2007-12-15 03:47:22.000000000 -0500
@@ -0,0 +1,8 @@
+// { dg-options "-std=c++0x" }
+
+template<template<typename...> class TT>
+struct X { };
+
+template<typename T, typename U> struct pair { };
+
+X<pair> x;
diff -up -N testsuite/g++.dg/cpp0x/variadic-ex14.C /home/dgregor/Projects/mainlinegcc/gcc/testsuite/g++.dg/cpp0x/variadic-ex14.C
--- testsuite/g++.dg/cpp0x/variadic-ex14.C	2007-12-15 04:11:12.000000000 -0500
+++ /home/dgregor/Projects/mainlinegcc/gcc/testsuite/g++.dg/cpp0x/variadic-ex14.C	2007-12-15 03:34:33.000000000 -0500
@@ -14,10 +14,6 @@ X<B> xB; // { dg-error "mismatch" }
 X<C> xC; // { dg-error "mismatch" }
 // { dg-error "expected a template" "" { target *-*-* } 14 }
 // { dg-error "invalid type" "" { target *-*-* } 14 }
-Y<A> yA; // { dg-error "mismatch" }
-// { dg-error "expected a template" "" { target *-*-* } 17 }
-// { dg-error "invalid type" "" { target *-*-* } 17 }
-Y<B> yB; // { dg-error "mismatch" }
-// { dg-error "expected a template" "" { target *-*-* } 20 }
-// { dg-error "invalid type" "" { target *-*-* } 20 }
+Y<A> yA;
+Y<B> yB;
 Y<C> yC; // okay
diff -up -N testsuite/g++.dg/cpp0x/variadic-lambda.C /home/dgregor/Projects/mainlinegcc/gcc/testsuite/g++.dg/cpp0x/variadic-lambda.C
--- testsuite/g++.dg/cpp0x/variadic-lambda.C	1969-12-31 19:00:00.000000000 -0500
+++ /home/dgregor/Projects/mainlinegcc/gcc/testsuite/g++.dg/cpp0x/variadic-lambda.C	2007-12-15 03:47:22.000000000 -0500
@@ -0,0 +1,79 @@
+// { dg-options "-std=c++0x" }
+
+struct int_placeholder;
+
+template<typename T>
+struct do_replace
+{
+  typedef T type;
+};
+
+template<>
+struct do_replace<int_placeholder>
+{
+  typedef int type;
+};
+
+template<typename T> struct lambdalike
+{
+  typedef T type;
+};
+
+template<template<typename...> class TT, typename... Args>
+struct lambdalike<TT<Args...> > {
+  typedef TT<typename do_replace<Args>::type...> type;
+};
+
+
+template<typename T, typename U>
+struct is_same
+{
+  static const bool value = false;
+};
+
+template<typename T>
+struct is_same<T, T>
+{
+  static const bool value = true;
+};
+
+template<typename... Elements> struct tuple;
+template<typename T1, typename T2> struct pair;
+
+static_assert(is_same<lambdalike<tuple<float, int_placeholder, double>>::type,
+		      tuple<float, int, double>>::value,
+	      "MPL lambda-like replacement on tuple");
+static_assert(is_same<lambdalike<pair<float, int_placeholder>>::type,
+		      pair<float, int>>::value,
+	      "MPL lambda-like replacement on pair");
+
+
+struct _1 {};
+
+template<typename Arg0, typename Lambda>
+struct eval
+{
+    typedef Lambda type;
+};
+
+template<typename Arg0>
+struct eval<Arg0, _1>
+{
+    typedef Arg0 type;
+};
+
+template<typename Arg0, template<typename...> class T, typename... Pack>
+struct eval<Arg0, T<Pack...> >
+{
+    typedef T< typename eval<Arg0, Pack>::type... > type;
+};
+
+static_assert(is_same<eval<int, tuple<float, _1, double>>::type,
+	              tuple<float, int, double>>::value, "eval tuple");
+static_assert(is_same<eval<int, pair<_1, double>>::type,
+	              pair<int, double>>::value, "eval pair");
+static_assert(is_same<eval<int, 
+	                   tuple<pair<_1, _1>, pair<float, float>,
+ 	                         pair<double, _1>>>::type,
+	      tuple<pair<int, int>, pair<float, float>, pair<double, int>>>::value,
+	      "recursive eval");

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