Patchwork c++/48320 - Template parameter packs cannot be expanded in

login
register
mail settings
Submitter Dodji Seketeli
Date Sept. 12, 2011, 9 p.m.
Message ID <m3zki9iihh.fsf@redhat.com>
Download mbox | patch
Permalink /patch/114419/
State New
Headers show

Comments

Dodji Seketeli - Sept. 12, 2011, 9 p.m.
Hello,

fixup_template_parms fixes up template parameters by substituting the
result of current_template_args into each template parameter, from
left to right.

In the example the patch, when the compiler comes to the point of
substituting into the default argument of template parameter Result,
it is exposed to a subtlety that is twofold.

1/ The parameter pack Indices is now represented by a
TEMPLATE_PARM_INDEX, as a result of substituting into the PARM_DECL that
was representing it prior to going through fixup_template_parm.

2/ The non-type template parameter Indices, when passed to
template_parm_to_arg (indirectly called by current_template_args) is
wrapped into an argument pack containing the pack expansion of
Indices.

Normally, the wrapped form of Indices shouldn't escape
tsubst_pack_expansion; this function should detect it, figure out that
we are basically substituting the parameter pack Indices into itself
and do the right thing.

The problem in this PR is that 1/ makes it so that what the
representation of Indices that is wrapped in 2/ is not a PARM_DECL but
rather a TEMPLATE_PARM_INDEX.  But the code of tsubst_pack_expansion
was expecting a PARM_DECL in that wrapped form.

The patch below factorizes out the code (that detects the wrapped
form) from tsubst_pack_expansion, and expands it a little bit to make
it be aware of 1/ and 2/ combined.

Bootstrapped and tested on x86_64-unknown-linux-gnu against trunk.
I'll launch a bootstrap & test on 4.6 as well.

From: Dodji Seketeli <dodji@redhat.com>  
Date: Mon, 12 Sep 2011 20:17:33 +0200
Subject: [PATCH] c++/48320 - Template parameter packs cannot be expanded in
 default template arguments

gcc/cp/

	PR c++/48320
	* pt.c (template_parameter_pack_p):  Support TEMPLATE_PARM_INDEX
	nodes.  Add a comment.
	(arg_from_parm_pack_p):  New static function, factorized out from
	tsubst_pack_expansion and extended to support non-type parameter
	packs represented with TEMPLATE_PARM_INDEX nodes.
	(tsubst_pack_expansion): Use arg_from_parm_pack_p.

gcc/testsuite/

	PR c++/48320
	* g++.dg/cpp0x/variadic116.C: New test case.
---
 gcc/cp/pt.c                              |  115 ++++++++++++++++++------------
 gcc/testsuite/g++.dg/cpp0x/variadic116.C |   32 ++++++++
 2 files changed, 100 insertions(+), 47 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/variadic116.C
Jason Merrill - Sept. 12, 2011, 10:09 p.m.
OK.

Jason

Patch

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 9a5e3dd..1407364 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -203,6 +203,7 @@  static void append_type_to_template_for_access_check_1 (tree, tree, tree,
 static tree listify (tree);
 static tree listify_autos (tree, tree);
 static tree template_parm_to_arg (tree t);
+static bool arg_from_parm_pack_p (tree, tree);
 static tree current_template_args (void);
 static tree fixup_template_type_parm_type (tree, int);
 static tree fixup_template_parm_index (tree, tree, int);
@@ -2741,12 +2742,15 @@  template_parameter_pack_p (const_tree parm)
   if (TREE_CODE (parm) == PARM_DECL)
     return (DECL_TEMPLATE_PARM_P (parm) 
             && TEMPLATE_PARM_PARAMETER_PACK (DECL_INITIAL (parm)));
+  if (TREE_CODE (parm) == TEMPLATE_PARM_INDEX)
+    return TEMPLATE_PARM_PARAMETER_PACK (parm);
 
   /* If this is a list of template parameters, we could get a
      TYPE_DECL or a TEMPLATE_DECL.  */ 
   if (TREE_CODE (parm) == TYPE_DECL || TREE_CODE (parm) == TEMPLATE_DECL)
     parm = TREE_TYPE (parm);
 
+  /* Otherwise it must be a type template parameter.  */
   return ((TREE_CODE (parm) == TEMPLATE_TYPE_PARM
 	   || TREE_CODE (parm) == TEMPLATE_TEMPLATE_PARM)
 	  && TEMPLATE_TYPE_PARAMETER_PACK (parm));
@@ -4005,6 +4009,63 @@  template_parm_to_arg (tree t)
   return t;
 }
 
