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.carl...@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.carl...@oracle.com>

        PR c++/54891
        * g++.dg/cpp0x/lambda/lambda-cast1.C: New.
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)[]{};
+}

Reply via email to