diff mbox

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

Message ID m3haqtozhj.fsf@redhat.com
State New
Headers show

Commit Message

Dodji Seketeli Sept. 20, 2012, 8:44 a.m. UTC
Hello,

Consider this example:

     1	template<class...I> struct List {};
     2	template<int T> struct Z {static const int value = T;};
     3	template<int...T> using LZ = List<Z<T>...>;
     4
     5	template<class...U>
     6	struct F
     7	{
     8	  using N = LZ<U::value...>; //#1 This should amount to List<Z<U::value>...>
     9	}
    10
    11	F<Z<1>, Z<2> >::N A; //#2

which G++ fails to compile, with this error message:

test-PR53609-3.cc: In instantiation of 'struct F<Z<1>, Z<2> >':
test-PR53609-3.cc:11:15:   required from here
test-PR53609-3.cc:3:43: error: wrong number of template arguments (2, should be 1)
 template<int...T> using LZ = List<Z<T>...>;
                                           ^
test-PR53609-3.cc:2:24: error: provided for 'template<int T> struct Z'
 template<int T> struct Z {static const int value = T;};

I think this is because in #1, when we substitute the argument pack
{U::value...} into the pack expansion Z<T>..., tsubst_pack_expansion
yields Z<U::value...>, instead of Z<U::value>..., so the instantiation
of LZ amounts to List<Z<U::value...> >, instead of
List<Z<U::value>...>.

