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]

[C++ PATCH] Robustify variadic templates against user errors


This patch fixes a bunch of PRs (8 of them, in fact) that all deal
with ice-on-invalid-code failures with variadic templates. What was
happening before is that we would diagnose an error with variadic
templates (typically, that one has tried to use a parameter pack
somewhere where it can't be used), but we left the C++ AST in an
invalid state. So, later parts of the compiler would have an invalid
C++ AST to work with, and we'd get an ICE eventually.

This patch is relatively straightforward. The main change is to
find_parameter_packs_r, which can now be instructed to replace
parameter packs with ERROR_MARK_NODE in the tree. This way, we report
the error and then stick ERROR_MARK_NODEs in the places where we had
invalid parameter packs. As part of this, I changed
check_for_bare_parameter_packs to take in a pointer to its top-level
tree node, which may end up being replaced by ERROR_MARK_NODE.

Tested i686-pc-linux-gnu; no regressions. Okay for mainline?

  - Doug

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

	PR c++/32114
	PR c++/32115
	PR c++/32125
	PR c++/32126
	PR c++/32127
	PR c++/32128
	PR c++/32253
	PR c++/32566
	* type.c (check_return_expr): Pass address of retval to
	check_for_bare_parameter_packs.
	* tree.c (canonical_type_variant): Be careful with
	ERROR_MARK_NODE.
	* cp-tree.h (check_for_bare_parameter_packs): Now accepts a
	tree*.
	* pt.c (find_parameter_pack_data): Add set_packs_to_error field,
	which states whether parameter packs should be replaced with
	ERROR_MARK_NODE.
	(find_parameter_packs_r): Pass addresses to cp_walk_tree wherever
	possible. If set_packs_to_error is set true, replace the parameter
	pack with ERROR_MARK_NODE.
	(uses_parameter_packs): Don't set parameter packs to
	ERROR_MARK_NODE.
	(check_for_bare_parameter_packs): Now takes a pointer to a tree,
	which may be modified (if it is a parameter pack). Instructs
	find_parameter_packs_r to replace parameter packs with
	ERROR_MARK_NODE (so that they won't cause errors later on).
	(process_template_parm): Pass pointer to
	check_for_bare_parameter_packs.
	(process_partial_specialization): Replace pack expansions before
	the end of the template argument list with ERROR_MARK_NODE.
	(push_template_decl_real): Pass pointer to
	check_for_bare_parameter_packs. Replace parameter packs not at the
	end of the template parameter list with ERROR_MARK_NODE.
	(convert_template_argument): Be more careful about using DECL_NAME
	on only declarations.
	(unify): Can't unify against ERROR_MARK_NODE.
	* semantics.c (finish_cond): Pass pointer to
	check_for_bare_parameter_packs.
	(finish_expr_stmt): Ditto.
	(finish_for_expr): Ditto.
	(finish_switch_cond): Pass pointer to
	check_for_bare_parameter_packs, and call it before we put the
	condition into the statement.
	(finish_mem_initializers): Pass pointer to
	check_for_bare_parameter_packs.
	(finish_member_declaration): Ditto.
	* parser.c (cp_parser_base_clause): Ditto.
	

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

	* testsuite/g++.dg/parser/crash36.C: Tweak expected errors.
	* testsuite/g++.dg/cpp0x/pr32114.C: New.
	* testsuite/g++.dg/cpp0x/pr32115.C: New.
	* testsuite/g++.dg/cpp0x/pr32125.C: New.
	* testsuite/g++.dg/cpp0x/pr32126.C: New.
	* testsuite/g++.dg/cpp0x/pr32127.C: New.
	* testsuite/g++.dg/cpp0x/pr32128.C: New.
	* testsuite/g++.dg/cpp0x/pr32253.C: New.
	* testsuite/g++.dg/cpp0x/pr32566.C: New.
	* testsuite/g++.dg/cpp0x/pr31445.C: Tweak expected errors.
	* testsuite/g++.dg/cpp0x/pr31438.C: Ditto.
	* testsuite/g++.dg/cpp0x/variadic81.C: Ditto.
	* testsuite/g++.dg/cpp0x/pr31432.C: Ditto.
	* testsuite/g++.dg/cpp0x/pr31442.C: Ditto.
Index: testsuite/g++.dg/parse/crash36.C
===================================================================
--- testsuite/g++.dg/parse/crash36.C	(revision 129658)
+++ testsuite/g++.dg/parse/crash36.C	(working copy)
@@ -5,7 +5,7 @@
 template <typename... T> struct A	// { dg-error "does not include variadic templates" }
 {
   static T &t;				// { dg-error "not expanded with|T" }
-  static const int i = sizeof (++t);	// { dg-error "invalid use of template type parameter" }
+  static const int i = sizeof (++t);
 };
 
 int x[A <int>::i];	// { dg-error "is not an integral constant-expression" }
Index: testsuite/g++.dg/cpp0x/pr32126.C
===================================================================
--- testsuite/g++.dg/cpp0x/pr32126.C	(revision 0)
+++ testsuite/g++.dg/cpp0x/pr32126.C	(revision 0)
@@ -0,0 +1,10 @@
+// { dg-options "-std=c++0x" }
+template<typename...> struct A;
+
+template<typename...T> struct A<T> // { dg-error "not expanded|T|not used|T" }
+{
+ static int i;
+};
+
+A<char> a; // { dg-error "incomplete" }
+A<int> b; // { dg-error "incomplete" }
Index: testsuite/g++.dg/cpp0x/pr32114.C
===================================================================
--- testsuite/g++.dg/cpp0x/pr32114.C	(revision 0)
+++ testsuite/g++.dg/cpp0x/pr32114.C	(revision 0)
@@ -0,0 +1,7 @@
+// { dg-options "-std=c++0x" }
+template<typename ...T> struct A
+{
+  typedef typename T::X Y; // { dg-error "not expanded|T" }
+};
+
+A<int> a;
Index: testsuite/g++.dg/cpp0x/pr31432.C
===================================================================
--- testsuite/g++.dg/cpp0x/pr31432.C	(revision 129658)
+++ testsuite/g++.dg/cpp0x/pr31432.C	(working copy)
@@ -4,5 +4,5 @@ template<typename..., typename> struct A
  static int i;
 };
 
-A<int, int> a; // { dg-error "invalid type" }
-A<char,int> b; // { dg-error "invalid type" }
+A<int, int> a; // { dg-error "mismatch|expected|invalid type" }
+A<char,int> b; // { dg-error "mismatch|expected|invalid type" }
Index: testsuite/g++.dg/cpp0x/pr32127.C
===================================================================
--- testsuite/g++.dg/cpp0x/pr32127.C	(revision 0)
+++ testsuite/g++.dg/cpp0x/pr32127.C	(revision 0)
@@ -0,0 +1,7 @@
+// { dg-options "-std=c++0x" }
+template<typename...T> struct A
+{
+  static T i; // { dg-error "parameter packs|T" }
+};
+
+int j = A<int>::i; // { dg-error "not a member" }
Index: testsuite/g++.dg/cpp0x/pr32115.C
===================================================================
--- testsuite/g++.dg/cpp0x/pr32115.C	(revision 0)
+++ testsuite/g++.dg/cpp0x/pr32115.C	(revision 0)
@@ -0,0 +1,4 @@
+// { dg-options "-std=c++0x" }
+template<typename ...T, int = 0> struct A {}; // { dg-error "end of" }
+
+A<int> a; // { dg-error "mismatch|expected|invalid" }
Index: testsuite/g++.dg/cpp0x/pr31445.C
===================================================================
--- testsuite/g++.dg/cpp0x/pr31445.C	(revision 129658)
+++ testsuite/g++.dg/cpp0x/pr31445.C	(working copy)
@@ -1,8 +1,8 @@
 // { dg-options "-std=gnu++0x" }
 template <typename... T> struct A
 {
-  void foo(T...); // { dg-error "candidates" }
-  A(T... t) { foo(t); } // { dg-error "parameter packs|t|no matching" }
+  void foo(T...);
+  A(T... t) { foo(t); } // { dg-error "parameter packs|t" }
 };
 
 A<int> a(0);
Index: testsuite/g++.dg/cpp0x/pr32128.C
===================================================================
--- testsuite/g++.dg/cpp0x/pr32128.C	(revision 0)
+++ testsuite/g++.dg/cpp0x/pr32128.C	(revision 0)
@@ -0,0 +1,7 @@
+// { dg-options "-std=c++0x" }
+template<typename...> struct A;
+
+template<typename...T, typename...U> 
+  struct A<T..., U...> {}; // { dg-error "must be at the end" }
+
+A<int> a; // { dg-error "incomplete" }
Index: testsuite/g++.dg/cpp0x/pr32566.C
===================================================================
--- testsuite/g++.dg/cpp0x/pr32566.C	(revision 0)
+++ testsuite/g++.dg/cpp0x/pr32566.C	(revision 0)
@@ -0,0 +1,4 @@
+// { dg-options "-std=c++0x" }
+template<int...> struct A;
+
+template<template<int> class... T> struct A<T...> {}; // { dg-error "mismatch|expected" }
Index: testsuite/g++.dg/cpp0x/pr31442.C
===================================================================
--- testsuite/g++.dg/cpp0x/pr31442.C	(revision 129658)
+++ testsuite/g++.dg/cpp0x/pr31442.C	(working copy)
@@ -6,4 +6,4 @@ struct B
   template <template <typename...> class C> B(C<int>);
 };
 
