re PR c++/54440 ([c++11] g++ prematurely applying rule that a template parameter...
authorJason Merrill <jason@redhat.com>
Wed, 26 Feb 2014 17:08:20 +0000 (12:08 -0500)
committerJason Merrill <jason@gcc.gnu.org>
Wed, 26 Feb 2014 17:08:20 +0000 (12:08 -0500)
PR c++/54440
* pt.c (get_template_parm_index): New.
(fixed_parameter_pack_p_1, fixed_parameter_pack_p): New.
(process_template_parm): Allow bare packs in template template
parm template parms.
(coerce_template_parameter_pack): Handle fixed template template
parm packs and fixed packs not at the end of the parm list.
(coerce_template_parms): Handle template parm packs not at the end
of the parm list.
(gen_elem_of_pack_expansion_instantiation): Handle a decl expansion.

From-SVN: r208178

gcc/cp/ChangeLog
gcc/cp/pt.c
gcc/testsuite/g++.dg/cpp0x/variadic151.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/variadic152.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/variadic74.C

index b2e687c..f1f1195 100644 (file)
@@ -1,5 +1,16 @@
 2014-02-26  Jason Merrill  <jason@redhat.com>
 
+       PR c++/54440
+       * pt.c (get_template_parm_index): New.
+       (fixed_parameter_pack_p_1, fixed_parameter_pack_p): New.
+       (process_template_parm): Allow bare packs in template template
+       parm template parms.
+       (coerce_template_parameter_pack): Handle fixed template template
+       parm packs and fixed packs not at the end of the parm list.
+       (coerce_template_parms): Handle template parm packs not at the end
+       of the parm list.
+       (gen_elem_of_pack_expansion_instantiation): Handle a decl expansion.
+
        PR c++/60182
        * pt.c (unify): Ignore alias templates when deducing a template
        template parameter.
index d723311..1f5a2b7 100644 (file)
@@ -3697,7 +3697,10 @@ process_template_parm (tree list, location_t parm_loc, tree parm,
             return chainon (list, err_parm_list);
           }
 
-        if (uses_parameter_packs (TREE_TYPE (parm)) && !is_parameter_pack)
+        if (uses_parameter_packs (TREE_TYPE (parm)) && !is_parameter_pack
+           /* If we're in a nested template parameter list, the template
+              template parameter could be a parameter pack.  */
+           && processing_template_parmlist == 1)
          {
            /* This template parameter is not a parameter pack, but it
               should be. Complain about "bare" parameter packs.  */
@@ -4326,6 +4329,77 @@ process_partial_specialization (tree decl)
   return decl;
 }
 
+/* PARM is a template parameter of some form; return the corresponding
+   TEMPLATE_PARM_INDEX.  */
+
+static tree
+get_template_parm_index (tree parm)
+{
+  if (TREE_CODE (parm) == PARM_DECL
+      || TREE_CODE (parm) == CONST_DECL)
+    parm = DECL_INITIAL (parm);
+  else if (TREE_CODE (parm) == TYPE_DECL
+          || TREE_CODE (parm) == TEMPLATE_DECL)
+    parm = TREE_TYPE (parm);
+  if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM
+      || TREE_CODE (parm) == TEMPLATE_TEMPLATE_PARM)
+    parm = TEMPLATE_TYPE_PARM_INDEX (parm);
+  gcc_assert (TREE_CODE (parm) == TEMPLATE_PARM_INDEX);
+  return parm;
+}
+
+/* Subroutine of fixed_parameter_pack_p below.  Look for any template
+   parameter packs used by the template parameter PARM.  */
+
+static void
+fixed_parameter_pack_p_1 (tree parm, struct find_parameter_pack_data *ppd)
+{
+  /* A type parm can't refer to another parm.  */
+  if (TREE_CODE (parm) == TYPE_DECL)
+    return;
+  else if (TREE_CODE (parm) == PARM_DECL)
+    {
+      cp_walk_tree (&TREE_TYPE (parm), &find_parameter_packs_r,
+                   ppd, ppd->visited);
+      return;
+    }
+
+  gcc_assert (TREE_CODE (parm) == TEMPLATE_DECL);
+
+  tree vec = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (parm));
+  for (int i = 0; i < TREE_VEC_LENGTH (vec); ++i)
+    fixed_parameter_pack_p_1 (TREE_VALUE (TREE_VEC_ELT (vec, i)), ppd);
+}
+
+/* PARM is a template parameter pack.  Return any parameter packs used in
+   its type or the type of any of its template parameters.  If there are
+   any such packs, it will be instantiated into a fixed template parameter
+   list by partial instantiation rather than be fully deduced.  */
+
+tree
+fixed_parameter_pack_p (tree parm)
+{
+  /* This can only be true in a member template.  */
+  if (TEMPLATE_PARM_ORIG_LEVEL (get_template_parm_index (parm)) < 2)
+    return NULL_TREE;
+  /* This can only be true for a parameter pack.  */
+  if (!template_parameter_pack_p (parm))
+    return NULL_TREE;
+  /* A type parm can't refer to another parm.  */
+  if (TREE_CODE (parm) == TYPE_DECL)
+    return NULL_TREE;
+
+  tree parameter_packs = NULL_TREE;
+  struct find_parameter_pack_data ppd;
+  ppd.parameter_packs = &parameter_packs;
+  ppd.visited = pointer_set_create ();
+
+  fixed_parameter_pack_p_1 (parm, &ppd);
+
+  pointer_set_destroy (ppd.visited);
+  return parameter_packs;
+}
+
 /* Check that a template declaration's use of default arguments and
    parameter packs is not invalid.  Here, PARMS are the template
    parameters.  IS_PRIMARY is true if DECL is the thing declared by
@@ -4431,7 +4505,10 @@ check_default_tmpl_args (tree decl, tree parms, bool is_primary,
                       && parm_level == parms
                       && TREE_CODE (decl) == TYPE_DECL
                       && i < ntparms - 1
-                      && template_parameter_pack_p (TREE_VALUE (parm)))
+                      && template_parameter_pack_p (TREE_VALUE (parm))
+                      /* A fixed parameter pack will be partially
+                         instantiated into a fixed length list.  */
+                      && !fixed_parameter_pack_p (TREE_VALUE (parm)))
                {
                  /* A primary class template can only have one
                     parameter pack, at the end of the template
@@ -6531,58 +6608,53 @@ coerce_template_parameter_pack (tree parms,
   int nargs = inner_args ? NUM_TMPL_ARGS (inner_args) : 0;
   tree packed_args;
   tree argument_pack;
-  tree packed_types = NULL_TREE;
+  tree packed_parms = NULL_TREE;
 
   if (arg_idx > nargs)
     arg_idx = nargs;
 
-  packed_args = make_tree_vec (nargs - arg_idx);
-
-  if (TREE_CODE (TREE_VALUE (parm)) == PARM_DECL
-      && uses_parameter_packs (TREE_TYPE (TREE_VALUE (parm))))
-    {
-      /* When the template parameter is a non-type template
-         parameter pack whose type uses parameter packs, we need
-         to look at each of the template arguments
-         separately. Build a vector of the types for these
-         non-type template parameters in PACKED_TYPES.  */
-      tree expansion 
-        = make_pack_expansion (TREE_TYPE (TREE_VALUE (parm)));
-      packed_types = tsubst_pack_expansion (expansion, args,
-                                            complain, in_decl);
-
-      if (packed_types == error_mark_node)
+  if (tree packs = fixed_parameter_pack_p (TREE_VALUE (parm)))
+    {
+      /* When the template parameter is a non-type template parameter pack
+         or template template parameter pack whose type or template
+         parameters use parameter packs, we know exactly how many arguments
+         we are looking for.  Build a vector of the instantiated decls for
+         these template parameters in PACKED_PARMS.  */
+      /* We can't use make_pack_expansion here because it would interpret a
+        _DECL as a use rather than a declaration.  */
+      tree decl = TREE_VALUE (parm);
+      tree exp = cxx_make_type (TYPE_PACK_EXPANSION);
+      SET_PACK_EXPANSION_PATTERN (exp, decl);
+      PACK_EXPANSION_PARAMETER_PACKS (exp) = packs;
+      SET_TYPE_STRUCTURAL_EQUALITY (exp);
+
+      TREE_VEC_LENGTH (args)--;
+      packed_parms = tsubst_pack_expansion (exp, args, complain, decl);
+      TREE_VEC_LENGTH (args)++;
+
+      if (packed_parms == error_mark_node)
         return error_mark_node;
 
-      /* Check that we have the right number of arguments.  */
-      if (arg_idx < nargs
-          && !PACK_EXPANSION_P (TREE_VEC_ELT (inner_args, arg_idx))
-          && nargs - arg_idx != TREE_VEC_LENGTH (packed_types))
-        {
-          int needed_parms 
-            = TREE_VEC_LENGTH (parms) - 1 + TREE_VEC_LENGTH (packed_types);
-          error ("wrong number of template arguments (%d, should be %d)",
-                 nargs, needed_parms);
-          return error_mark_node;
-        }
-
-      /* If we aren't able to check the actual arguments now
-         (because they haven't been expanded yet), we can at least
+      /* If we're doing a partial instantiation of a member template,
          verify that all of the types used for the non-type
          template parameter pack are, in fact, valid for non-type
          template parameters.  */
-      if (arg_idx < nargs 
+      if (arg_idx < nargs
           && PACK_EXPANSION_P (TREE_VEC_ELT (inner_args, arg_idx)))
         {
-          int j, len = TREE_VEC_LENGTH (packed_types);
+          int j, len = TREE_VEC_LENGTH (packed_parms);
           for (j = 0; j < len; ++j)
             {
-              tree t = TREE_VEC_ELT (packed_types, j);
+              tree t = TREE_TYPE (TREE_VEC_ELT (packed_parms, j));
               if (invalid_nontype_parm_type_p (t, complain))
                 return error_mark_node;
             }
         }
+
+      packed_args = make_tree_vec (TREE_VEC_LENGTH (packed_parms));
     }
+  else
+    packed_args = make_tree_vec (nargs - arg_idx);
 
   /* Convert the remaining arguments, which will be a part of the
      parameter pack "parm".  */
@@ -6590,18 +6662,19 @@ coerce_template_parameter_pack (tree parms,
     {
       tree arg = TREE_VEC_ELT (inner_args, arg_idx);
       tree actual_parm = TREE_VALUE (parm);
+      int pack_idx = arg_idx - parm_idx;
 
-      if (packed_types && !PACK_EXPANSION_P (arg))
+      if (packed_parms)
         {
-          /* When we have a vector of types (corresponding to the
-             non-type template parameter pack that uses parameter
-             packs in its type, as mention above), and the
-             argument is not an expansion (which expands to a
-             currently unknown number of arguments), clone the
-             parm and give it the next type in PACKED_TYPES.  */
-          actual_parm = copy_node (actual_parm);
-          TREE_TYPE (actual_parm) = 
-            TREE_VEC_ELT (packed_types, arg_idx - parm_idx);
+         /* Once we've packed as many args as we have types, stop.  */
+         if (pack_idx >= TREE_VEC_LENGTH (packed_parms))
+           break;
+         else if (PACK_EXPANSION_P (arg))
+           /* We don't know how many args we have yet, just
+              use the unconverted ones for now.  */
+           return NULL_TREE;
+         else
+           actual_parm = TREE_VEC_ELT (packed_parms, pack_idx);
         }
 
       if (arg == error_mark_node)
@@ -6615,7 +6688,15 @@ coerce_template_parameter_pack (tree parms,
                                         in_decl);
       if (arg == error_mark_node)
         (*lost)++;
-      TREE_VEC_ELT (packed_args, arg_idx - parm_idx) = arg; 
+      TREE_VEC_ELT (packed_args, pack_idx) = arg;
+    }
+
+  if (arg_idx - parm_idx < TREE_VEC_LENGTH (packed_args)
+      && TREE_VEC_LENGTH (packed_args) > 0)
+    {
+      error ("wrong number of template arguments (%d, should be %d)",
+            arg_idx - parm_idx, TREE_VEC_LENGTH (packed_args));
+      return error_mark_node;
     }
 
   if (TREE_CODE (TREE_VALUE (parm)) == TYPE_DECL
@@ -6676,6 +6757,7 @@ coerce_template_parms (tree parms,
                       bool use_default_args)
 {
   int nparms, nargs, parm_idx, arg_idx, lost = 0;
+  tree orig_inner_args;
   tree inner_args;
   tree new_args;
   tree new_inner_args;
@@ -6705,7 +6787,7 @@ coerce_template_parms (tree parms,
        ++variadic_p;
     }
 
-  inner_args = INNERMOST_TEMPLATE_ARGS (args);
+  inner_args = orig_inner_args = INNERMOST_TEMPLATE_ARGS (args);
   /* If there are no parameters that follow a parameter pack, we need to
      expand any argument packs so that we can deduce a parameter pack from
      some non-packed args followed by an argument pack, as in variadic85.C.
@@ -6783,6 +6865,7 @@ coerce_template_parms (tree parms,
   c_inhibit_evaluation_warnings = 0;
   new_inner_args = make_tree_vec (nparms);
   new_args = add_outermost_template_args (args, new_inner_args);
+  int pack_adjust = 0;
   for (parm_idx = 0, arg_idx = 0; parm_idx < nparms; parm_idx++, arg_idx++)
     {
       tree arg;
@@ -6806,22 +6889,36 @@ coerce_template_parms (tree parms,
       if (template_parameter_pack_p (TREE_VALUE (parm))
          && !(arg && ARGUMENT_PACK_P (arg)))
         {
-         /* All remaining arguments will be placed in the
+         /* Some arguments will be placed in the
             template parameter pack PARM.  */
          arg = coerce_template_parameter_pack (parms, parm_idx, args, 
                                                inner_args, arg_idx,
                                                new_args, &lost,
                                                in_decl, complain);
 
-          /* Store this argument.  */
-          if (arg == error_mark_node)
-            lost++;
-         if (lost)
-           break;
+         if (arg == NULL_TREE)
+           {
+             /* We don't know how many args we have yet, just use the
+                unconverted (and still packed) ones for now.  */
+             new_inner_args = orig_inner_args;
+             arg_idx = nargs;
+             break;
+           }
+
           TREE_VEC_ELT (new_inner_args, parm_idx) = arg;
 
-         /* We are done with all of the arguments.  */
-         arg_idx = nargs;
+          /* Store this argument.  */
+          if (arg == error_mark_node)
+           {
+             lost++;
+             /* We are done with all of the arguments.  */
+             arg_idx = nargs;
+           }
+         else
+           {
+             pack_adjust = TREE_VEC_LENGTH (ARGUMENT_PACK_ARGS (arg)) - 1;
+             arg_idx += pack_adjust;
+           }
           
           continue;
         }
@@ -6832,6 +6929,7 @@ coerce_template_parms (tree parms,
               /* We don't know how many args we have yet, just
                  use the unconverted ones for now.  */
               new_inner_args = inner_args;
+             arg_idx = nargs;
               break;
             }
         }
@@ -6844,7 +6942,8 @@ coerce_template_parms (tree parms,
             is also the number of non-defaulted arguments in NEW_INNER_ARGS.
             Record that.  */
          if (!NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_inner_args))
-           SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_inner_args, arg_idx);
+           SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_inner_args,
+                                                arg_idx - pack_adjust);
        }
       else
        break;