The idea of this patch is to make tsubst_pack_expansion support
substituting an argument pack (into a pack expansion) where one of the
arguments (let's call it the Ith argument) is itself a pack expansion
P.  In that case, the Ith element resulting from the substituting
should be a pack expansion P'.

The pattern of P' is then the pattern of P into which the pattern of
the Ith argument of the argument pack has been substituted.

Tested on x86_64-unknown-linux-gnu against trunk.

gcc/cp/

	* pt.c (real_argument_pack_element_p)
	(any_non_real_argument_pack_element_p)
	(arg_pack_select_for_pack_expansion)
	(set_arg_pack_select_index_for_pack_expansion): New static
	functions.
	(has_bare_parameter_packs): Factorized out of ...
	(check_for_bare_parameter_packs): ... here.
	(tsubst_pack_expansion): Support substituting an argument pack
	that contains a pack expansion.

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                              |  151 ++++++++++++++++++++++++------
 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, 182 insertions(+), 27 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

Dodji Seketeli Nov. 16, 2012, 1:15 p.m. UTC | #1
I am friendly pinging the patch below ...


Dodji Seketeli <dodji@redhat.com> a écrit:

> Hello,
>
> Consider this example:
>
>      1	template<class...I> struct List {};
>      2	template<int T> struct Z {static const int value = T;};
>      3	template<int...T> using LZ = List<Z<T>...>;
>      4
>      5	template<class...U>
>      6	struct F
>      7	{
>      8	  using N = LZ<U::value...>; //#1 This should amount to List<Z<U::value>...>
>      9	}
>     10
>     11	F<Z<1>, Z<2> >::N A; //#2
>
> which G++ fails to compile, with this error message:
>
> test-PR53609-3.cc: In instantiation of 'struct F<Z<1>, Z<2> >':
> test-PR53609-3.cc:11:15:   required from here
> test-PR53609-3.cc:3:43: error: wrong number of template arguments (2, should be 1)
>  template<int...T> using LZ = List<Z<T>...>;
>                                            ^
> test-PR53609-3.cc:2:24: error: provided for 'template<int T> struct Z'
>  template<int T> struct Z {static const int value = T;};
>
> I think this is because in #1, when we substitute the argument pack
> {U::value...} into the pack expansion Z<T>..., tsubst_pack_expansion
> yields Z<U::value...>, instead of Z<U::value>..., so the instantiation
> of LZ amounts to List<Z<U::value...> >, instead of
> List<Z<U::value>...>.
>
> The idea of this patch is to make tsubst_pack_expansion support
> substituting an argument pack (into a pack expansion) where one of the
> arguments (let's call it the Ith argument) is itself a pack expansion
> P.  In that case, the Ith element resulting from the substituting
> should be a pack expansion P'.
>
> The pattern of P' is then the pattern of P into which the pattern of
> the Ith argument of the argument pack has been substituted.
>
> Tested on x86_64-unknown-linux-gnu against trunk.
>
> gcc/cp/
>
> 	* pt.c (real_argument_pack_element_p)
> 	(any_non_real_argument_pack_element_p)
> 	(arg_pack_select_for_pack_expansion)
> 	(set_arg_pack_select_index_for_pack_expansion): New static
> 	functions.
> 	(has_bare_parameter_packs): Factorized out of ...
> 	(check_for_bare_parameter_packs): ... here.
> 	(tsubst_pack_expansion): Support substituting an argument pack
> 	that contains a pack expansion.
>
> 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                              |  151 ++++++++++++++++++++++++------
>  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, 182 insertions(+), 27 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
>
> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
> index 16952bf..bcfe83f 100644
> --- a/gcc/cp/pt.c
> +++ b/gcc/cp/pt.c
> @@ -3310,6 +3310,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
> @@ -3327,19 +3350,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) 
>      {
> @@ -9065,6 +9076,86 @@ 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
> +   *NOT* a pack expansion.  */
> +
> +static bool
> +real_argument_pack_element_p (tree arg_pack, int i)
> +{
> +  return !PACK_EXPANSION_P (TREE_VEC_ELT
> +			    (ARGUMENT_PACK_ARGS (arg_pack), i));
> +}
> +
> +/* Return true iff ARG_PACK is an argument pack that contains a pack
> +   expansion.  */
> +
> +static bool
> +any_non_real_argument_pack_element_p (tree arg_pack)
> +{
> +  if (arg_pack == NULL_TREE
> +      || arg_pack == error_mark_node
> +      || !ARGUMENT_PACK_P (arg_pack))
> +    return false;
> +
> +  for (int i = 0; i < TREE_VEC_LENGTH (ARGUMENT_PACK_ARGS (arg_pack)); ++i)
> +    if (!real_argument_pack_element_p (arg_pack, i))
> +      return true;
> +  return false;
> +}
> +
> +/* Creates an ARGUMENT_PACK_SELECT tree node, for the purpose of
> +   substituting an argument pack into a pack expansion.  This is a
> +   subroutine of tsubst_pack_expansion.   */
> +
> +static tree
> +arg_pack_select_for_pack_expansion (tree arg_pack)
> +{
> +  tree aps = make_node (ARGUMENT_PACK_SELECT);
> +
> +  if (!any_non_real_argument_pack_element_p (arg_pack))
> +    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;
> +}
> +
> +/* Setup APS, which is an instance of an ARGUMENT_PACK_SELECT tree, so
> +   that it selects the Ith argument out of the argument pack
> +   ARG_PACK.  If the Ith argument is a pack expansion, then just
> +   select its pattern.  Otherwise, select the whole argument.  This
> +   is a subroutine of tsubst_pack_expansion.  */
> +
> +static void
> +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))
> +    {
> +      tree args_vec =
> +	ARGUMENT_PACK_ARGS (ARGUMENT_PACK_SELECT_FROM_PACK (aps));
> +      if (real_argument_pack_element_p (arg_pack, i))
> +	TREE_VEC_ELT (args_vec, i) =
> +	  TREE_VEC_ELT (ARGUMENT_PACK_ARGS (arg_pack), i);
> +      else
> +	TREE_VEC_ELT (args_vec, i) =
> +	  PACK_EXPANSION_PATTERN (TREE_VEC_ELT
> +				  (ARGUMENT_PACK_ARGS (arg_pack),
> +				   i));
> +    }
> +
> +  ARGUMENT_PACK_SELECT_INDEX (aps) = i;
> +}
> +
>  /* 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
> @@ -9284,20 +9375,21 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
>        for (pack = packs; pack; pack = TREE_CHAIN (pack))
>          {
>            tree parm = TREE_PURPOSE (pack);
> -	  tree arg;
> +	  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 (i == 0)
>  		{
> -		  arg = make_node (ARGUMENT_PACK_SELECT);
> -		  ARGUMENT_PACK_SELECT_FROM_PACK (arg) = TREE_VALUE (pack);
> +		  aps = arg_pack_select_for_pack_expansion (arg_pack);
>  		  mark_used (parm);
> -		  register_local_specialization (arg, parm);
> +		  register_local_specialization (aps, parm);
>  		}
>  	      else
> -		arg = retrieve_local_specialization (parm);
> +		aps = retrieve_local_specialization (parm);
>              }
>            else
>              {
> @@ -9306,25 +9398,30 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
>  
>  	      if (i == 0)
>  		{
> -		  arg = make_node (ARGUMENT_PACK_SELECT);
> -		  ARGUMENT_PACK_SELECT_FROM_PACK (arg) = TREE_VALUE (pack);
> +		  aps = arg_pack_select_for_pack_expansion (arg_pack);
>  		  /* Update the corresponding argument.  */
> -		  TMPL_ARG (args, level, idx) = arg;
> +		  TMPL_ARG (args, level, idx) = aps;
>  		}
>  	      else
>  		/* Re-use the ARGUMENT_PACK_SELECT.  */
> -		arg = TMPL_ARG (args, level, idx);
> +		aps = TMPL_ARG (args, level, idx);
>              }
> -	  ARGUMENT_PACK_SELECT_INDEX (arg) = i;
> +	  set_arg_pack_select_index_for_pack_expansion (aps, i,
> +							arg_pack);
>          }
>  
>        /* 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);
> +	t = tsubst_expr (pattern, args, complain, in_decl,
> +			 /*integral_constant_expression_p=*/false);
>        else
> -        TREE_VEC_ELT (result, i) = tsubst (pattern, args, complain, in_decl);
> +	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.  */
> +      TREE_VEC_ELT (result, i) =
> +	(has_bare_parameter_packs (t)) ? make_pack_expansion (t) : t;
>  
>        if (TREE_VEC_ELT (result, i) == error_mark_node)
>  	{
> 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, "");
Jason Merrill Nov. 16, 2012, 10:39 p.m. UTC | #2
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.

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.