-B b = A<int>();
+B b = A<int>(); // { dg-error "mismatch|expected" }
Index: testsuite/g++.dg/cpp0x/pr32125.C
===================================================================
--- testsuite/g++.dg/cpp0x/pr32125.C	(revision 0)
+++ testsuite/g++.dg/cpp0x/pr32125.C	(revision 0)
@@ -0,0 +1,8 @@
+// { dg-options "-std=c++0x" }
+template<typename...> struct A;
+
+template<typename...T> struct A<T*> // { dg-error "not expanded|T|not used|T" }
+{
+  A();
+  A(T); // { dg-error "not expanded|T" }
+};
Index: testsuite/g++.dg/cpp0x/pr31438.C
===================================================================
--- testsuite/g++.dg/cpp0x/pr31438.C	(revision 129658)
+++ testsuite/g++.dg/cpp0x/pr31438.C	(working copy)
@@ -1,9 +1,9 @@
 // { dg-options "-std=gnu++0x" }
 
-template<typename> struct A; // { dg-error "candidates" }
-template<typename T, typename... U> struct A<T(U)> // { dg-error "parameter packs|U" }
-{ // { dg-error "parameter packs|U" }
- template<typename X> A(X); // { dg-error "parameter packs|U" }
+template<typename> struct A;
+template<typename T, typename... U> struct A<T(U)> // { dg-error "parameter packs|U|not used|U" }
+{
+ template<typename X> A(X);
 };
 
-A<void(int)> a(0); // { dg-error "no matching" }
+A<void(int)> a(0); // { dg-error "incomplete type" }
Index: testsuite/g++.dg/cpp0x/variadic81.C
===================================================================
--- testsuite/g++.dg/cpp0x/variadic81.C	(revision 129658)
+++ testsuite/g++.dg/cpp0x/variadic81.C	(working copy)
@@ -3,9 +3,9 @@
 
 template<typename> struct A;
 
-template<typename... T> struct A<T*>  // { dg-error "not expanded|note" }
-{                                     // { dg-error "not expanded|note" }
+template<typename... T> struct A<T*>  // { dg-error "not expanded|T|not used|T" }
+{                                     
   struct B;
 };
 
-A<void*> a;
+A<void*> a; // { dg-error "incomplete" }
Index: testsuite/g++.dg/cpp0x/pr32253.C
===================================================================
--- testsuite/g++.dg/cpp0x/pr32253.C	(revision 0)
+++ testsuite/g++.dg/cpp0x/pr32253.C	(revision 0)
@@ -0,0 +1,9 @@
+// { dg-options "-std=c++0x" }
+template<void (*... fp)()> struct A
+{
+  A() { fp(); } // { dg-error "not expanded|fp" }
+};
+
+void foo();
+
+A<foo> a;
Index: cp/typeck.c
===================================================================
--- cp/typeck.c	(revision 129658)
+++ cp/typeck.c	(working copy)
@@ -6613,7 +6613,7 @@ check_return_expr (tree retval, bool *no
   if (processing_template_decl)
     {
       current_function_returns_value = 1;
-      check_for_bare_parameter_packs (retval);
+      check_for_bare_parameter_packs (&retval);
       return retval;
     }
 
Index: cp/tree.c
===================================================================
--- cp/tree.c	(revision 129658)
+++ cp/tree.c	(working copy)
@@ -846,6 +846,9 @@ cp_build_qualified_type_real (tree type,
 tree
 canonical_type_variant (tree t)
 {
+  if (t == error_mark_node)
+    return error_mark_node;
+
   return cp_build_qualified_type (TYPE_MAIN_VARIANT (t), cp_type_quals (t));
 }
 
Index: cp/cp-tree.h
===================================================================
--- cp/cp-tree.h	(revision 129658)
+++ cp/cp-tree.h	(working copy)
@@ -4436,7 +4436,7 @@ extern int comp_template_parms			(const_
 extern bool uses_parameter_packs                (tree);
 extern bool template_parameter_pack_p           (const_tree);
 extern tree make_pack_expansion                 (tree);
-extern bool check_for_bare_parameter_packs      (tree);
+extern bool check_for_bare_parameter_packs      (tree*);
 extern int template_class_depth			(tree);
 extern int is_specialization_of			(tree, tree);
 extern bool is_specialization_of_friend		(tree, tree);
Index: cp/pt.c
===================================================================
--- cp/pt.c	(revision 129658)
+++ cp/pt.c	(working copy)
@@ -2412,13 +2412,21 @@ make_ith_pack_parameter_name (tree name,
 /* Structure used to track the progress of find_parameter_pack_r.  */
 struct find_parameter_pack_data 
 {
+  /* TREE_LIST that will contain all of the parameter packs found by
+     the traversal.  */
   tree* parameter_packs;
+
+  /* Set of AST nodes that have been visited by the traversal.  */
   struct pointer_set_t *visited;
+
+  /* Whether we should replace parameter packs with
+     ERROR_MARK_NODE. Used by check_for_bare_parameter_packs.  */
+  bool set_packs_to_error;
 };
 
 /* Identifiers 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)
@@ -2428,10 +2436,8 @@ find_parameter_packs_r (tree *tp, int *w
     (struct find_parameter_pack_data*)data;
 
   if (TYPE_P (t))
-    {
-      tree context = TYPE_CONTEXT (t);
-      cp_walk_tree (&context, &find_parameter_packs_r, ppd, ppd->visited);
-    }
+    cp_walk_tree (&TYPE_CONTEXT (t), 
+		  &find_parameter_packs_r, ppd, ppd->visited);
 
   /* This switch statement will return immediately if we don't find a
      parameter pack.  */
@@ -2479,10 +2485,8 @@ find_parameter_packs_r (tree *tp, int *w
     case UNION_TYPE:
     case ENUMERAL_TYPE:
       if (TYPE_TEMPLATE_INFO (t))
-        {
-          tree args = TREE_VALUE (TYPE_TEMPLATE_INFO (t));
-          cp_walk_tree (&args, &find_parameter_packs_r, ppd, ppd->visited);
-        }
+	cp_walk_tree (&TREE_VALUE (TYPE_TEMPLATE_INFO (t)), 
+		      &find_parameter_packs_r, ppd, ppd->visited);
 
       *walk_subtrees = 0;
       return NULL_TREE;
@@ -2513,6 +2517,12 @@ find_parameter_packs_r (tree *tp, int *w
   /* Add this parameter pack to the list.  */
   *ppd->parameter_packs = tree_cons (NULL_TREE, t, *ppd->parameter_packs);
 
+  /* If the caller requested it, set this parameter pack to
+     ERROR_MARK_NODE so that it will not trip up the compiler
+     later. The caller is responsible to emitting an error.  */
+  if (ppd->set_packs_to_error)
+    *tp = error_mark_node;
+
   return NULL_TREE;
 }
 
@@ -2524,6 +2534,7 @@ uses_parameter_packs (tree t)
   struct find_parameter_pack_data ppd;
   ppd.parameter_packs = &parameter_packs;
   ppd.visited = pointer_set_create ();
+  ppd.set_packs_to_error = false;
   cp_walk_tree (&t, &find_parameter_packs_r, &ppd, ppd.visited);
   pointer_set_destroy (ppd.visited);
   return parameter_packs != NULL_TREE;
@@ -2542,6 +2553,8 @@ make_pack_expansion (tree arg)
   bool for_types = false;
   struct find_parameter_pack_data ppd;
 
+  ppd.set_packs_to_error = false;
+
   if (!arg || arg == error_mark_node)
     return arg;
 
@@ -2663,20 +2676,21 @@ make_pack_expansion (tree arg)
    Returns TRUE if there were no bare parameter packs, returns FALSE
    (and emits an error) if there were bare parameter packs.*/
 bool 
-check_for_bare_parameter_packs (tree t)
+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)
+  if (!processing_template_decl || !t || !*t || *t == error_mark_node)
     return true;
 
-  if (TREE_CODE (t) == TYPE_DECL)
-    t = TREE_TYPE (t);
+  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);
+  ppd.set_packs_to_error = true;
+  cp_walk_tree (t, &find_parameter_packs_r, &ppd, ppd.visited);
   pointer_set_destroy (ppd.visited);
 
   if (parameter_packs) 
@@ -2959,7 +2973,7 @@ process_template_parm (tree list, tree p
 	  {
 	    /* This template parameter is not a parameter pack, but it
 	       should be. Complain about "bare" parameter packs.  */
-	    check_for_bare_parameter_packs (TREE_TYPE (parm));
+	    check_for_bare_parameter_packs (&TREE_TYPE (parm));
 	    
 	    /* Recover by calling this a parameter pack.  */
 	    is_parameter_pack = true;
@@ -3363,7 +3377,10 @@ process_partial_specialization (tree dec
                   if (TREE_CODE (arg) == EXPR_PACK_EXPANSION)
                     error ("parameter pack argument %qE must be at the end of the template argument list", arg);
                   else
-                    error ("parameter pack argument %qT must be at the end of the template argument list", arg);                   
+                    error ("parameter pack argument %qT must be at the end of the template argument list", arg);
+
+		  if (packed_args)
+		    TREE_VEC_ELT (packed_args, j) = error_mark_node;
                 }
             }
 
@@ -3770,7 +3787,7 @@ push_template_decl_real (tree decl, bool
       while (arg && argtype)
         {
           if (!FUNCTION_PARAMETER_PACK_P (arg)
-              && !check_for_bare_parameter_packs (TREE_TYPE (arg)))
+              && !check_for_bare_parameter_packs (&TREE_TYPE (arg)))
             {
             /* This is a PARM_DECL that contains unexpanded parameter
                packs. We have already complained about this in the
@@ -3786,11 +3803,11 @@ push_template_decl_real (tree decl, bool
 
       /* Check for bare parameter packs in the return type and the
          exception specifiers.  */
-      check_for_bare_parameter_packs (TREE_TYPE (type));
-      check_for_bare_parameter_packs (TYPE_RAISES_EXCEPTIONS (type));
+      check_for_bare_parameter_packs (&TREE_TYPE (type));
+      check_for_bare_parameter_packs (&TYPE_RAISES_EXCEPTIONS (type));
     }
   else
-    check_for_bare_parameter_packs (TREE_TYPE (decl));
+    check_for_bare_parameter_packs (&TREE_TYPE (decl));
 
   if (is_partial)
     return process_partial_specialization (decl);
@@ -3814,6 +3831,8 @@ push_template_decl_real (tree decl, bool
 	      else
 		error ("parameter pack %qT must be at the end of the"
 		       " template parameter list", TREE_TYPE (parm));
+
+	      TREE_VALUE (TREE_VEC_ELT (inner_parms, i)) = error_mark_node;
 	    }
         }
     }
@@ -4719,7 +4738,7 @@ 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));
+		       (DECL_P (arg)? DECL_NAME (arg) : arg));
 	      else if (requires_tmpl_type)
 		error ("  expected a class template, got %qE", arg);
 	      else
@@ -13042,6 +13061,10 @@ unify (tree tparms, tree targs, tree par
          nodes.  */
       return 0;
 
+    case ERROR_MARK:
+      /* Unification fails if we hit an error node.  */
+      return 1;
+
     default:
       gcc_assert (EXPR_P (parm));
 
Index: cp/semantics.c
===================================================================
--- cp/semantics.c	(revision 129658)
+++ cp/semantics.c	(working copy)
@@ -508,7 +508,7 @@ finish_cond (tree *cond_p, tree expr)
       if (TREE_CODE (cond) == DECL_EXPR)
 	expr = cond;
 
-      check_for_bare_parameter_packs (expr);
+      check_for_bare_parameter_packs (&expr);
     }
   *cond_p = expr;
 }
@@ -618,7 +618,7 @@ finish_expr_stmt (tree expr)
       else if (!type_dependent_expression_p (expr))
 	convert_to_void (build_non_dependent_expr (expr), "statement");
 
-      check_for_bare_parameter_packs (expr);
+      check_for_bare_parameter_packs (&expr);
 
       /* Simplification of inner statement expressions, compound exprs,
 	 etc can result in us already having an EXPR_STMT.  */
@@ -875,7 +875,7 @@ finish_for_expr (tree expr, tree for_stm
   else if (!type_dependent_expression_p (expr))
     convert_to_void (build_non_dependent_expr (expr), "3rd expression in for");
   expr = maybe_cleanup_point_expr_void (expr);
-  check_for_bare_parameter_packs (expr);
+  check_for_bare_parameter_packs (&expr);
   FOR_EXPR (for_stmt) = expr;
 }
 
@@ -971,12 +971,12 @@ finish_switch_cond (tree cond, tree swit
 	    cond = index;
 	}
     }