@@ -6868,11 +6967,23 @@ coerce_template_parms (tree parms,
 
       if (arg == error_mark_node)
        lost++;
-      TREE_VEC_ELT (new_inner_args, arg_idx) = arg;
+      TREE_VEC_ELT (new_inner_args, arg_idx - pack_adjust) = arg;
     }
   cp_unevaluated_operand = saved_unevaluated_operand;
   c_inhibit_evaluation_warnings = saved_inhibit_evaluation_warnings;
 
+  if (variadic_p && arg_idx < nargs)
+    {
+      if (complain & tf_error)
+       {
+         error ("wrong number of template arguments "
+                "(%d, should be %d)", nargs, arg_idx);
+         if (in_decl)
+           error ("provided for %q+D", in_decl);
+       }
+      return error_mark_node;
+    }
+
   if (lost)
     return error_mark_node;
 
@@ -9509,7 +9620,11 @@ gen_elem_of_pack_expansion_instantiation (tree pattern,
 
   /* Substitute into the PATTERN with the (possibly altered)
      arguments.  */
-  if (!TYPE_P (pattern))
+  if (pattern == in_decl)
+    /* Expanding a fixed parameter pack from
+       coerce_template_parameter_pack.  */
+    t = tsubst_decl (pattern, args, complain);
+  else if (!TYPE_P (pattern))
     t = tsubst_expr (pattern, args, complain, in_decl,
                     /*integral_constant_expression_p=*/false);
   else
diff --git a/gcc/testsuite/g++.dg/cpp0x/variadic151.C b/gcc/testsuite/g++.dg/cpp0x/variadic151.C
new file mode 100644 (file)
index 0000000..a65351d
--- /dev/null
@@ -0,0 +1,9 @@
+// PR c++/54440
+// { dg-do compile { target c++11 } }
+
+template <class...T> struct A
+{
+  template <T... t, class U> struct B { };
+};
+
+A<int,char>::B<42,'a',float> b;
diff --git a/gcc/testsuite/g++.dg/cpp0x/variadic152.C b/gcc/testsuite/g++.dg/cpp0x/variadic152.C
new file mode 100644 (file)
index 0000000..d7dccc5
--- /dev/null
@@ -0,0 +1,12 @@
+// PR c++/54440
+// { dg-do compile { target c++11 } }
+
+template <class...T> struct A
+{
+  template <template <T> class... TP, class U> struct B { };
+};
+
+template <int I> struct C { };
+template <char C> struct D { };
+
+A<int,char>::B<C,D,float> b;
index 312fe9d..1af6531 100644 (file)
@@ -24,3 +24,5 @@ A<int*, float*>::X<&i> apple2; // { dg-error "wrong number of template arguments
 A<int*, float*>::X<&i, &f, &f> apple3; // { dg-error "wrong number of template arguments" "wrong number" }
 // { dg-error "invalid type" "invalid" { target *-*-* } 24 }
 A<int, float> apple4;
+
+// { dg-prune-output "provided for" }