re PR c++/63455 (decltype of statement expression internal compiler error: in cp_par...
authorJason Merrill <jason@redhat.com>
Wed, 15 Oct 2014 14:12:24 +0000 (10:12 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Wed, 15 Oct 2014 14:12:24 +0000 (10:12 -0400)
PR c++/63455
c-family/
* c-common.h (CPP_PREPARSED_EXPR): New.
(N_CP_TTYPES): Adjust.
cp/
* parser.c (struct saved_token_sentinel): New.
(cp_parser_statement): Use it.
(cp_parser_start_tentative_firewall): New.
(cp_parser_end_tentative_firewall): New.
(cp_parser_lambda_expression): Use them.
(cp_parser_statement_expr): New.
(cp_parser_primary_expression): Use it.

From-SVN: r216260

gcc/c-family/ChangeLog
gcc/c-family/c-common.h
gcc/cp/ChangeLog
gcc/cp/parser.c
gcc/testsuite/g++.dg/cpp0x/lambda/lambda-sfinae1.C
gcc/testsuite/g++.dg/ext/stmtexpr16.C [new file with mode: 0644]

index 056a0d52d2ebdd49979e00457ac3136b488c7118..7c5152810687f7be9718f9d0f1c72bd51417b26b 100644 (file)
@@ -1,3 +1,9 @@
+2014-10-14  Jason Merrill  <jason@redhat.com>
+
+       PR c++/63455
+       * c-common.h (CPP_PREPARSED_EXPR): New.
+       (N_CP_TTYPES): Adjust.
+
 2014-10-15  Marek Polacek  <polacek@redhat.com>
 
        * c-opts.c (c_common_init_options): Make -std=gnu11 the default for C.
index fec9a0691915746ed37104d94538d930b5cf47c2..b45ccfce35be0b7e45b4f6b4b79ad05e267f8b63 100644 (file)
@@ -361,8 +361,11 @@ struct c_common_resword
 /* A token type for pre-parsed C++0x decltype.  */
 #define CPP_DECLTYPE ((enum cpp_ttype) (CPP_NESTED_NAME_SPECIFIER + 1))
 
+/* A token type for pre-parsed primary-expression (lambda- or statement-).  */
+#define CPP_PREPARSED_EXPR ((enum cpp_ttype) (CPP_DECLTYPE + 1))
+
 /* The number of token types, including C++-specific ones.  */
-#define N_CP_TTYPES ((int) (CPP_DECLTYPE + 1))
+#define N_CP_TTYPES ((int) (CPP_PREPARSED_EXPR + 1))
 
 /* Disable mask.  Keywords are disabled if (reswords[i].disable &
    mask) is _true_.  Thus for keywords which are present in all
index 00f5269fb687e706da7978636c690b50061df012..97b373c270362064468049ece81f0fa642bb565e 100644 (file)
@@ -1,3 +1,14 @@
+2014-10-14  Jason Merrill  <jason@redhat.com>
+
+       PR c++/63455
+       * parser.c (struct saved_token_sentinel): New.
+       (cp_parser_statement): Use it.
+       (cp_parser_start_tentative_firewall): New.
+       (cp_parser_end_tentative_firewall): New.
+       (cp_parser_lambda_expression): Use them.
+       (cp_parser_statement_expr): New.
+       (cp_parser_primary_expression): Use it.
+
 2014-10-14  DJ Delorie  <dj@redhat.com>
 
        * typeck.c (cp_common_type): Check for all __intN types, not just
index b5a3724378fcbe46a1f090379a0e3499473f2a99..c73099547ff9a3cd70a0d2869b7c83f26ef26741 100644 (file)
@@ -1,4 +1,4 @@
-/* C++ Parser.
+/* -*- C++ -*- Parser.
    Copyright (C) 2000-2014 Free Software Foundation, Inc.
    Written by Mark Mitchell <mark@codesourcery.com>.
 
@@ -1155,6 +1155,34 @@ cp_lexer_rollback_tokens (cp_lexer* lexer)
   lexer->next_token = lexer->saved_tokens.pop ();
 }
 
+/* RAII wrapper around the above functions, with sanity checking.  Creating
+   a variable saves tokens, which are committed when the variable is
+   destroyed unless they are explicitly rolled back by calling the rollback
+   member function.  */
+
+struct saved_token_sentinel
+{
+  cp_lexer *lexer;
+  unsigned len;
+  bool commit;
+  saved_token_sentinel(cp_lexer *lexer): lexer(lexer), commit(true)
+  {
+    len = lexer->saved_tokens.length ();
+    cp_lexer_save_tokens (lexer);
+  }
+  void rollback ()
+  {
+    cp_lexer_rollback_tokens (lexer);
+    commit = false;
+  }
+  ~saved_token_sentinel()
+  {
+    if (commit)
+      cp_lexer_commit_tokens (lexer);
+    gcc_assert (lexer->saved_tokens.length () == len);
+  }
+};
+
 /* Print a representation of the TOKEN on the STREAM.  */
 
 static void
@@ -4107,6 +4135,65 @@ complain_flags (bool decltype_p)
   return complain;
 }
 
+/* We're about to parse a collection of statements.  If we're currently
+   parsing tentatively, set up a firewall so that any nested
+   cp_parser_commit_to_tentative_parse won't affect the current context.  */
+
+static cp_token_position
+cp_parser_start_tentative_firewall (cp_parser *parser)
+{
+  if (!cp_parser_uncommitted_to_tentative_parse_p (parser))
+    return 0;
+
+  cp_parser_parse_tentatively (parser);
+  cp_parser_commit_to_topmost_tentative_parse (parser);
+  return cp_lexer_token_position (parser->lexer, false);
+}
+
+/* We've finished parsing the collection of statements.  Wrap up the
+   firewall and replace the relevant tokens with the parsed form.  */
+
+static void
+cp_parser_end_tentative_firewall (cp_parser *parser, cp_token_position start,
+                                 tree expr)
+{
+  if (!start)
+    return;
+
+  /* Finish the firewall level.  */
+  cp_parser_parse_definitely (parser);
+  /* And remember the result of the parse for when we try again.  */
+  cp_token *token = cp_lexer_token_at (parser->lexer, start);
+  token->type = CPP_PREPARSED_EXPR;
+  token->u.value = expr;
+  token->keyword = RID_MAX;
+  cp_lexer_purge_tokens_after (parser->lexer, start);
+}
+
+/* Parse a GNU statement-expression, i.e. ({ stmts }), except for the
+   enclosing parentheses.  */
+
+static tree
+cp_parser_statement_expr (cp_parser *parser)
+{
+  cp_token_position start = cp_parser_start_tentative_firewall (parser);
+
+  /* Consume the '('.  */
+  cp_lexer_consume_token (parser->lexer);
+  /* Start the statement-expression.  */
+  tree expr = begin_stmt_expr ();
+  /* Parse the compound-statement.  */
+  cp_parser_compound_statement (parser, expr, false, false);
+  /* Finish up.  */
+  expr = finish_stmt_expr (expr, false);
+  /* Consume the ')'.  */
+  if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+    cp_parser_skip_to_end_of_statement (parser);
+
+  cp_parser_end_tentative_firewall (parser, start, expr);
+  return expr;
+}
+
 /* Expressions [gram.expr] */
 
 /* Parse a primary-expression.
@@ -4193,6 +4280,7 @@ cp_parser_primary_expression (cp_parser *parser,
     case CPP_CHAR32:
     case CPP_WCHAR:
     case CPP_NUMBER:
+    case CPP_PREPARSED_EXPR:
       if (TREE_CODE (token->u.value) == USERDEF_LITERAL)
        return cp_parser_userdef_numeric_literal (parser);
       token = cp_lexer_consume_token (parser->lexer);
@@ -4272,6 +4360,36 @@ cp_parser_primary_expression (cp_parser *parser,
                                       true);
 
     case CPP_OPEN_PAREN:
+      /* If we see `( { ' then we are looking at the beginning of
+        a GNU statement-expression.  */
+      if (cp_parser_allow_gnu_extensions_p (parser)
+         && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_BRACE))
+       {
+         /* Statement-expressions are not allowed by the standard.  */
+         pedwarn (token->location, OPT_Wpedantic,
+                  "ISO C++ forbids braced-groups within expressions");
+
+         /* And they're not allowed outside of a function-body; you
+            cannot, for example, write:
+
+            int i = ({ int j = 3; j + 1; });
+
+            at class or namespace scope.  */
+         if (!parser->in_function_body
+             || parser->in_template_argument_list_p)
+           {
+             error_at (token->location,
+                       "statement-expressions are not allowed outside "
+                       "functions nor in template-argument lists");
+             cp_parser_skip_to_end_of_block_or_statement (parser);
+             if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+               cp_lexer_consume_token (parser->lexer);
+             return error_mark_node;
+           }
+         else
+           return cp_parser_statement_expr (parser);
+       }
+      /* Otherwise it's a normal parenthesized expression.  */
       {
        tree expr;
        bool saved_greater_than_is_operator_p;
@@ -4283,57 +4401,22 @@ cp_parser_primary_expression (cp_parser *parser,
        saved_greater_than_is_operator_p
          = parser->greater_than_is_operator_p;
        parser->greater_than_is_operator_p = true;
-       /* If we see `( { ' then we are looking at the beginning of
-          a GNU statement-expression.  */
-       if (cp_parser_allow_gnu_extensions_p (parser)
-           && cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
-         {
-           /* Statement-expressions are not allowed by the standard.  */
-           pedwarn (token->location, OPT_Wpedantic, 
-                    "ISO C++ forbids braced-groups within expressions");
-
-           /* And they're not allowed outside of a function-body; you
-              cannot, for example, write:
 
-                int i = ({ int j = 3; j + 1; });
+       /* Parse the parenthesized expression.  */
+       expr = cp_parser_expression (parser, idk, cast_p, decltype_p);
+       /* Let the front end know that this expression was
+          enclosed in parentheses. This matters in case, for
+          example, the expression is of the form `A::B', since
+          `&A::B' might be a pointer-to-member, but `&(A::B)' is
+          not.  */
+       expr = finish_parenthesized_expr (expr);
+       /* DR 705: Wrapping an unqualified name in parentheses
+          suppresses arg-dependent lookup.  We want to pass back
+          CP_ID_KIND_QUALIFIED for suppressing vtable lookup
+          (c++/37862), but none of the others.  */
+       if (*idk != CP_ID_KIND_QUALIFIED)
+         *idk = CP_ID_KIND_NONE;
 
-              at class or namespace scope.  */
-           if (!parser->in_function_body
-               || parser->in_template_argument_list_p)
-             {
-               error_at (token->location,
-                         "statement-expressions are not allowed outside "
-                         "functions nor in template-argument lists");
-               cp_parser_skip_to_end_of_block_or_statement (parser);
-               expr = error_mark_node;
-             }
-           else
-             {
-               /* Start the statement-expression.  */
-               expr = begin_stmt_expr ();
-               /* Parse the compound-statement.  */
-               cp_parser_compound_statement (parser, expr, false, false);
-               /* Finish up.  */
-               expr = finish_stmt_expr (expr, false);
-             }
-         }
-       else
-         {
-           /* Parse the parenthesized expression.  */
-           expr = cp_parser_expression (parser, idk, cast_p, decltype_p);
-           /* Let the front end know that this expression was
-              enclosed in parentheses. This matters in case, for
-              example, the expression is of the form `A::B', since
-              `&A::B' might be a pointer-to-member, but `&(A::B)' is
-              not.  */
-           expr = finish_parenthesized_expr (expr);
-           /* DR 705: Wrapping an unqualified name in parentheses
-              suppresses arg-dependent lookup.  We want to pass back
-              CP_ID_KIND_QUALIFIED for suppressing vtable lookup
-              (c++/37862), but none of the others.  */
-           if (*idk != CP_ID_KIND_QUALIFIED)
-             *idk = CP_ID_KIND_NONE;
-         }
        /* The `>' token might be the end of a template-id or
           template-parameter-list now.  */
        parser->greater_than_is_operator_p
@@ -8869,6 +8952,7 @@ cp_parser_lambda_expression (cp_parser* parser)
   tree type;
   bool ok = true;
   cp_token *token = cp_lexer_peek_token (parser->lexer);
+  cp_token_position start = 0;
 
   LAMBDA_EXPR_LOCATION (lambda_expr) = token->location;
 
@@ -8882,6 +8966,15 @@ cp_parser_lambda_expression (cp_parser* parser)
        }
       ok = false;
     }
+  else if (parser->in_template_argument_list_p)
+    {
+      if (!token->error_reported)
+       {
+         error_at (token->location, "lambda-expression in template-argument");
+         token->error_reported = true;
+       }
+      ok = false;
+    }
 
   /* We may be in the middle of deferred access check.  Disable
      it now.  */
@@ -8929,7 +9022,13 @@ cp_parser_lambda_expression (cp_parser* parser)
     ok &= cp_parser_lambda_declarator_opt (parser, lambda_expr);
 
     if (ok)
-      cp_parser_lambda_body (parser, lambda_expr);
+      {
+       if (!cp_parser_error_occurred (parser)
+           && cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)
+           && cp_parser_start_tentative_firewall (parser))
+         start = token;
+       cp_parser_lambda_body (parser, lambda_expr);
+      }
     else if (cp_parser_require (parser, CPP_OPEN_BRACE, RT_OPEN_BRACE))
       {
        if (cp_parser_skip_to_closing_brace (parser))
@@ -8967,9 +9066,13 @@ cp_parser_lambda_expression (cp_parser* parser)
   insert_pending_capture_proxies ();
 
   if (ok)
-    return build_lambda_object (lambda_expr);
+    lambda_expr = build_lambda_object (lambda_expr);
   else
-    return error_mark_node;
+    lambda_expr = error_mark_node;
+
+  cp_parser_end_tentative_firewall (parser, start, lambda_expr);
+
+  return lambda_expr;
 }
 
 /* Parse the beginning of a lambda expression.
@@ -9503,7 +9606,7 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
   /* There is no statement yet.  */
   statement = NULL_TREE;
 
