diff mbox

PR c++/53609 - Wrong argument deduction for pack expansion in argument pack

Message ID 87vccjclke.fsf@redhat.com
State New
Headers show

Commit Message

Dodji Seketeli Dec. 3, 2012, 1:27 p.m. UTC
Jason Merrill <jason@redhat.com> writes:

> It seems like your new code is a generalization of the old code for
> handling substitution of a pack for itself (arg_from_parm_pack and
> such) and the code for handling other packs with a single pack
> expansion argument, and should replace those rather than adding on.

I guess that can of worms is now open.  :-)

I tried to think about how to do this "properly".  What I came up with
in the patch below is to properly organize tsubst_pack_expansion in
two parts that are already screaming to emerge, IMHO.  The first part
maps each parameter pack with its matching argument pack, and does
only that.  The second part walks that map and builds the list of Ei
where each Ei is the result of substituting elements at index I in the
argument packs into the pattern of the expansion passed to
tsubst_pack_expansion.

It's only the latter part knows how to deal with special cases like:
  - some parameter packs having no matching argument pack
  - parameter packs having matching argument packs element that are
  expansions
  - etc

Is that what you had in mind?

> The solution that if at a certain index all the packs have expansion
> arguments then the substitution produces a pack expansion seems right
> to me, but if one pack has an expansion and another pack has a normal
> argument, we can't do the substitution and need to fall back on the
> PACK_EXPANSION_EXTRA_ARGS mechanism.

Right, this is hopefully what this updated patch implements.

> > +set_arg_pack_select_index_for_pack_expansion (tree aps,
> > +					      int i,
> > +					      tree arg_pack)
> > +{
> > +  if (any_non_real_argument_pack_element_p (arg_pack))
> 
> I don't think we care if *any* element is an expansion (and please
> talk about expansions rather than "non-real elements").  What we care
> about is whether the i'th element is an expansion.  And we need to
> compare all the pack elements, so I think this needs to be handled in
> the main function rather than encapsulated here.

Done.

> 
> > +	TREE_VEC_ELT (args_vec, i) =
> > +	  TREE_VEC_ELT (ARGUMENT_PACK_ARGS (arg_pack), i);
> 
> Aren't the LHS and RHS the same location here?

Yes, this is hopefully dropped now.

Bootstrapped and tested against trunk on x86_64-unknown-linux-gnu.

gcc/cp/

	* pt.c (argument_pack_element_is_expansion_p)
	(arg_pack_select_for_pack_expansion)
	(set_arg_pack_select_index_for_pack_expansion, scan_parm_packs)
	(gen_elem_of_pack_expansion_instantiation): New static functions.
	(has_bare_parameter_packs): Factorized out of ...
	(check_for_bare_parameter_packs): ... here.
	(tsubst_pack_expansion): Now this function is clearly organized in
	two parts: a part that maps each parameter pack with its matching
	argument pack, and a part that walks that map to build the result
	of the substituting each element of the argument packs into the
	parameter packs.  Use gen_elem_of_pack_expansion_instantiation for
	the latter part.

gcc/testsuite/

	* g++.dg/cpp0x/variadic139.C: New test.
	* g++.dg/cpp0x/variadic140.C: Likewise.
	* g++.dg/cpp0x/variadic141.C: Likewise.
---
 gcc/cp/pt.c                              | 525 ++++++++++++++++++++++---------
 gcc/testsuite/g++.dg/cpp0x/variadic139.C |  14 +
 gcc/testsuite/g++.dg/cpp0x/variadic140.C |  22 ++
 gcc/testsuite/g++.dg/cpp0x/variadic141.C |  22 ++
 4 files changed, 441 insertions(+), 142 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/variadic139.C
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/variadic140.C
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/variadic141.C

Comments

Jason Merrill Dec. 5, 2012, 4:01 p.m. UTC | #1
On 12/03/2012 08:27 AM, Dodji Seketeli wrote:
> +	- HAS_EXPANSION_ARG_P: Set to TRUE iff at least one parameter
> +	  pack has got an argument that is an expansion.

The "got" is unnecessary, just "has an argument" is better.

> +   Setup APS, which is an instance of an ARGUMENT_PACK_SELECT tree, so
> +   that it selects the Ith element out of the argument pack ARG_PACK.
> +   If the Ith element is a pack expansion, then just select its
> +   pattern.  Otherwise, select the whole element.

