diff mbox

C++ PATCH for c++/51239, 57138 (pack expansion and alias templates)

Message ID 51FD68B2.2050400@redhat.com
State New
Headers show

Commit Message

Jason Merrill Aug. 3, 2013, 8:31 p.m. UTC
DR 1430 seems to be being resolved to prohibit passing a pack expansion 
to a non-pack parameter of an alias template.  I disagree with this 
direction, but I'm going to go ahead and implement it for now.

The second patch implements DR 1286, which allows an alias template to 
be equivalent to another template if the type it aliases is the same as 
the type of the other template.

Tested x86_64-pc-linux-gnu, applying to trunk.

Comments

Gabriel Dos Reis Aug. 3, 2013, 10:43 p.m. UTC | #1
On Sat, Aug 3, 2013 at 3:31 PM, Jason Merrill <jason@redhat.com> wrote:
> DR 1430 seems to be being resolved to prohibit passing a pack expansion to a
> non-pack parameter of an alias template.  I disagree with this direction,
> but I'm going to go ahead and implement it for now.

I too disagree.  Do we need a NB-level comment to get a reconsideration?

> The second patch implements DR 1286, which allows an alias template to be
> equivalent to another template if the type it aliases is the same as the
> type of the other template.

this was part of the original intent.  I am mystified it was reversed.

Thanks for doing it!

-- Gaby
Jason Merrill Aug. 4, 2013, 12:11 a.m. UTC | #2
On 08/03/2013 06:43 PM, Gabriel Dos Reis wrote:
> On Sat, Aug 3, 2013 at 3:31 PM, Jason Merrill <jason@redhat.com> wrote:
>> DR 1430 seems to be being resolved to prohibit passing a pack expansion to a
>> non-pack parameter of an alias template.  I disagree with this direction,
>> but I'm going to go ahead and implement it for now.
>
> I too disagree.  Do we need a NB-level comment to get a reconsideration?

1430 is still in drafting, so an NB comment on the CD wouldn't make 
sense procedurally.  You might drop a note of concern to Mike Miller, 
though.

>> The second patch implements DR 1286, which allows an alias template to be
>> equivalent to another template if the type it aliases is the same as the
>> type of the other template.
>
> this was part of the original intent.  I am mystified it was reversed.

It wasn't reversed, particularly; Core just noticed that it wasn't 
specified by the language, so we fixed the example to match the 
normative text.

Jason
diff mbox

Patch

commit 74510935cc6b482407a30e53ee27f6f13b8b3d0f
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Apr 5 15:39:35 2013 -0400

    	DR 1430
    	PR c++/51239
    	* pt.c (pack_expansion_args_count): Rename from
    	any_pack_expanson_args_p.
    	(coerce_template_parms): Reject pack expansion to
    	non-pack template parameter of alias template.

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 82c72dd..bbaeb7d 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -6542,18 +6542,22 @@  coerce_template_parameter_pack (tree parms,
   return argument_pack;
 }
 
-/* Returns true if the template argument vector ARGS contains
-   any pack expansions, false otherwise.  */
+/* Returns the number of pack expansions in the template argument vector
+   ARGS.  */
 
-static bool
-any_pack_expanson_args_p (tree args)
+static int
+pack_expansion_args_count (tree args)
 {
   int i;
+  int count = 0;
   if (args)
     for (i = 0; i < TREE_VEC_LENGTH (args); ++i)
-      if (PACK_EXPANSION_P (TREE_VEC_ELT (args, i)))
-	return true;
-  return false;
+      {
+	tree elt = TREE_VEC_ELT (args, i);
+	if (elt && PACK_EXPANSION_P (elt))
+	  ++count;
+      }
+  return count;
 }
 
 /* Convert all template arguments to their appropriate types, and
@@ -6588,6 +6592,7 @@  coerce_template_parms (tree parms,
      subtract it from nparms to get the number of non-variadic
      parameters.  */
   int variadic_p = 0;
+  int variadic_args_p = 0;
   int post_variadic_parms = 0;
 
   if (args == error_mark_node)
