diff mbox

[C++] PR 54891

Message ID 53B16F2E.1020401@oracle.com
State New
Headers show

Commit Message

Paolo Carlini June 30, 2014, 2:07 p.m. UTC
Hi,

I think it's fair to say that this issue is rather tricky, considering 
that among the compilers I have at hand none gets it right without 
regressing on parse/pr26997.C. The basic issue is simple: in C++11

     (void)[]{};

is well formed, thus cp_parser_tokens_start_cast_expression should be 
changed to return non-zero when a '[' follows the parenthesized type-id. 
Then, however, if we do nothing else we regress on the line:

     (C ())[2];

of the above testcase, because in that case we have a '[' but in fact we 
don't have a cast-expression - ie, we don't have a lambda-expression - 
and we want to parse the whole expression as unary-expression. Thus I 
figured out that in such ambiguous cases we can avoid committing (we 
used to call cp_parser_parse_definitely which becomes a conditional 
cp_parser_commit_to_topmost_tentative_parse) and instead first try 
cp_parser_cast_expression and then fall back to 
cp_parser_unary_expression (if cp_parser_parse_definitely returns false 
after the former). Tested x86_64-linux.

Thanks,
Paolo.

////////////////////
/cp
2014-06-30  Paolo Carlini  <paolo.carlini@oracle.com>

	PR c++/54891
	* parser.c (cp_parser_tokens_start_cast_expression): In C++11
	a '[' can also start a primary-expression.
	(cp_parser_cast_expression): Parse a cast-expression only tentatively
	when cp_parser_tokens_start_cast_expression returns -1.

/testsuite
2014-06-30  Paolo Carlini  <paolo.carlini@oracle.com>

	PR c++/54891
	* g++.dg/cpp0x/lambda/lambda-cast1.C: New.

Comments

Jason Merrill June 30, 2014, 2:23 p.m. UTC | #1
OK.

Jason
diff mbox

Patch

Index: cp/parser.c
===================================================================
--- cp/parser.c	(revision 212119)
+++ cp/parser.c	(working copy)
@@ -4109,6 +4109,7 @@  complain_flags (bool decltype_p)
      this
      ( expression )
      id-expression
+     lambda-expression (C++11)
 
    GNU Extensions:
 
@@ -7621,10 +7622,10 @@  cp_parser_delete_expression (cp_parser* parser)
 			tf_warning_or_error);
 }
 
-/* Returns true if TOKEN may start a cast-expression and false
-   otherwise.  */
+/* Returns 1 if TOKEN may start a cast-expression and, in C++11,
+   isn't '[', -1 if the TOKEN is '[' in C++11, 0 otherwise.  */
 
-static bool
+static int
 cp_parser_tokens_start_cast_expression (cp_parser *parser)
 {
   cp_token *token = cp_lexer_peek_token (parser->lexer);
@@ -7667,7 +7668,7 @@  cp_parser_tokens_start_cast_expression (cp_parser
     case CPP_OR:
     case CPP_OR_OR:
     case CPP_EOF:
-      return false;
+      return 0;
 
     case CPP_OPEN_PAREN:
       /* In ((type ()) () the last () isn't a valid cast-expression,
@@ -7675,12 +7676,15 @@  cp_parser_tokens_start_cast_expression (cp_parser
       return cp_lexer_peek_nth_token (parser->lexer, 2)->type
 	     != CPP_CLOSE_PAREN;
 
-      /* '[' may start a primary-expression in obj-c++.  */
+      /* '[' may start a primary-expression in obj-c++ and in C++11,
+	 as a lambda expression, eg, '(void)[]{}'.  */
     case CPP_OPEN_SQUARE:
+      if (cxx_dialect >= cxx11)
+	return -1;
       return c_dialect_objc ();
 
     default:
-      return true;
+      return 1;
     }
 }
 