I wonder if, rather than set up a temporary pack at this point, it makes 
sense to look through pack expansions when we use an 
ARGUMENT_PACK_SELECT.  Is there any case where we actually want an 
ARGUMENT_PACK_SELECT to be an expansion?

> +  /* If we have one parameter pack whose matching argument pack is
> +     just what template_parm_to_arg returned when passed the
> +     parameter pack, or if we only have empty arguments ... */
> +  else if (arg_from_pack_level_to_prune || has_empty_arg)
> +    {
> +      /* ... we just return a pack expansion which pattern is PATTERN
> +	 into which ARGS has been substituted.  */
> +      *instantiation_yields_no_list_p = true;
> +    }

I was thinking we wouldn't need to recognize this case specifically, 
that the code following it would work the way we want.  If callers get a 
vector with a single pack expansion element rather than just a pack 
expansion, is that a problem?  Alternately, if len == 1, maybe we should 
always just return the single element.

> +  /* We could not find any argument packs that work, so we'll just
> +     return an unsubstituted pack expansion.  The caller must be
> +     prepared to deal with this.  */
>    if (len < 0)
> -    return error_mark_node;
> +    len = 1;

Why this change?  Why is returning error_mark_node no longer the right 
thing to do?

Jason
diff mbox

Patch

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index ecb013e..b8f4294 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -3308,6 +3308,29 @@  make_pack_expansion (tree arg)
   return result;
 }
 