@@ -6617,11 +6622,14 @@  coerce_template_parms (tree parms,
   if (!post_variadic_parms)
     inner_args = expand_template_argument_pack (inner_args);
 
+  /* Count any pack expansion args.  */
+  variadic_args_p = pack_expansion_args_count (inner_args);
+
   nargs = inner_args ? NUM_TMPL_ARGS (inner_args) : 0;
   if ((nargs > nparms && !variadic_p)
       || (nargs < nparms - variadic_p
 	  && require_all_args
-	  && !any_pack_expanson_args_p (inner_args)
+	  && !variadic_args_p
 	  && (!use_default_args
 	      || (TREE_VEC_ELT (parms, nargs) != error_mark_node
                   && !TREE_PURPOSE (TREE_VEC_ELT (parms, nargs))))))
@@ -6644,6 +6652,33 @@  coerce_template_parms (tree parms,
 
       return error_mark_node;
     }
+  /* We can't pass a pack expansion to a non-pack parameter of an alias
+     template (DR 1430).  */
+  else if (in_decl && DECL_ALIAS_TEMPLATE_P (in_decl)
+	   && variadic_args_p
+	   && nargs - variadic_args_p < nparms - variadic_p)
+    {
+      if (complain & tf_error)
+	{
+	  for (int i = 0; i < TREE_VEC_LENGTH (inner_args); ++i)
+	    {
+	      tree arg = TREE_VEC_ELT (inner_args, i);
+	      tree parm = TREE_VALUE (TREE_VEC_ELT (parms, i));
+
+	      if (PACK_EXPANSION_P (arg)
+		  && !template_parameter_pack_p (parm))
+		{
+		  error ("pack expansion argument for non-pack parameter "
+			 "%qD of alias template %qD", parm, in_decl);
+		  inform (DECL_SOURCE_LOCATION (parm), "declared here");
+		  goto found;
+		}
+	    }
+	  gcc_unreachable ();
+	found:;
+	}
+      return error_mark_node;
+    }
 
   /* We need to evaluate the template arguments, even though this
      template-id may be nested within a "sizeof".  */
diff --git a/gcc/testsuite/g++.dg/cpp0x/alias-decl-33.C b/gcc/testsuite/g++.dg/cpp0x/alias-decl-33.C
new file mode 100644
index 0000000..25781a4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/alias-decl-33.C
@@ -0,0 +1,14 @@ 
+// PR c++/51239
+// { dg-require-effective-target c++11 }
+
+template<class... x>
+class list{};
+template<class a, class... b>
+using tail=list<b...>;
+template <class...T>
+void f(tail<T...>);		// { dg-error "alias" }
+
+int main()
+{
+  f<int,int>({});
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/alias-decl-37.C b/gcc/testsuite/g++.dg/cpp0x/alias-decl-37.C
new file mode 100644
index 0000000..d6a3e12
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/alias-decl-37.C
@@ -0,0 +1,21 @@ 
+// PR c++/57138
+// { dg-do compile { target c++11 } }
+
+template <template <typename ... X> class T, typename ... Y>
+struct D
+{
+  template <typename ... Z>
+  using type = T <Y..., Z...>;	// { dg-error "pack expansion" }
+};
+template <typename T>
+class A {};
+template <typename X, typename Y>
+struct B;
+template <typename T>
+struct B <int, T>
+{
+  typedef A <T> type;
+};
+template <typename X, typename Y>
+using C = typename B <X, Y>::type;
+struct E : public D <C> {};

commit 42137e0d2c1a1dd949999080707a95b60266c6aa
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Aug 2 20:08:48 2013 -0400

    	DR 1286
    	* pt.c (get_underlying_template): New.
    	(convert_template_argument, lookup_template_class_1): Use it.

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index bbaeb7d..d03c1cf 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -5111,6 +5111,34 @@  alias_template_specialization_p (const_tree t)
 	  && DECL_ALIAS_TEMPLATE_P (TYPE_TI_TEMPLATE (t)));
 }
 