@@ -7705,7 +7709,7 @@  cp_parser_cast_expression (cp_parser *parser, bool
     {
       tree type = NULL_TREE;
       tree expr = NULL_TREE;
-      bool cast_expression_p;
+      int cast_expression = 0;
       const char *saved_message;
 
       /* There's no way to know yet whether or not this is a cast.
@@ -7728,6 +7732,7 @@  cp_parser_cast_expression (cp_parser *parser, bool
 	 will commit to the parse at that point, because we cannot
 	 undo the action that is done when creating a new class.  So,
 	 then we cannot back up and do a postfix-expression.
+
 	 Another tricky case is the following (c++/29234):
 
          struct S { void operator () (); };
@@ -7746,20 +7751,30 @@  cp_parser_cast_expression (cp_parser *parser, bool
 	 we are dealing with an unary-expression, a postfix-expression
 	 or something else.
 
+	 Yet another tricky case, in C++11, is the following (c++/54891):
+
+	 (void)[]{};
+
+         The issue is that usually, besides the case of lambda-expressions,
+	 the parenthesized type-id cannot be followed by '[', and, eg, we
+	 want to parse '(C ())[2];' in parse/pr26997.C as unary-expression.
+	 Thus, if cp_parser_tokens_start_cast_expression returns -1, below
+	 we don't commit, we try a cast-expression, then an unary-expression.
+
 	 Save tokens so that we can put them back.  */
       cp_lexer_save_tokens (parser->lexer);
 
       /* We may be looking at a cast-expression.  */
-      cast_expression_p
-	= (cp_parser_skip_to_closing_parenthesis (parser, false, false,
-						  /*consume_paren=*/true)
-	   && cp_parser_tokens_start_cast_expression (parser));
+      if (cp_parser_skip_to_closing_parenthesis (parser, false, false,
+						 /*consume_paren=*/true))
+	cast_expression
+	  = cp_parser_tokens_start_cast_expression (parser);
 
       /* Roll back the tokens we skipped.  */
       cp_lexer_rollback_tokens (parser->lexer);
       /* If we aren't looking at a cast-expression, simulate an error so
-	 that the call to cp_parser_parse_definitely below will fail.  */
-      if (!cast_expression_p)
+	 that the call to cp_parser_error_occurred below returns true.  */
+      if (!cast_expression)
 	cp_parser_simulate_error (parser);
       else
 	{
@@ -7780,7 +7795,11 @@  cp_parser_cast_expression (cp_parser *parser, bool
 	 function returning T.  */
       if (!cp_parser_error_occurred (parser))
 	{
-	  cp_parser_parse_definitely (parser);
+	  /* Only commit if the cast-expression doesn't start with '[' in
+	     C++11, which may or may not start a lambda-expression.  */
+	  if (cast_expression > 0)
+	    cp_parser_commit_to_topmost_tentative_parse (parser);
+
 	  expr = cp_parser_cast_expression (parser,
 					    /*address_p=*/false,
 					    /*cast_p=*/true,
@@ -7787,23 +7806,26 @@  cp_parser_cast_expression (cp_parser *parser, bool
 					    /*decltype_p=*/false,
 					    pidk);
 
-	  /* Warn about old-style casts, if so requested.  */
-	  if (warn_old_style_cast
-	      && !in_system_header_at (input_location)
-	      && !VOID_TYPE_P (type)
-	      && current_lang_name != lang_name_c)
-	    warning (OPT_Wold_style_cast, "use of old-style cast");
+	  if (cp_parser_parse_definitely (parser))
+	    {
+	      /* Warn about old-style casts, if so requested.  */
+	      if (warn_old_style_cast
+		  && !in_system_header_at (input_location)
+		  && !VOID_TYPE_P (type)
+		  && current_lang_name != lang_name_c)
+		warning (OPT_Wold_style_cast, "use of old-style cast");
 
-	  /* Only type conversions to integral or enumeration types
-	     can be used in constant-expressions.  */
-	  if (!cast_valid_in_integral_constant_expression_p (type)
-	      && cp_parser_non_integral_constant_expression (parser,
-							     NIC_CAST))
-	    return error_mark_node;
+	      /* Only type conversions to integral or enumeration types
+		 can be used in constant-expressions.  */
+	      if (!cast_valid_in_integral_constant_expression_p (type)
+		  && cp_parser_non_integral_constant_expression (parser,
+								 NIC_CAST))
+		return error_mark_node;
 
-	  /* Perform the cast.  */
-	  expr = build_c_cast (input_location, type, expr);
-	  return expr;
+	      /* Perform the cast.  */
+	      expr = build_c_cast (input_location, type, expr);
+	      return expr;
+	    }
 	}
       else 
         cp_parser_abort_tentative_parse (parser);
Index: testsuite/g++.dg/cpp0x/lambda/lambda-cast1.C
===================================================================
--- testsuite/g++.dg/cpp0x/lambda/lambda-cast1.C	(revision 0)
+++ testsuite/g++.dg/cpp0x/lambda/lambda-cast1.C	(working copy)
@@ -0,0 +1,7 @@ 
+// PR c++/54891
+// { dg-do compile { target c++11 } }
+
+int main()
+{
+  (void)[]{};
+}