+/* Return NULL_TREE iff T contains *NO* unexpanded parameter packs.
+   Return the TREE_LIST of unexpanded parameter packs otherwise.  */
+
+static tree
+has_bare_parameter_packs (tree t)
+{
+  tree parameter_packs = NULL_TREE;
+  struct find_parameter_pack_data ppd;
+
+  if (!processing_template_decl || !t || t == error_mark_node)
+    return NULL_TREE;
+
+  if (TREE_CODE (t) == TYPE_DECL)
+    t = TREE_TYPE (t);
+
+  ppd.parameter_packs = &parameter_packs;
+  ppd.visited = pointer_set_create ();
+  cp_walk_tree (&t, &find_parameter_packs_r, &ppd, ppd.visited);
+  pointer_set_destroy (ppd.visited);
+
+  return parameter_packs;
+}
+
 /* Checks T for any "bare" parameter packs, which have not yet been
    expanded, and issues an error if any are found. This operation can
    only be done on full expressions or types (e.g., an expression
@@ -3325,19 +3348,7 @@  make_pack_expansion (tree arg)
 bool 
 check_for_bare_parameter_packs (tree t)
 {
-  tree parameter_packs = NULL_TREE;
-  struct find_parameter_pack_data ppd;
-
-  if (!processing_template_decl || !t || t == error_mark_node)
-    return false;
-
-  if (TREE_CODE (t) == TYPE_DECL)
-    t = TREE_TYPE (t);
-
-  ppd.parameter_packs = &parameter_packs;
-  ppd.visited = pointer_set_create ();
-  cp_walk_tree (&t, &find_parameter_packs_r, &ppd, ppd.visited);
-  pointer_set_destroy (ppd.visited);
+  tree parameter_packs = has_bare_parameter_packs (t);
 
   if (parameter_packs) 
     {
@@ -9095,6 +9106,328 @@  make_fnparm_pack (tree spec_parm)
   return extract_fnparm_pack (NULL_TREE, &spec_parm);
 }
 
+/* Return true iff the Ith element of the argument pack ARG_PACK is a
+   pack expansion.  */
+
+static bool
+argument_pack_element_is_expansion_p (tree arg_pack, int i)
+{
+  tree vec = ARGUMENT_PACK_ARGS (arg_pack);
+  if (i >= TREE_VEC_LENGTH (vec))
+    return false;
+  return PACK_EXPANSION_P (TREE_VEC_ELT (vec, i));
+}
+
+
+/* This is a subroutine of tsubst_pack_expansion.
+
+   It creates an ARGUMENT_PACK_SELECT tree node, for the purpose of
+   substituting an argument pack into a pack expansion.  ARG_PACK is
+   an instance of {TYPE,NON_TYPE}_ARGUMENT_PACK.
+   ALL_PACKS_HAVE_EXPANSION_ARGS must be set to true if every single
+   parameter pack in the expansion passed to tsubst_pack_expansion has
+   an argument that is itself an expansion.  */
+
+static tree
+arg_pack_select_for_pack_expansion (tree arg_pack,
+				    bool all_packs_have_expansion_args)
+{
+  tree aps = make_node (ARGUMENT_PACK_SELECT);
+
+  if (!all_packs_have_expansion_args)
+    ARGUMENT_PACK_SELECT_FROM_PACK (aps) = arg_pack;
+  else
+    {
+      tree tmp_arg_pack = TYPE_P (arg_pack)
+	? cxx_make_type (TREE_CODE (arg_pack))
+	: make_node (TREE_CODE (arg_pack));
+      tree tmp_vec =
+	make_tree_vec (TREE_VEC_LENGTH (ARGUMENT_PACK_ARGS (arg_pack)));
+      SET_ARGUMENT_PACK_ARGS (tmp_arg_pack, tmp_vec);
+      ARGUMENT_PACK_SELECT_FROM_PACK (aps) = tmp_arg_pack;
+    }
+
+   return aps;
+}
+
+/* This is a subroutine of tsubst_pack_expansion.
+
+   Setup APS, which is an instance of an ARGUMENT_PACK_SELECT tree, so
+   that it selects the Ith element out of the argument pack ARG_PACK.
+   If the Ith element is a pack expansion, then just select its
+   pattern.  Otherwise, select the whole element.
+   ALL_PACKS_HAVE_EXPANSION_ARGS must be set to true if every single
+   parameter pack in the expansion passed to tsubst_pack_expansion has
+   an argument that is itself an expansion.  */
+
+static void
+set_arg_pack_select_index_for_pack_expansion (tree aps, int i, tree arg_pack,
+					      bool all_packs_have_expansion_args)
+{
+  if (all_packs_have_expansion_args)
+    {
+      tree args_vec =
+	ARGUMENT_PACK_ARGS (ARGUMENT_PACK_SELECT_FROM_PACK (aps));
+      gcc_assert (argument_pack_element_is_expansion_p (arg_pack, i));
+
+      TREE_VEC_ELT (args_vec, i) =
+	PACK_EXPANSION_PATTERN (TREE_VEC_ELT
+				(ARGUMENT_PACK_ARGS (arg_pack),
+				 i));
+    }
+  ARGUMENT_PACK_SELECT_INDEX (aps) = i;
+}
+
+/*  This is a subroutine of gen_elem_of_pack_expansion_instantiation,
+    which is itself a subroutine of tsubst_pack_expansion.
+
+    For a given INDEX of an argument pack, this function scans the
+    list of parameter packs PARM_PACKS and reports some interesting
+    properties about them.  Note that PARM_PACKS must be an instance of
+    TREE_LIST where TREE_PURPOSE is a parameter pack, and TREE_VALUE
+    is the argument for that pack.
+
+    The properties returned about the scanned parameter packs
+    are the following:
+
+	- HAS_EXPANSION_ARG_P: Set to TRUE iff at least one parameter
+	  pack has got an argument that is an expansion. 
+	- HAS_NON_EXPANSION_ARG_P: Set to TRUE iff at least one
+	  parameter pack has got
+	  an argument that is not an expansion.
+	- HAS_EMPTY_ARG_P: Set to TRUE iff at least one parameter pack has
+	  no argument.
+	- ARG_FROM_PACK_LEVEL_TO_PRUNE: is non-zero iff the argument of
+	  the parameter pack is the result of calling template_parm_to_arg
+	  on the pack.  In that case, this is set to the parameter level
+	  of that pack.  */
+
+static void
+scan_parm_packs (tree parm_packs,
+		 unsigned index,
+		 bool *has_expansion_arg_p,
+		 bool *has_non_expansion_arg_p,
+		 bool *has_empty_arg_p,
+		 int *arg_from_pack_level_to_prune)
+{
+
+  if (parm_packs == NULL_TREE)
+    {
+      if (has_empty_arg_p)
+	*has_empty_arg_p = true;
+      return ;
+    }
+
+  for (tree parm_pack = parm_packs;
+       parm_pack;
+       parm_pack = TREE_CHAIN (parm_pack))
+    {
+      tree arg = TREE_VALUE (parm_pack);
+      tree parm = TREE_PURPOSE (parm_pack);
+
+      if (arg == NULL_TREE)
+	{
+	  if (has_empty_arg_p)
+	    *has_empty_arg_p = true;
+	  continue;
+	}
+
+      if (arg_from_pack_level_to_prune != NULL
+	  && arg_from_parm_pack_p (arg, parm))
+	{
+	  int level = 0, idx = 0;
+	  if (TREE_CODE (parm) == PARM_DECL)
+	    parm = DECL_INITIAL (parm);
+	  template_parm_level_and_index (parm, &level, &idx);
+	  if (*arg_from_pack_level_to_prune == 0
+	      || *arg_from_pack_level_to_prune > level)
+	    *arg_from_pack_level_to_prune = level;
+	  return;
+	}
+
+      if (argument_pack_element_is_expansion_p (arg, index))
+	{
+	  if (has_expansion_arg_p != NULL)
+	    *has_expansion_arg_p = true;
+	}
+      else
+	{ 
+	  if (has_non_expansion_arg_p != NULL)
+	    *has_non_expansion_arg_p = true;
+	}
+    }
+}
+
+/* [temp.variadic]/6 says that:
+
+       The instantiation of a pack expansion [...]
+       produces a list E1,E2, ..., En, where N is the number of elements
+       in the pack expansion parameters.
+
+   This subroutine of tsubst_pack_expansion produces one of these Ei.
+
+   PATTERN is the pattern of the pack expansion.  PARM_PACKS is a
+   TREE_LIST in which each TREE_PURPOSE is a parameter pack of
+   PATTERN, and each TREE_VALUE is its corresponding argument pack.
+   INDEX is the index 'i' of the element Ei to produce.  ARGS,
+   COMPLAIN, and IN_DECL are the same parameters as for the
+   tsubst_pack_expansion function.
+
+   The function returns the resulting Ei upon successful completion,
+   or error_mark_node.  If the *INSTANTIATION_YIELDS_NO_LIST_P is set
+   to true upon completion, that means the result of the function is
+   not an element of a list, but rather the sole result of the pack
+   expansion substituting that has to be returned as the result of the
+   tsubst_pack_expansion function.
+
+   Note that this function possibly modifies the ARGS parameter, so
+   it's the responsibility of the caller to restaure it.  */
+
+static tree
+gen_elem_of_pack_expansion_instantiation (tree pattern,
+					  tree parm_packs,
+					  unsigned index,
+					  bool *instantiation_yields_no_list_p,
+					  tree args /* This parm
+						       gets modified.  */,
+					  tsubst_flags_t complain,
+					  tree in_decl)
+{
+  /* The boolean below is TRUE if at least one parameter pack doesn't
+     yet have all argument packs to really perform the
+     susbtituting.  */
+  bool use_pack_expansion_extra = false,
+    has_expansion_arg = false,
+    has_non_expansion_arg = false,
+    has_empty_arg = false,
+    /* this is true if, at index I of the argument pack,, all parm
+       packs have a pack expansion argument.  */
+    all_packs_have_expansion_args = false;
+
+  int arg_from_pack_level_to_prune = 0;
+  tree t;
+
+  scan_parm_packs (parm_packs, index,
+		   &has_expansion_arg,
+		   &has_non_expansion_arg,
+		   &has_empty_arg,
+		   &arg_from_pack_level_to_prune);
+
+  if ((has_expansion_arg && has_non_expansion_arg)
+      || (has_empty_arg && (has_expansion_arg || has_non_expansion_arg)))
+    use_pack_expansion_extra = true;
+
+  all_packs_have_expansion_args =
+    has_expansion_arg && !has_non_expansion_arg;
+
+  if (use_pack_expansion_extra)
+    {
+      /* We got some full packs, but we can't substitute them in until we
+	 have values for all the packs.  So remember these until then.  */
+
+      int levels = TMPL_ARGS_DEPTH (args);
+      tree save_args;
+
+      /* The call to add_to_template_args in tsubst_pack_expansion
+	 assumes no overlap between saved args and new args, so prune
+	 away any fake args, i.e. those that satisfied
+	 arg_from_parm_pack_p in scan_parm_packs above.  */
+      if (arg_from_pack_level_to_prune != 0
+	  && levels >= arg_from_pack_level_to_prune)
+	{
+	  gcc_assert (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args)
+		      && arg_from_pack_level_to_prune > 1);
+	  TREE_VEC_LENGTH (args) = arg_from_pack_level_to_prune - 1;
+	  save_args = copy_node (args);
+	  TREE_VEC_LENGTH (args) = levels;
+	}
+      else
+	save_args = args;
+
+      t = make_pack_expansion (pattern);
+      PACK_EXPANSION_EXTRA_ARGS (t) = save_args;
+
+      *instantiation_yields_no_list_p = true;
+
+      return t;
+    }
+  /* If we have one parameter pack whose matching argument pack is
+     just what template_parm_to_arg returned when passed the
+     parameter pack, or if we only have empty arguments ... */
+  else if (arg_from_pack_level_to_prune || has_empty_arg)
+    {
+      /* ... we just return a pack expansion which pattern is PATTERN
+	 into which ARGS has been substituted.  */
+      *instantiation_yields_no_list_p = true;
+    }
+  else
+    {
+      /* For each parameter pack, change the substitution of the parameter
+	 pack to the ith argument in its argument pack, then expand the
+	 pattern.  */
+      for (tree pack = parm_packs; pack; pack = TREE_CHAIN (pack))
+	{
+	  tree parm = TREE_PURPOSE (pack);
+	  tree arg_pack = TREE_VALUE (pack);
+	  tree aps;			/* instance of ARGUMENT_PACK_SELECT
+					   tree.  */
+
+	  /* Select the Ith argument from the pack.  */
+	  if (TREE_CODE (parm) == PARM_DECL)
+	    {
+	      if (index == 0)
+		{
+		  aps =
+		    arg_pack_select_for_pack_expansion
+		    (arg_pack, all_packs_have_expansion_args);
+
+		  mark_used (parm);
+		  register_local_specialization (aps, parm);
+		}
+	      else
+		aps = retrieve_local_specialization (parm);
+	    }
+	  else
+	    {
+	      int idx, level;
+	      template_parm_level_and_index (parm, &level, &idx);
+
+	      if (index == 0)
+		{
+		  aps =
+		    arg_pack_select_for_pack_expansion
+		    (arg_pack, all_packs_have_expansion_args);
+		  /* Update the corresponding argument.  */
+		  TMPL_ARG (args, level, idx) = aps;
+		}
+	      else
+		/* Re-use the ARGUMENT_PACK_SELECT.  */
+		aps = TMPL_ARG (args, level, idx);
+	    }
+	  set_arg_pack_select_index_for_pack_expansion
+	    (aps, index, arg_pack, all_packs_have_expansion_args);
+	}
+
+      *instantiation_yields_no_list_p = false;
+    }
+
+  /* Substitute into the PATTERN with the (possibly altered)
+     arguments.  */
+  if (!TYPE_P (pattern))
+    t = tsubst_expr (pattern, args, complain, in_decl,
+		     /*integral_constant_expression_p=*/false);
+  else
+    t = tsubst (pattern, args, complain, in_decl);
+
+  /*  If the Ith argument pack element is a pack expansion, then
+      the Ith element resulting from the substituting is going to
+      be a pack expansion as well.  */
+  if (has_bare_parameter_packs (t))
+    t = make_pack_expansion (t);
+
+  return t;
+}
+
 /* Substitute ARGS into T, which is an pack expansion
    (i.e. TYPE_PACK_EXPANSION or EXPR_PACK_EXPANSION). Returns a
    TREE_VEC with the substituted arguments, a PACK_EXPANSION_* node
@@ -9106,9 +9439,6 @@  tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
 {
   tree pattern;
   tree pack, packs = NULL_TREE;
-  bool unsubstituted_packs = false;
-  bool real_packs = false;
-  int missing_level = 0;
   int i, len = -1;
   tree result;
   struct pointer_map_t *saved_local_specializations = NULL;
@@ -9187,14 +9517,6 @@  tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
 	  return result;
 	}
 
-      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)
         {
           int my_len = 
@@ -9221,77 +9543,19 @@  tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
                        pattern);
               return error_mark_node;
             }
-
-	  if (TREE_VEC_LENGTH (ARGUMENT_PACK_ARGS (arg_pack)) == 1
-	      && PACK_EXPANSION_P (TREE_VEC_ELT (ARGUMENT_PACK_ARGS (arg_pack),
-						 0)))
-	    /* This isn't a real argument pack yet.  */;
-	  else
-	    real_packs = true;
-
-          /* Keep track of the parameter packs and their corresponding
-             argument packs.  */
-          packs = tree_cons (parm_pack, arg_pack, packs);
-          TREE_TYPE (packs) = orig_arg;
         }
