diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 8672da123f4..7898834faa6 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -27,6 +27,7 @@ along with GCC; see the file COPYING3.  If not see
      Fixed by: C++20 modules.  */
 
 #include "config.h"
+#define INCLUDE_ALGORITHM // for std::equal
 #include "system.h"
 #include "coretypes.h"
 #include "cp-tree.h"
@@ -13544,17 +13545,22 @@ tsubst_argument_pack (tree orig_arg, tree args, tsubst_flags_t complain,
 tree
 tsubst_template_args (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 {
-  tree orig_t = t;
-  int len, need_new = 0, i, expanded_len_adjust = 0, out;
-  tree *elts;
-
   if (t == error_mark_node)
     return error_mark_node;
 
-  len = TREE_VEC_LENGTH (t);
-  elts = XALLOCAVEC (tree, len);
+  const int len = TREE_VEC_LENGTH (t);
+  tree *elts = XALLOCAVEC (tree, len);
+  int expanded_len_adjust = 0;
 
-  for (i = 0; i < len; i++)
+  /* True iff no element of T was changed by the substitution.  */
+  bool const_subst_p = true;
+
+  /* If MAYBE_REUSE is non-NULL, as an optimization we'll try to reuse and
+     return this TREE_VEC instead of allocating a new one if possible.  This
+     will either be ARGS or a level from ARGS.  */
+  tree maybe_reuse = NULL_TREE;
+
+  for (int i = 0; i < len; i++)
     {
       tree orig_arg = TREE_VEC_ELT (t, i);
       tree new_arg;
@@ -13580,56 +13586,90 @@ tsubst_template_args (tree t, tree args, tsubst_flags_t complain, tree in_decl)
       else if (ARGUMENT_PACK_P (orig_arg))
 	new_arg = tsubst_argument_pack (orig_arg, args, complain, in_decl);
       else
+	{
 	  new_arg = tsubst_template_arg (orig_arg, args, complain, in_decl);
 
+	  /* If T heuristically appears to be a set of generic template
+	     arguments, set MAYBE_REUSE to the corresponding level from
+	     ARGS.  */
+	  if (maybe_reuse == NULL_TREE && orig_arg != NULL_TREE)
+	    {
+	      if (TEMPLATE_PARM_P (orig_arg))
+		{
+		  int level, index;
+		  template_parm_level_and_index (orig_arg, &level, &index);
+		  if (index == i && TMPL_ARGS_DEPTH (args) >= level)
+		    maybe_reuse = TMPL_ARGS_LEVEL (args, level);
+		  else
+		    maybe_reuse = error_mark_node;
+		}
+	      else
+		/* T is not a set of generic template arguments; use
+		   error_mark_node to denote this.  */
+		maybe_reuse = error_mark_node;
+	    }
+	}
+
       if (new_arg == error_mark_node)
 	return error_mark_node;
 
       elts[i] = new_arg;
       if (new_arg != orig_arg)
-	need_new = 1;
+	const_subst_p = false;
     }
 
-  if (!need_new)
+  if (const_subst_p)
     return t;
 
+  /* If ARGS and T are both multi-level, then substituted T may be
+     identical to ARGS (if each level was pairwise identical).  */
+  if (maybe_reuse == NULL_TREE
+      && TMPL_ARGS_HAVE_MULTIPLE_LEVELS (t)
+      && TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args))
+    maybe_reuse = args;
+
+  /* Return MAYBE_REUSE and avoid allocating a new TREE_VEC if the substituted
+     result is identical to it.  */
+  if (NON_ERROR (maybe_reuse) != NULL_TREE
+      && TREE_VEC_LENGTH (maybe_reuse) == len
+      && std::equal (elts, elts+len, TREE_VEC_BEGIN (maybe_reuse)))
+    return maybe_reuse;
+
   /* Make space for the expanded arguments coming from template
      argument packs.  */
-  t = make_tree_vec (len + expanded_len_adjust);
-  /* ORIG_T can contain TREE_VECs. That happens if ORIG_T contains the
+  tree r = make_tree_vec (len + expanded_len_adjust);
+  /* T can contain TREE_VECs. That happens if T contains the
      arguments for a member template.
-     In that case each TREE_VEC in ORIG_T represents a level of template
-     arguments, and ORIG_T won't carry any non defaulted argument count.
+     In that case each TREE_VEC in T represents a level of template
+     arguments, and T won't carry any non defaulted argument count.
      It will rather be the nested TREE_VECs that will carry one.
-     In other words, ORIG_T carries a non defaulted argument count only
+     In other words, T carries a non defaulted argument count only
      if it doesn't contain any nested TREE_VEC.  */
-  if (NON_DEFAULT_TEMPLATE_ARGS_COUNT (orig_t))
+  if (NON_DEFAULT_TEMPLATE_ARGS_COUNT (t))
     {
-      int count = GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (orig_t);
+      int count = GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (t);
       count += expanded_len_adjust;
-      SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (t, count);
+      SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (r, count);
     }
-  for (i = 0, out = 0; i < len; i++)
+  for (int i = 0, out = 0; i < len; i++)
     {
-      tree orig_arg = TREE_VEC_ELT (orig_t, i);
+      tree orig_arg = TREE_VEC_ELT (t, i);
       if (orig_arg
 	  && (PACK_EXPANSION_P (orig_arg) || ARGUMENT_PACK_P (orig_arg))
           && TREE_CODE (elts[i]) == TREE_VEC)
         {
-          int idx;
-
           /* Now expand the template argument pack "in place".  */
-          for (idx = 0; idx < TREE_VEC_LENGTH (elts[i]); idx++, out++)
-            TREE_VEC_ELT (t, out) = TREE_VEC_ELT (elts[i], idx);
+	  for (int idx = 0; idx < TREE_VEC_LENGTH (elts[i]); idx++, out++)
+	    TREE_VEC_ELT (r, out) = TREE_VEC_ELT (elts[i], idx);
         }
       else
         {
-          TREE_VEC_ELT (t, out) = elts[i];
+	  TREE_VEC_ELT (r, out) = elts[i];
           out++;
         }
     }
 
-  return t;
+  return r;
 }
 
 /* Substitute ARGS into one level PARMS of template parameters.  */
@@ -14965,32 +15005,21 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
 
 	    if (!spec)
 	      {
-		int args_depth = TMPL_ARGS_DEPTH (args);
-		int parms_depth = TMPL_ARGS_DEPTH (DECL_TI_ARGS (t));
 		tmpl = DECL_TI_TEMPLATE (t);
 		gen_tmpl = most_general_template (tmpl);
-		if (args_depth == parms_depth
-		    && !PRIMARY_TEMPLATE_P (gen_tmpl))
-		  /* The DECL_TI_ARGS in this case are the generic template
-		     arguments for the enclosing class template, so we can
-		     shortcut substitution (which would just be the identity
-		     mapping).  */
-		  argvec = args;
-		else
-		  {
 		argvec = tsubst (DECL_TI_ARGS (t), args, complain, in_decl);
-		    /* Coerce the innermost arguments again if necessary.  If
-		       there's fewer levels of args than of parms, then the
-		       substitution could not have changed the innermost args
-		       (modulo level lowering).  */
-		    if (args_depth >= parms_depth && argvec != error_mark_node)
+		if (argvec != error_mark_node
+		    && PRIMARY_TEMPLATE_P (gen_tmpl)
+		    && TMPL_ARGS_DEPTH (args) >= TMPL_ARGS_DEPTH (argvec))
+		    /* We're fully specializing a template declaration, so
+		       we need to coerce the innermost arguments corresponding
+		       to the template.  */
 		    argvec = (coerce_innermost_template_parms
 			      (DECL_TEMPLATE_PARMS (gen_tmpl),
 			       argvec, t, complain,
 			       /*all*/true, /*defarg*/true));
 		if (argvec == error_mark_node)
 		  RETURN (error_mark_node);
-		  }
 		hash = spec_hasher::hash (gen_tmpl, argvec);
 		spec = retrieve_specialization (gen_tmpl, argvec, hash);
 	      }
diff --git a/gcc/testsuite/g++.dg/cpp0x/variadic183.C b/gcc/testsuite/g++.dg/cpp0x/variadic183.C
new file mode 100644
index 00000000000..3938e52e0e7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/variadic183.C
@@ -0,0 +1,14 @@
+// PR c++/105956
+// { dg-do compile { target c++11 } }
+
+template<int... Ns> struct list;
+
+template<int... Ns> struct impl {
+  static const int idx = 0;
+  using type = list<(idx + Ns)...>;
+
+  static constexpr const int* a[2] = {(Ns, &idx)...};
+  static_assert(a[0] == &idx && a[1] == &idx, "");
+};
+
+template struct impl<0, 1>;
