[patch] Fix PR c++/28858: Algorithm to find the end of a template parameter list is flawed

Volker Reichelt reichelt@igpm.rwth-aachen.de
Sat Aug 26 21:56:00 GMT 2006


If an error occurs in a template parameter list, the frontend
sometimes calls cp_parser_skip_until_found to find the ">" that
marks the end of the list. The algorithm used there has some flaws,
though:

1) It doesn't keep track of the nesting levels of "< ... >".
   This results in bogus error messages when templates are used as default
   parameters or when template template parameters are involved, e.g.:

     template<TEMPLATE<int> class> struct A {};

   yields:

     bug.cc:1: error: 'TEMPLATE' has not been declared
     bug.cc:1: error: expected `>' before '<' token
     bug.cc:1: error: expected identifier before '>' token
     bug.cc:1: error: expected unqualified-id before '>' token

2) It doesn't stop at ";", or "{", or "}".
   (These tokens probably indicate the end of a declaration or the start of
   a function/struct body which is not part of a template parameter list.)
   Because of this large parts of the source might be skipped,
   if one forgets the closing ">", e.g.:

     template<int N struct A;
     bool i = 1 > 0;
     int j = i;

   yields:

     bug.cc:1: error: expected `>' before 'struct'
     bug.cc:2: error: expected unqualified-id before numeric constant
     bug.cc:3: error: 'i' was not declared in this scope

The patch below fixes this as follows:
First of all, since cp_parser_skip_until_found is only used to find the
end of a template parameter list, the patch renames the function to
cp_parser_skip_to_end_of_template_parameter_list, drops the last two
arguments which were always the same and adjusts the two calls of the
function.

The patch then refines the algorithm in that function to also track
levels of "< ... >". and to stop at ";", or "{", or "}".

Some problems are regressions that were introduced with the new
C++ parser. Nevertheless, since this is only a diagnostic problem 
w.r.t. error recovery, I think it's sufficient to apply this to
mainline.

Bootstrapped and regtested on x86_64-unknown-linux-gnu.
Ok for mainline?

Regards,
Volker

:ADDPATCH C++:


2006-08-26  Volker Reichelt  <reichelt@igpm.rwth-aachen.de>

	PR c++/28858
	* parser.c (cp_parser_skip_until_found): Rename to
	cp_parser_skip_to_end_of_template_parameter_list.  Remove last two
	parameters.  Track levels of '< ... >'.  Stop at '{', '}', or ';'.
	Reorganize.  Adjust comment.
	(cp_parser_template_declaration_after_export): Adjust call.
	(cp_parser_enclosed_template_argument_list): Likewise.

===================================================================
--- gcc/gcc/cp/parser.c	2006-08-03 04:49:07 +0200
+++ gcc/gcc/cp/parser.c	2006-08-20 20:13:00 +0200
@@ -1816,8 +1816,8 @@ static void cp_parser_skip_to_end_of_blo
   (cp_parser *);
 static void cp_parser_skip_to_closing_brace
   (cp_parser *);
-static void cp_parser_skip_until_found
-  (cp_parser *, enum cpp_ttype, const char *);
+static void cp_parser_skip_to_end_of_template_parameter_list
+  (cp_parser *);
 static void cp_parser_skip_to_pragma_eol
   (cp_parser*, cp_token *);
 static bool cp_parser_error_occurred
@@ -15527,7 +15527,7 @@ cp_parser_template_declaration_after_exp
   checks = get_deferred_access_checks ();
 
   /* Look for the `>'.  */
-  cp_parser_skip_until_found (parser, CPP_GREATER, "`>'");
+  cp_parser_skip_to_end_of_template_parameter_list (parser);
   /* We just processed one more parameter list.  */
   ++parser->num_template_parameter_lists;
   /* If the next token is `template', there are more template
@@ -15872,7 +15872,7 @@ cp_parser_enclosed_template_argument_lis
 	}
     }
   else
-    cp_parser_skip_until_found (parser, CPP_GREATER, "`>'");
+    cp_parser_skip_to_end_of_template_parameter_list (parser);
   /* The `>' token might be a greater-than operator again now.  */
   parser->greater_than_is_operator_p
     = saved_greater_than_is_operator_p;
@@ -16300,54 +16300,61 @@ cp_parser_require (cp_parser* parser,
     }
 }
 
-/* Like cp_parser_require, except that tokens will be skipped until
-   the desired token is found.  An error message is still produced if
-   the next token is not as expected.  */
+/* An error message is produced if the next token is not '>'.
+   All further tokens are skipped until the desired token is
+   found or '{', '}', ';' or an unbalanced ')' or ']'.  */
 
 static void
