This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: PING: C++0x variadic templates fixes


On 8/17/07, Jason Merrill <jason@redhat.com> wrote:
>  >  http://gcc.gnu.org/ml/gcc-patches/2007-07/msg00365.html
>
> >       (tsubst_pack_expansion): We our substitution of a parameter pack
> >       is a "trivial" substitution of itself, just substitute into the
>
> We?

Heh, "When" was intended there...

> Your changes to convert_template_argument cause check_arg to be used
> much more.  Do we still need the distinction between arg and check_arg?

We need to keep the original argument around in some cases, for error
messages and to safely back out of some error conditions. I've taken
another sweep through this routine: we now work on just "arg", which
plays the same role as "check_arg" did before. We keep around
"orig_arg" for those cases where we really need the original argument
(before we saw through the TYPE_PACK_EXPANSION).

> > +          if ((TYPE_P (pattern) && same_type_p (pattern, parm_pack))
> > +              || (!TYPE_P (pattern) && cp_tree_equal (parm_pack, pattern)))
> > +            /* 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.  */

It happens in the test for PR 31993, here:

  template<typename...> struct A;

  template<template<int> class... T> struct A<T<0>...>
  {
    template<int> struct B {};
    B<0> b;
  };

When we lookup 'B<0>' (in lookup_template_class), we find
A<T<0>...>::B<0>. In performing the substitution for the template
arguments of A<T<0>...>::B, we substitute both the outer level of
arguments and the inner level of arguments. In the outer level of
arguments, we end up substituting the expansion of 'T' for 'T'.
Normally, substituting into an expansion produces a new argument pack,
but not here: we want to retain the pack expansion because nothing is
actually being expanded.

> > +                /* We can get a pack expansion here when we are
> > +                   instantiating a member template and ARG is a pack
> > +                   expansion from the outer template.  */
> > +                if (PACK_EXPANSION_P (arg))
> > +                  arg = PACK_EXPANSION_PATTERN (arg);
>
> Can you give me an example of how these two situations come up?  It's
> not clear to me how the testcases relate to the fixes.

Ah, this case no longer happens here, because we've dealt with it in
tsubst_pack_expansion above. I've removed this cruft.

Here's an updated patch. Tested i686-pc-linux-gnu, no regressions. It
fixes the two PRs mentioned in the original e-mail (32252 and 31993).

  - Doug

2007-10-26  Douglas Gregor  <doug.gregor@gmail.com>

	PR c++/31993
	PR c++/32252
	* pt.c (find_parameter_packs_r): Fix typo in comment.
	(convert_template_argument): Look at the pattern of a pack
	expansion to determine what kind of entity we're converting.
	(coerce_template_parameter_pack): When we have coerced a non-type
	template parameter pack, substitute into the type of that pack.
	(tsubst_pack_expansion): When our substitution of a parameter pack
	is a "trivial" substitution of itself, just substitute into the
	pack expansion rather than actually expanding.

2007-10-26  Douglas Gregor  <doug.gregor@gmail.com>

	* g++.dg/cpp0x/pr31993.C: New
	* g++.dg/cpp0x/pr32252.C: New
Index: testsuite/g++.dg/cpp0x/pr31993.C
===================================================================
--- testsuite/g++.dg/cpp0x/pr31993.C	(revision 0)
+++ testsuite/g++.dg/cpp0x/pr31993.C	(revision 0)
@@ -0,0 +1,9 @@
+// { dg-options "-std=c++0x" }
+
+template<typename...> struct A;
+
+template<template<int> class... T> struct A<T<0>...>
+{
+  template<int> struct B {};
+  B<0> b;
+};
Index: testsuite/g++.dg/cpp0x/pr32252.C
===================================================================
--- testsuite/g++.dg/cpp0x/pr32252.C	(revision 0)
+++ testsuite/g++.dg/cpp0x/pr32252.C	(revision 0)
@@ -0,0 +1,8 @@
+// { dg-options "-std=c++0x" }
+int x[5];
+
+template<int M, int N, int (&... p)[N]> struct A;
+
+template<int M> struct A<M,5,x> {};
+
+A<0,5,x> a;  
Index: cp/pt.c
===================================================================
--- cp/pt.c	(revision 129655)
+++ cp/pt.c	(working copy)
@@ -2416,9 +2416,9 @@ struct find_parameter_pack_data 
   struct pointer_set_t *visited;
 };
 