-      else
-	{
-	  /* We can't substitute for this parameter pack.  We use a flag as
-	     well as the missing_level counter because function parameter
-	     packs don't have a level.  */
-	  unsubstituted_packs = true;
-	  if (!missing_level || missing_level > level)
-	    missing_level = level;
-	}
-    }
 
-  /* We cannot expand this expansion expression, because we don't have
-     all of the argument packs we need.  */
-  if (unsubstituted_packs)
-    {
-      if (real_packs)
-	{
-	  /* We got some full packs, but we can't substitute them in until we
-	     have values for all the packs.  So remember these until then.  */
-	  tree save_args;
-
-	  t = make_pack_expansion (pattern);
-
-	  /* The call to add_to_template_args above assumes no overlap
-	     between saved args and new args, so prune away any fake
-	     args, i.e. those that satisfied arg_from_parm_pack_p above.  */
-	  if (missing_level && levels >= missing_level)
-	    {
-	      gcc_assert (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args)
-			  && missing_level > 1);
-	      TREE_VEC_LENGTH (args) = missing_level - 1;
-	      save_args = copy_node (args);
-	      TREE_VEC_LENGTH (args) = levels;
-	    }
-	  else
-	    save_args = args;
-
-	  PACK_EXPANSION_EXTRA_ARGS (t) = save_args;
-	}
-      else
-	{
-	  /* There were no real arguments, we're just replacing a parameter
-	     pack with another version of itself. Substitute into the
-	     pattern and return a PACK_EXPANSION_*. The caller will need to
-	     deal with that.  */
-	  if (TREE_CODE (t) == EXPR_PACK_EXPANSION)
-	    t = tsubst_expr (pattern, args, complain, in_decl,
-			     /*integral_constant_expression_p=*/false);
-	  else
-	    t = tsubst (pattern, args, complain, in_decl);
-	  t = make_pack_expansion (t);
-	}
-      return t;
+      /* Keep track of the parameter packs and their corresponding
+	 argument packs.  */
+      packs = tree_cons (parm_pack, arg_pack, packs);
+      TREE_TYPE (packs) = orig_arg;
     }
 