-cp_parser_skip_until_found (cp_parser* parser,
-			    enum cpp_ttype type,
-			    const char* token_desc)
+cp_parser_skip_to_end_of_template_parameter_list (cp_parser* parser)
 {
-  cp_token *token;
+  /* Current level of '< ... >'.  */
+  unsigned level = 0;
+  /* Ignore '<' and '>' nested inside '( ... )' or '[ ... ]'.  */
   unsigned nesting_depth = 0;
 
-  if (cp_parser_require (parser, type, token_desc))
+  /* Are we ready, yet?  If not, issue error message.  */
+  if (cp_parser_require (parser, CPP_GREATER, "%<>%>"))
     return;
 
   /* Skip tokens until the desired token is found.  */
   while (true)
     {
       /* Peek at the next token.  */
-      token = cp_lexer_peek_token (parser->lexer);
-
-      /* If we've reached the token we want, consume it and stop.  */
-      if (token->type == type && !nesting_depth)
+      switch (cp_lexer_peek_token (parser->lexer)->type)
 	{
-	  cp_lexer_consume_token (parser->lexer);
-	  return;
-	}
+	case CPP_LESS:
+	  if (!nesting_depth)
+	    ++level;
+	  break;
 
-      switch (token->type)
-	{
-	case CPP_EOF:
-	case CPP_PRAGMA_EOL:
-	  /* If we've run out of tokens, stop.  */
-	  return;
+	case CPP_GREATER:
+	  if (!nesting_depth && level-- == 0)
+	    {
+	      /* We've reached the token we want, consume it and stop.  */
+	      cp_lexer_consume_token (parser->lexer);
+	      return;
+	    }
+	  break;
 
-	case CPP_OPEN_BRACE:
 	case CPP_OPEN_PAREN:
 	case CPP_OPEN_SQUARE:
 	  ++nesting_depth;
 	  break;
 
-	case CPP_CLOSE_BRACE:
 	case CPP_CLOSE_PAREN:
 	case CPP_CLOSE_SQUARE:
 	  if (nesting_depth-- == 0)
 	    return;
 	  break;
 
+	case CPP_EOF:
+	case CPP_PRAGMA_EOL:
+	case CPP_SEMICOLON:
+	case CPP_OPEN_BRACE:
+	case CPP_CLOSE_BRACE:
+	  /* The '>' was probably forgotten, don't look further.  */
+	  return;
+
 	default:
 	  break;
 	}
===================================================================

2006-08-26  Volker Reichelt  <reichelt@igpm.rwth-aachen.de>

	PR c++/28858
	* g++.dg/parse/template20.C: New test.
	* g++.dg/parse/def-tmpl-arg1.C: Adjust error-markers.
	* g++.old-deja/g++.pt/crash65.C: Likewise.

===================================================================
--- gcc/gcc/testsuite/g++.dg/parse/template20.C	2003-09-23 19:59:22 +0200
+++ gcc/gcc/testsuite/g++.dg/parse/template20.C	2006-08-19 17:05:39 +0200
@@ -0,0 +1,7 @@
+// PR c++/28858
+// { dg-do compile }
+
+template<int N struct A;  // { dg-error "before" }
+
+bool i = 1 > 0;           // { dg-bogus "" }
+int j = i;                // { dg-bogus "" }
===================================================================
--- gcc/gcc/testsuite/g++.dg/parse/def-tmpl-arg1.C	2003-07-22 12:54:14 +0200
+++ gcc/gcc/testsuite/g++.dg/parse/def-tmpl-arg1.C	2006-08-20 20:22:22 +0200
@@ -6,4 +6,4 @@ template <typename X, typename Y = B<X> 
 {
     A();
     A(const A&);
-}; // { dg-error "" }
+};
===================================================================
--- gcc/gcc/testsuite/g++.old-deja/g++.pt/crash65.C	2005-12-14 19:19:39 +0100
+++ gcc/gcc/testsuite/g++.old-deja/g++.pt/crash65.C	2006-08-19 19:34:08 +0200
@@ -8,5 +8,5 @@
 
 
 template<class T =
-struct W {};    // { dg-error "" } inside template parms
-> struct S{};
+struct W {};    // { dg-error "inside template parameter list|before" }
+> struct S{};   // { dg-error "before" }
===================================================================




More information about the Gcc-patches mailing list