-/* Identifiers all of the argument packs that occur in a template
+/* Identifies all of the argument packs that occur in a template
    argument and appends them to the TREE_LIST inside DATA, which is a
-   find_parameter_pack_Data structure. This is a subroutine of
+   find_parameter_pack_data structure. This is a subroutine of
    make_pack_expansion and uses_parameter_packs.  */
 static tree
 find_parameter_packs_r (tree *tp, int *walk_subtrees, void* data)
@@ -4657,9 +4657,9 @@ convert_template_argument (tree parm,
 			   int i,
 			   tree in_decl)
 {
+  tree orig_arg;
   tree val;
   int is_type, requires_type, is_tmpl_type, requires_tmpl_type;
-  tree check_arg = arg;
 
   if (TREE_CODE (arg) == TREE_LIST
       && TREE_CODE (TREE_VALUE (arg)) == OFFSET_REF)
@@ -4669,24 +4669,26 @@ convert_template_argument (tree parm,
 	 invalid, but static members are OK.  In any
 	 case, grab the underlying fields/functions
 	 and issue an error later if required.  */
-      arg = TREE_VALUE (arg);
+      orig_arg = TREE_VALUE (arg);
       TREE_TYPE (arg) = unknown_type_node;
     }
 
+  orig_arg = arg;
+
   requires_tmpl_type = TREE_CODE (parm) == TEMPLATE_DECL;
   requires_type = (TREE_CODE (parm) == TYPE_DECL
 		   || requires_tmpl_type);
 
   /* When determining whether an argument pack expansion is a template,
      look at the pattern.  */
-  if (TREE_CODE (check_arg) == TYPE_PACK_EXPANSION)
-    check_arg = PACK_EXPANSION_PATTERN (check_arg);
+  if (TREE_CODE (arg) == TYPE_PACK_EXPANSION)
+    arg = PACK_EXPANSION_PATTERN (arg);
 
   is_tmpl_type = 
-    ((TREE_CODE (check_arg) == TEMPLATE_DECL
-      && TREE_CODE (DECL_TEMPLATE_RESULT (check_arg)) == TYPE_DECL)
-     || TREE_CODE (check_arg) == TEMPLATE_TEMPLATE_PARM
-     || TREE_CODE (check_arg) == UNBOUND_CLASS_TEMPLATE);
+    ((TREE_CODE (arg) == TEMPLATE_DECL
+      && TREE_CODE (DECL_TEMPLATE_RESULT (arg)) == TYPE_DECL)
+     || TREE_CODE (arg) == TEMPLATE_TEMPLATE_PARM
+     || TREE_CODE (arg) == UNBOUND_CLASS_TEMPLATE);
 
   if (is_tmpl_type
       && (TREE_CODE (arg) == TEMPLATE_TEMPLATE_PARM
@@ -4699,12 +4701,13 @@ convert_template_argument (tree parm,
       && TREE_CODE (TREE_OPERAND (arg, 0)) == TEMPLATE_TYPE_PARM)
     {
       pedwarn ("to refer to a type member of a template parameter, "
-	       "use %<typename %E%>", arg);
+	       "use %<typename %E%>", orig_arg);
 
-      arg = make_typename_type (TREE_OPERAND (arg, 0),
-				TREE_OPERAND (arg, 1),
-				typename_type,
-				complain & tf_error);
+      orig_arg = make_typename_type (TREE_OPERAND (arg, 0),
+				     TREE_OPERAND (arg, 1),
+				     typename_type,
+				     complain & tf_error);
+      arg = orig_arg;
       is_type = 1;
     }
   if (is_type != requires_type)
@@ -4719,11 +4722,11 @@ convert_template_argument (tree parm,
 	      if (is_type)
 		error ("  expected a constant of type %qT, got %qT",
 		       TREE_TYPE (parm),
-		       (is_tmpl_type ? DECL_NAME (arg) : arg));
+		       (is_tmpl_type ? DECL_NAME (arg) : orig_arg));
 	      else if (requires_tmpl_type)
-		error ("  expected a class template, got %qE", arg);
+		error ("  expected a class template, got %qE", orig_arg);
 	      else
-		error ("  expected a type, got %qE", arg);
+		error ("  expected a type, got %qE", orig_arg);
 	    }
 	}
       return error_mark_node;
@@ -4738,7 +4741,7 @@ convert_template_argument (tree parm,
 	  if (is_tmpl_type)
 	    error ("  expected a type, got %qT", DECL_NAME (arg));
 	  else
-	    error ("  expected a class template, got %qT", arg);
+	    error ("  expected a class template, got %qT", orig_arg);
 	}
       return error_mark_node;
     }