-  /* We could not find any argument packs that work.  */
+  /* We could not find any argument packs that work, so we'll just
+     return an unsubstituted pack expansion.  The caller must be
+     prepared to deal with this.  */
   if (len < 0)
-    return error_mark_node;
+    len = 1;
 
   if (need_local_specializations)
     {
@@ -9305,63 +9569,39 @@  tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
 
   /* For each argument in each argument pack, substitute into the
      pattern.  */
-  result = make_tree_vec (len);
-  for (i = 0; i < len; ++i)
-    {
-      /* For parameter pack, change the substitution of the parameter
-         pack to the ith argument in its argument pack, then expand
-         the pattern.  */
-      for (pack = packs; pack; pack = TREE_CHAIN (pack))
-        {
-          tree parm = TREE_PURPOSE (pack);
-	  tree arg;
-
-	  /* Select the Ith argument from the pack.  */
-          if (TREE_CODE (parm) == PARM_DECL)
-            {
-	      if (i == 0)
-		{
-		  arg = make_node (ARGUMENT_PACK_SELECT);
-		  ARGUMENT_PACK_SELECT_FROM_PACK (arg) = TREE_VALUE (pack);
-		  mark_used (parm);
-		  register_local_specialization (arg, parm);
-		}
-	      else
-		arg = retrieve_local_specialization (parm);
-            }
-          else
-            {
-              int idx, level;
-              template_parm_level_and_index (parm, &level, &idx);
-
-	      if (i == 0)
-		{
-		  arg = make_node (ARGUMENT_PACK_SELECT);
-		  ARGUMENT_PACK_SELECT_FROM_PACK (arg) = TREE_VALUE (pack);
-		  /* Update the corresponding argument.  */
-		  TMPL_ARG (args, level, idx) = arg;
-		}
-	      else
-		/* Re-use the ARGUMENT_PACK_SELECT.  */
-		arg = TMPL_ARG (args, level, idx);
-            }
-	  ARGUMENT_PACK_SELECT_INDEX (arg) = i;
-        }
+  if (len > 0)
+    {
+      bool yield_no_list = false;
+      t = gen_elem_of_pack_expansion_instantiation (pattern, packs, 0,
+						    &yield_no_list,
+						    args, complain,
+						    in_decl);
+      if (yield_no_list)
+	{
+	  result = t;
+	  goto out;
+	}
 
-      /* Substitute into the PATTERN with the altered arguments.  */
-      if (!TYPE_P (pattern))
-        TREE_VEC_ELT (result, i) = 
-          tsubst_expr (pattern, args, complain, in_decl,
-                       /*integral_constant_expression_p=*/false);
-      else
-        TREE_VEC_ELT (result, i) = tsubst (pattern, args, complain, in_decl);
+      result = make_tree_vec (len);
+      TREE_VEC_ELT (result, 0) = t;
 
-      if (TREE_VEC_ELT (result, i) == error_mark_node)
+      for (i = 1; i < len; ++i)
 	{
-	  result = error_mark_node;
-	  break;
+	  
+	  t = gen_elem_of_pack_expansion_instantiation (pattern, packs, i,
+							&yield_no_list,
+							args, complain,
+							in_decl);
+	  TREE_VEC_ELT (result, i) = t;
+      	  if (t == error_mark_node)
+	    {
+	      result = error_mark_node;
+	      break;
+	    }
 	}
     }
+  else
+    result = make_tree_vec (0);
 
   /* Update ARGS to restore the substitution from parameter packs to
      their argument packs.  */
@@ -9385,6 +9625,7 @@  tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
         }
     }
 