+/* Return either TMPL or another template that it is equivalent to under DR
+   1286: An alias that just changes the name of a template is equivalent to
+   the other template.  */
+
+static tree
+get_underlying_template (tree tmpl)
+{
+  gcc_assert (TREE_CODE (tmpl) == TEMPLATE_DECL);
+  while (DECL_ALIAS_TEMPLATE_P (tmpl))
+    {
+      tree result = DECL_ORIGINAL_TYPE (DECL_TEMPLATE_RESULT (tmpl));
+      if (TYPE_TEMPLATE_INFO (result))
+	{
+	  tree sub = TYPE_TI_TEMPLATE (result);
+	  if (PRIMARY_TEMPLATE_P (sub)
+	      && same_type_p (result, TREE_TYPE (sub)))
+	    {
+	      /* The alias type is equivalent to the pattern of the
+		 underlying template, so strip the alias.  */
+	      tmpl = sub;
+	      continue;
+	    }
+	}
+      break;
+    }
+  return tmpl;
+}
+
 /* Subroutine of convert_nontype_argument. Converts EXPR to TYPE, which
    must be a function or a pointer-to-function type, as specified
    in [temp.arg.nontype]: disambiguate EXPR if it is an overload set,
@@ -6319,6 +6347,9 @@  convert_template_argument (tree parm,
 	      tree parmparm = DECL_INNERMOST_TEMPLATE_PARMS (parm);
 	      tree argparm;
 
+	      /* Strip alias templates that are equivalent to another
+		 template.  */
+	      arg = get_underlying_template (arg);
               argparm = DECL_INNERMOST_TEMPLATE_PARMS (arg);
 
 	      if (coerce_template_template_parms (parmparm, argparm,
@@ -7177,6 +7208,13 @@  lookup_template_class_1 (tree d1, tree arglist, tree in_decl, tree context,
 
   complain &= ~tf_user;
 
+  /* An alias that just changes the name of a template is equivalent to the
+     other template, so if any of the arguments are pack expansions, strip
+     the alias to avoid problems with a pack expansion passed to a non-pack
+     alias template parameter (DR 1430).  */
+  if (pack_expansion_args_count (INNERMOST_TEMPLATE_ARGS (arglist)))
+    templ = get_underlying_template (templ);
+
   if (DECL_TEMPLATE_TEMPLATE_PARM_P (templ))
     {
       /* Create a new TEMPLATE_DECL and TEMPLATE_TEMPLATE_PARM node to store
diff --git a/gcc/testsuite/g++.dg/cpp0x/alias-decl-0.C b/gcc/testsuite/g++.dg/cpp0x/alias-decl-0.C
index c5760cf..f38f993 100644
--- a/gcc/testsuite/g++.dg/cpp0x/alias-decl-0.C
+++ b/gcc/testsuite/g++.dg/cpp0x/alias-decl-0.C
@@ -10,14 +10,13 @@  void g(X<Z>);
 void
 foo()
 {
-    // Below x and y don't have the same type, because Y and Z don't
-    // designate the same template ...
+    // Below x and y have the same type (DR 1286)
     X<Y> y; 
     X<Z> z;
 
-    // ... So these must fail to compile.
-    f(z);   // { dg-error "" }
-    g(y);   // { dg-error "" }
+    // ... So these must compile.
+    f(z);   // { dg-bogus "" }
+    g(y);   // { dg-bogus "" }
 }
 
 template<class> struct A0 {};
diff --git a/gcc/testsuite/g++.dg/cpp0x/alias-decl-33a.C b/gcc/testsuite/g++.dg/cpp0x/alias-decl-33a.C
new file mode 100644
index 0000000..a1c442e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/alias-decl-33a.C
@@ -0,0 +1,15 @@ 
+// PR c++/51239
+// { dg-require-effective-target c++11 }
+// This variant should work because tail is equivalent to list.
+
+template<class y, class... x>
+class list{};
+template<class a, class... b>
+using tail=list<a, b...>;
+template <class...T>
+void f(tail<T...>);
+
+int main()
+{
+  f<int,int>({});
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/alias-decl-dr1286.C b/gcc/testsuite/g++.dg/cpp0x/alias-decl-dr1286.C
new file mode 100644
index 0000000..0c545c7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/alias-decl-dr1286.C
@@ -0,0 +1,13 @@ 
+// DR 1286: An alias template can be equivalent to an underlying template.
+// { dg-do compile { target c++11 } }
+
+template <class T, class U> struct same;
+template <class T> struct same<T,T> {};
+
+template <class T> struct A {};
+template <class T> using B = A<T>;
+
+template <template <class> class T> class C {};
+
+void f(C<B>) { }	    // { dg-final { scan-assembler "_Z1f1CI1AE" } }
+same<C<A>, C<B> > s;
diff --git a/gcc/testsuite/g++.dg/cpp0x/alias-decl-dr1286a.C b/gcc/testsuite/g++.dg/cpp0x/alias-decl-dr1286a.C
new file mode 100644
index 0000000..1780c9a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/alias-decl-dr1286a.C
@@ -0,0 +1,60 @@ 
+// DR 1286
+// { dg-do compile { target c++11 } }
+
+template <class,class> struct same;
+template <class T> struct same<T,T> {};
+
+template <class,class> struct different {};
+template <class T> struct different<T,T>;
+
+template<typename T, typename U = T> struct A;
+template<template <class...> class> struct X;
+
+// equivalent to A
+template<typename V, typename W>
+using B = A<V, W>;
+
+same<X<A>,X<B>> s1;
+
+// not equivalent to A: not all parameters used
+template<typename V, typename W>
+using C = A<V>;
+
+different<X<A>,X<C>> d1;
+
+// not equivalent to A: different number of parameters
+template<typename V>
+using D = A<V>;
+
+different<X<A>,X<D>> d2;
+
+// not equivalent to A: template-arguments in wrong order
+template<typename V, typename W>
+using E = A<W, V>;
+
+different<X<A>,X<E>> d3;
+
+// equivalent to A: default arguments not considered
+template<typename V, typename W = int>
+using F = A<V, W>;
+
+same<X<A>,X<F>> s2;
+
+// equivalent to A and B
+template<typename V, typename W>
+using G = A<V, W>;
+
+same<X<A>,X<G>> s3;
+same<X<B>,X<G>> s3b;
+
+// equivalent to E
+template<typename V, typename W>
+using H = E<V, W>;
+
+same<X<E>,X<H>> s4;
+
+// not equivalent to A: argument not identifier
+template<typename V, typename W>
+using I = A<V, typename W::type>;
+
+different<X<A>,X<I>> d4;