-  cp_lexer_save_tokens (parser->lexer);
+  saved_token_sentinel saved_tokens (parser->lexer);
   attrs_location = cp_lexer_peek_token (parser->lexer)->location;
   if (c_dialect_objc ())
     /* In obj-c++, seeing '[[' might be the either the beginning of
@@ -9668,7 +9771,7 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
            {
              /*  Attributes should be parsed as part of the the
                  declaration, so let's un-parse them.  */
-             cp_lexer_rollback_tokens (parser->lexer);
+             saved_tokens.rollback();
              std_attrs = NULL_TREE;
            }
 
index 973f8a7804819db2a3f4fc756e6d5b31a53cdc07..40abcb99a574c67882528f6ab3583ad4faf64b73 100644 (file)
@@ -8,9 +8,9 @@ struct AddRvalueReferenceImpl { typedef T type; };
 
 template <typename T>
 struct AddRvalueReferenceImpl<T, typename BoolSink<false &&
-      [] {
+      [] {                     // { dg-error "lambda" }
          extern T &&tref;
-      }>::type> {              // { dg-error "lambda" }
+      }>::type> {
    typedef T &&type;
 };
 
@@ -27,9 +27,9 @@ struct IsConstructibleImpl { enum { value = 0 }; };
 
 template <typename T, typename ...Args>
 struct IsConstructibleImpl<T, typename BoolSink<false &&
-      [] {
+      [] {                     // { dg-error "lambda" }
          T t( ::ImplHelpers::create<Args>() ...);
-      }>::type, Args ...> {    // { dg-error "lambda" }
+      }>::type, Args ...> {
    enum { value = 1 };
 };
 
@@ -53,3 +53,4 @@ static_assert(+IsConstructible<int &&, int &&>::value, "error");
 // { dg-prune-output "expected" }
 // { dg-prune-output "does not name a class" }
 // { dg-prune-output "static assertion" }
+// { dg-prune-output "template argument . is invalid" }
diff --git a/gcc/testsuite/g++.dg/ext/stmtexpr16.C b/gcc/testsuite/g++.dg/ext/stmtexpr16.C
new file mode 100644 (file)
index 0000000..ddce40c
--- /dev/null
@@ -0,0 +1,10 @@
+// PR c++/63455
+// { dg-options "-std=gnu++11" }
+
+int main()
+{
+    int x = 0;
+
+    // without '+0', gcc 4.6 gives a different error (no ICE though)
+    decltype(({ int y = x; y; })+0) v1 = 0;
+}