+ out:
   if (need_local_specializations)
     {
       pointer_map_destroy (local_specializations);
diff --git a/gcc/testsuite/g++.dg/cpp0x/variadic139.C b/gcc/testsuite/g++.dg/cpp0x/variadic139.C
new file mode 100644
index 0000000..89f7b32
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/variadic139.C
@@ -0,0 +1,14 @@ 
+// Origin: PR c++/53609
+// { dg-do compile { target c++11 } }
+
+template<class...I> struct List {};
+template<int T> struct Z {static const int value = T;};
+template<int...T> using LZ = List<Z<T>...>;
+
+template<class...U>
+struct F
+{
+  using N = LZ<U::value...>;
+};                           
+
+F<Z<1>, Z<2> >::N A;
diff --git a/gcc/testsuite/g++.dg/cpp0x/variadic140.C b/gcc/testsuite/g++.dg/cpp0x/variadic140.C
new file mode 100644
index 0000000..17ca9e5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/variadic140.C
@@ -0,0 +1,22 @@ 
+// Origin: PR c++/53609
+// { dg-do compile { target c++11 } }
+
+template<class...I> struct List{ static const bool is_ok = false;};
+template<int T> struct Z
+{
+  static const int value = T;
+  static const int value_square = T * T;
+};
+
+template<template<int> class U>
+struct List<U<2>, U<3>, U<4>, U<9>> { static const bool is_ok = true;};
+
+template<int...T> using LZ = List<Z<T>...>;
+
+template<class...T>
+struct F
+{
+  using N = LZ<T::value..., T::value_square...>;
+};
+
+static_assert (F<Z<2>, Z<3>>::N::is_ok, "");
diff --git a/gcc/testsuite/g++.dg/cpp0x/variadic141.C b/gcc/testsuite/g++.dg/cpp0x/variadic141.C
new file mode 100644
index 0000000..6b893a7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/variadic141.C
@@ -0,0 +1,22 @@ 
+// Origin: PR c++/53609
+// { dg-do compile { target c++11 } }
+
+template<class...I> struct List{ static const bool is_ok = false;};
+template<int T> struct Z
+{
+  static const int value = T;
+  static const int value_square = T * T;
+};
+
+template<template<int> class U>
+struct List<U<2>, U<3>, U<4>, U<9>> { static const bool is_ok = true;};
+
+template<int...T> using LZ = List<Z<T>...>;
+
+template<class...T>
+struct F
+{
+  using N = LZ<T::value..., Z<4>::value, Z<9>::value>;
+};
+
+static_assert (F<Z<2>, Z<3>>::N::is_ok, "");