+/* This function returns TRUE if PARM_PACK is a template parameter
+   pack and if ARG_PACK is what template_parm_to_arg returned when
+   passed PARM_PACK.  */
+
+static bool
+arg_from_parm_pack_p (tree arg_pack, tree parm_pack)
+{
+  /* For clarity in the comments below let's use the representation
+     argument_pack<elements>' to denote an argument pack and its
+     elements.
+
+     In the 'if' block below, we want to detect cases where
+     ARG_PACK is argument_pack<PARM_PACK...>.  I.e, we want to
+     check if ARG_PACK is an argument pack which sole element is
+     the expansion of PARM_PACK.  That argument pack is typically
+     created by template_parm_to_arg when passed a parameter
+     pack.  */
+
+  if (arg_pack
+      && TREE_VEC_LENGTH (ARGUMENT_PACK_ARGS (arg_pack)) == 1
+      && PACK_EXPANSION_P (TREE_VEC_ELT (ARGUMENT_PACK_ARGS (arg_pack), 0)))
+    {
+      tree expansion = TREE_VEC_ELT (ARGUMENT_PACK_ARGS (arg_pack), 0);
+      tree pattern = PACK_EXPANSION_PATTERN (expansion);
+      /* So we have an argument_pack<P...>.  We want to test if P
+	 is actually PARM_PACK.  We will not use cp_tree_equal to
+	 test P and PARM_PACK because during type fixup (by
+	 fixup_template_parm) P can be a pre-fixup version of a
+	 type and PARM_PACK be its post-fixup version.
+	 cp_tree_equal would consider them as different even
+	 though we would want to consider them compatible for our
+	 precise purpose here.
+
+	 Thus we are going to consider that P and PARM_PACK are
+	 compatible if they have the same DECL.  */
+      if ((/* If ARG_PACK is a type parameter pack named by the
+	      same DECL as parm_pack ...  */
+	   (TYPE_P (pattern)
+	    && TYPE_P (parm_pack)
+	    && TYPE_NAME (pattern) == TYPE_NAME (parm_pack))
+	   /* ... or if PARM_PACK is a non-type parameter named by the
+	      same DECL as ARG_PACK.  Note that PARM_PACK being a
+	      non-type parameter means it's either a PARM_DECL or a
+	      TEMPLATE_PARM_INDEX.  */
+	   || (TREE_CODE (pattern) == TEMPLATE_PARM_INDEX
+	       && ((TREE_CODE (parm_pack) == PARM_DECL
+		    && (TEMPLATE_PARM_DECL (pattern)
+			== TEMPLATE_PARM_DECL (DECL_INITIAL (parm_pack))))
+		   || (TREE_CODE (parm_pack) == TEMPLATE_PARM_INDEX
+		       && (TEMPLATE_PARM_DECL (pattern)
+			   == TEMPLATE_PARM_DECL (parm_pack))))))
+	  && template_parameter_pack_p (pattern))
+	return true;
+    }
+  return false;
+}
+
 /* Within the declaration of a template, return all levels of template
    parameters that apply.  The template parameters are represented as
    a TREE_VEC, in the form documented in cp-tree.h for template
@@ -9105,53 +9166,13 @@  tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
 	  return result;
 	}
 
-      /* For clarity in the comments below let's use the
-	 representation 'argument_pack<elements>' to denote an
-	 argument pack and its elements.
-
-	 In the 'if' block below, we want to detect cases where
-	 ARG_PACK is argument_pack<PARM_PACK...>.  I.e, we want to
-	 check if ARG_PACK is an argument pack which sole element is
-	 the expansion of PARM_PACK.  That argument pack is typically
-	 created by template_parm_to_arg when passed a parameter
-	 pack.  */
-      if (arg_pack
-          && TREE_VEC_LENGTH (ARGUMENT_PACK_ARGS (arg_pack)) == 1
-          && PACK_EXPANSION_P (TREE_VEC_ELT (ARGUMENT_PACK_ARGS (arg_pack), 0)))
-        {
-          tree expansion = TREE_VEC_ELT (ARGUMENT_PACK_ARGS (arg_pack), 0);
-          tree pattern = PACK_EXPANSION_PATTERN (expansion);
-	  /* So we have an argument_pack<P...>.  We want to test if P
-	     is actually PARM_PACK.  We will not use cp_tree_equal to
-	     test P and PARM_PACK because during type fixup (by
-	     fixup_template_parm) P can be a pre-fixup version of a
-	     type and PARM_PACK be its post-fixup version.
-	     cp_tree_equal would consider them as different even
-	     though we would want to consider them compatible for our
-	     precise purpose here.
-
-	     Thus we are going to consider that P and PARM_PACK are
-	     compatible if they have the same DECL.  */
-	  if ((/* If ARG_PACK is a type parameter pack named by the
-		  same DECL as parm_pack ...  */
-	       (TYPE_P (pattern)
-		&& TYPE_P (parm_pack)
-		&& TYPE_NAME (pattern) == TYPE_NAME (parm_pack))
-	       /* ... or if ARG_PACK is a non-type parameter
-		  named by the same DECL as parm_pack ...  */
-	       || (TREE_CODE (pattern) == TEMPLATE_PARM_INDEX
-		   && TREE_CODE (parm_pack) == PARM_DECL
-		   && TEMPLATE_PARM_DECL (pattern)
-		   == TEMPLATE_PARM_DECL (DECL_INITIAL (parm_pack))))
-	      && template_parameter_pack_p (pattern))
-            /* ... then the argument pack that the parameter maps to
-               is just an expansion of the parameter itself, such as
-               one would find in the implicit typedef of a class
-               inside the class itself.  Consider this parameter
-               "unsubstituted", so that we will maintain the outer
-               pack expansion.  */
-            arg_pack = NULL_TREE;
-        }
+      if (arg_from_parm_pack_p (arg_pack, parm_pack))
+	/* The argument pack that the parameter maps to is just an
+	   expansion of the parameter itself, such as one would find
+	   in the implicit typedef of a class inside the class itself.
+	   Consider this parameter "unsubstituted", so that we will
+	   maintain the outer pack expansion.  */
+	arg_pack = NULL_TREE;
           
       if (arg_pack)
         {
diff --git a/gcc/testsuite/g++.dg/cpp0x/variadic116.C b/gcc/testsuite/g++.dg/cpp0x/variadic116.C
new file mode 100644
index 0000000..079d751
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/variadic116.C
@@ -0,0 +1,32 @@ 
+// Origin: PR c++/48320
+// { dg-options -std=c++0x }
+
+template<class... T>
+struct tuple
+{
+    typedef int type;
+};
+
+template<int... Indices>
+struct indices
+{
+};
+
+template<unsigned i, class Tuple>
+struct tuple_element
+{
+    typedef Tuple type;
+};
+
+template<class Tuple,
+         int... Indices,
+         class Result = tuple<typename tuple_element<Indices, Tuple>::type...> >
+Result
+f(Tuple&&, indices<Indices...>);
+
+
+void
+foo()
+{
+    f(tuple<int, char, unsigned> (), indices<2, 1, 0> ());
+}