@@ -4756,19 +4759,13 @@ convert_template_argument (tree parm,
 	      tree parmparm = DECL_INNERMOST_TEMPLATE_PARMS (parm);
 	      tree argparm;
 
-              check_arg = arg;
-              /* When determining whether a pack expansion is a template,
-                 look at the pattern.  */
-              if (TREE_CODE (check_arg) == TYPE_PACK_EXPANSION)
-                check_arg = PACK_EXPANSION_PATTERN (check_arg);
-
-              argparm = DECL_INNERMOST_TEMPLATE_PARMS (check_arg);
+              argparm = DECL_INNERMOST_TEMPLATE_PARMS (arg);
 
 	      if (coerce_template_template_parms (parmparm, argparm,
 						  complain, in_decl,
 						  args))
 		{
-		  val = arg;
+		  val = orig_arg;
 
 		  /* TEMPLATE_TEMPLATE_PARM node is preferred over
 		     TEMPLATE_DECL.  */
@@ -4777,9 +4774,9 @@ convert_template_argument (tree parm,
                       if (DECL_TEMPLATE_TEMPLATE_PARM_P (val))
                         val = TREE_TYPE (val);
                       else if (TREE_CODE (val) == TYPE_PACK_EXPANSION
-                               && DECL_TEMPLATE_TEMPLATE_PARM_P (check_arg))
+                               && DECL_TEMPLATE_TEMPLATE_PARM_P (arg))
                         {
-                          val = TREE_TYPE (check_arg);
+                          val = TREE_TYPE (arg);
                           val = make_pack_expansion (val);
                         }
                     }
@@ -4792,7 +4789,7 @@ convert_template_argument (tree parm,
 			     "template parameter list for %qD",
 			     i + 1, in_decl);
 		      error ("  expected a template of type %qD, got %qD",
-			     parm, arg);
+			     parm, orig_arg);
 		    }
 
 		  val = error_mark_node;
@@ -4800,7 +4797,7 @@ convert_template_argument (tree parm,
 	    }
 	}
       else
-	val = arg;
+	val = orig_arg;
       /* We only form one instance of each template specialization.
 	 Therefore, if we use a non-canonical variant (i.e., a
 	 typedef), any future messages referring to the type will use
@@ -4816,7 +4813,7 @@ convert_template_argument (tree parm,
       if (invalid_nontype_parm_type_p (t, complain))
 	return error_mark_node;
 
-      if (!uses_template_parms (arg) && !uses_template_parms (t))
+      if (!uses_template_parms (orig_arg) && !uses_template_parms (t))
 	/* We used to call digest_init here.  However, digest_init
 	   will report errors, which we don't want when complain
 	   is zero.  More importantly, digest_init will try too
@@ -4827,14 +4824,14 @@ convert_template_argument (tree parm,
 	   conversions can occur is part of determining which
 	   function template to call, or whether a given explicit
 	   argument specification is valid.  */
-	val = convert_nontype_argument (t, arg);
+	val = convert_nontype_argument (t, orig_arg);
       else
-	val = arg;
+	val = orig_arg;
 
       if (val == NULL_TREE)
 	val = error_mark_node;
       else if (val == error_mark_node && (complain & tf_error))
-	error ("could not convert template argument %qE to %qT",  arg, t);
+	error ("could not convert template argument %qE to %qT",  orig_arg, t);
     }
 
   return val;
@@ -4948,7 +4945,8 @@ coerce_template_parameter_pack (tree par
   else
     {
       argument_pack = make_node (NONTYPE_ARGUMENT_PACK);
-      TREE_TYPE (argument_pack) = TREE_TYPE (TREE_VALUE (parm));
+      TREE_TYPE (argument_pack) 
+        = tsubst (TREE_TYPE (TREE_VALUE (parm)), args, complain, in_decl);
       TREE_CONSTANT (argument_pack) = 1;
     }
 
@@ -7097,6 +7095,22 @@ tsubst_pack_expansion (tree t, tree args
 	  return result;
 	}
 
+      if (arg_pack
+          && TREE_VEC_LENGTH (ARGUMENT_PACK_ARGS (arg_pack)) == 1
+          && PACK_EXPANSION_P (TREE_VEC_ELT (ARGUMENT_PACK_ARGS (arg_pack), 0)))
+        {
+          tree expansion = TREE_VEC_ELT (ARGUMENT_PACK_ARGS (arg_pack), 0);
+          tree pattern = PACK_EXPANSION_PATTERN (expansion);
+          if ((TYPE_P (pattern) && same_type_p (pattern, parm_pack))
+              || (!TYPE_P (pattern) && cp_tree_equal (parm_pack, pattern)))
+            /* 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 = 

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]