> +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.

> +	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?

Jason
diff mbox

Patch

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 16952bf..bcfe83f 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -3310,6 +3310,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
@@ -3327,19 +3350,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) 
     {
@@ -9065,6 +9076,86 @@  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
+   *NOT* a pack expansion.  */
+
+static bool
+real_argument_pack_element_p (tree arg_pack, int i)
+{
+  return !PACK_EXPANSION_P (TREE_VEC_ELT
+			    (ARGUMENT_PACK_ARGS (arg_pack), i));
+}
+
+/* Return true iff ARG_PACK is an argument pack that contains a pack
+   expansion.  */
+
+static bool
+any_non_real_argument_pack_element_p (tree arg_pack)
+{
+  if (arg_pack == NULL_TREE
+      || arg_pack == error_mark_node
+      || !ARGUMENT_PACK_P (arg_pack))
+    return false;
+
+  for (int i = 0; i < TREE_VEC_LENGTH (ARGUMENT_PACK_ARGS (arg_pack)); ++i)
+    if (!real_argument_pack_element_p (arg_pack, i))
+      return true;
+  return false;
+}
+
+/* Creates an ARGUMENT_PACK_SELECT tree node, for the purpose of
+   substituting an argument pack into a pack expansion.  This is a
+   subroutine of tsubst_pack_expansion.   */
+
+static tree
+arg_pack_select_for_pack_expansion (tree arg_pack)
+{
+  tree aps = make_node (ARGUMENT_PACK_SELECT);
+
+  if (!any_non_real_argument_pack_element_p (arg_pack))
+    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;
+}
+
+/* Setup APS, which is an instance of an ARGUMENT_PACK_SELECT tree, so
+   that it selects the Ith argument out of the argument pack
+   ARG_PACK.  If the Ith argument is a pack expansion, then just
+   select its pattern.  Otherwise, select the whole argument.  This
+   is a subroutine of tsubst_pack_expansion.  */
+
+static void
+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))
+    {
+      tree args_vec =
+	ARGUMENT_PACK_ARGS (ARGUMENT_PACK_SELECT_FROM_PACK (aps));
+      if (real_argument_pack_element_p (arg_pack, i))
+	TREE_VEC_ELT (args_vec, i) =
+	  TREE_VEC_ELT (ARGUMENT_PACK_ARGS (arg_pack), i);
+      else
+	TREE_VEC_ELT (args_vec, i) =
+	  PACK_EXPANSION_PATTERN (TREE_VEC_ELT
+				  (ARGUMENT_PACK_ARGS (arg_pack),
+				   i));
+    }
+
+  ARGUMENT_PACK_SELECT_INDEX (aps) = i;
+}
+
 /* 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
@@ -9284,20 +9375,21 @@  tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
       for (pack = packs; pack; pack = TREE_CHAIN (pack))
         {
           tree parm = TREE_PURPOSE (pack);
-	  tree arg;
+	  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 (i == 0)
 		{
-		  arg = make_node (ARGUMENT_PACK_SELECT);
-		  ARGUMENT_PACK_SELECT_FROM_PACK (arg) = TREE_VALUE (pack);
+		  aps = arg_pack_select_for_pack_expansion (arg_pack);
 		  mark_used (parm);
-		  register_local_specialization (arg, parm);
+		  register_local_specialization (aps, parm);
 		}
 	      else
-		arg = retrieve_local_specialization (parm);
+		aps = retrieve_local_specialization (parm);
             }
           else
             {
@@ -9306,25 +9398,30 @@  tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
 
 	      if (i == 0)
 		{
-		  arg = make_node (ARGUMENT_PACK_SELECT);
-		  ARGUMENT_PACK_SELECT_FROM_PACK (arg) = TREE_VALUE (pack);
+		  aps = arg_pack_select_for_pack_expansion (arg_pack);
 		  /* Update the corresponding argument.  */
-		  TMPL_ARG (args, level, idx) = arg;
+		  TMPL_ARG (args, level, idx) = aps;
 		}
 	      else
 		/* Re-use the ARGUMENT_PACK_SELECT.  */
-		arg = TMPL_ARG (args, level, idx);
+		aps = TMPL_ARG (args, level, idx);
             }
-	  ARGUMENT_PACK_SELECT_INDEX (arg) = i;
+	  set_arg_pack_select_index_for_pack_expansion (aps, i,
+							arg_pack);
         }
 
       /* 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);
+	t = tsubst_expr (pattern, args, complain, in_decl,
+			 /*integral_constant_expression_p=*/false);
       else
-        TREE_VEC_ELT (result, i) = tsubst (pattern, args, complain, in_decl);
+	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.  */
+      TREE_VEC_ELT (result, i) =
+	(has_bare_parameter_packs (t)) ? make_pack_expansion (t) : t;
 
       if (TREE_VEC_ELT (result, i) == error_mark_node)
 	{
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, "");