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, "");