+  check_for_bare_parameter_packs (&cond);
   finish_cond (&SWITCH_STMT_COND (switch_stmt), cond);
   SWITCH_STMT_TYPE (switch_stmt) = orig_type;
   add_stmt (switch_stmt);
   push_switch (switch_stmt);
   SWITCH_STMT_BODY (switch_stmt) = push_stmt_list ();
-  check_for_bare_parameter_packs (cond);
 }
 
 /* Finish the body of a switch-statement, which may be given by
@@ -1389,7 +1389,7 @@ finish_mem_initializers (tree mem_inits)
              bound as part of the TREE_PURPOSE.  See
              make_pack_expansion for more information.  */
           if (TREE_CODE (TREE_PURPOSE (mem)) != TYPE_PACK_EXPANSION)
-            check_for_bare_parameter_packs (TREE_VALUE (mem));
+            check_for_bare_parameter_packs (&TREE_VALUE (mem));
         }
 
       add_stmt (build_min_nt (CTOR_INITIALIZER, mem_inits));
@@ -2306,9 +2306,8 @@ finish_member_declaration (tree decl)
   DECL_CONTEXT (decl) = current_class_type;
 
   /* Check for bare parameter packs in the member variable declaration.  */
-  if (TREE_CODE (decl) == FIELD_DECL
-      && !check_for_bare_parameter_packs (TREE_TYPE (decl)))
-    TREE_TYPE (decl) = error_mark_node;
+  if (TREE_CODE (decl) == FIELD_DECL)
+    check_for_bare_parameter_packs (&TREE_TYPE (decl));
 
   /* [dcl.link]
 
Index: cp/parser.c
===================================================================
--- cp/parser.c	(revision 129658)
+++ cp/parser.c	(working copy)
@@ -15228,7 +15228,7 @@ cp_parser_base_clause (cp_parser* parser
             /* Make this a pack expansion type. */
             TREE_VALUE (base) = make_pack_expansion (TREE_VALUE (base));
           else
-            check_for_bare_parameter_packs (TREE_VALUE (base));
+            check_for_bare_parameter_packs (&TREE_VALUE (base));
 
 	  TREE_CHAIN (base) = bases;
 	  bases = base;

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