]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
gccrs: Fix parsing of block expressions followed by `.`
authorMatthew Jasper <mjjasper1@gmail.com>
Wed, 10 May 2023 01:08:37 +0000 (02:08 +0100)
committerArthur Cohen <arthur.cohen@embecosm.com>
Tue, 16 Jan 2024 17:37:19 +0000 (18:37 +0100)
`{ ... }.f;` is parsed as a single statement in rust. This means that we can't
determine whether an expression statement will need a semicolon terminator
until we finish parsing it. To handle this we change expression parsing to
check for this case by inspecting the expression returned from null_denotation
and looking ahead for a `.` or `?` token.

gcc/rust/ChangeLog:

* ast/rust-ast.h (Expr::as_expr_without_block): Remove.
(Expr::set_outer_attrs): Make public in base class.
* expand/rust-macro-expand.cc:
Add fixme comment for pre-existing bug.
* hir/tree/rust-hir.h: Remove Expr::as_expr_without_block.
* parse/rust-parse-impl.h (Parser::parse_lifetime): Use lifetime_from_token.
(Parser::lifetime_from_token): New method.
(Parser::null_denotation): Handle labelled loop expressions and for loop expressions.
(Parser::parse_loop_label): Make initial token a parameter.
(Parser::parse_labelled_loop_expr): Likewise.
(Parser::parse_for_loop_expr): Allow FOR token to already be skipped.
(Parser::parse_expr): Handle expr_can_be_stmt.
(Parser::parse_expr_with_block): Remove.
(Parser::parse_expr_stmt_with_block): Remove.
(Parser::parse_expr_stmt_without_block): Remove.
(Parser::parse_expr_without_block): Remove.
(Parser::parse_stmt_or_expr_with_block): Remove.
(Parser::parse_expr_stmt): Use parse_expr directly.
(Parser::parse_match_expr): Likewise.
(Parser::parse_stmt): Use parse_expr_stmt in more cases.
(Parser::parse_stmt_or_expr):
Rename from parse_stmt_or_expr_without_block, use parse_expr directly.
(Parser::parse_block_expr): Update error message.
* parse/rust-parse.h: Update declarations.

gcc/testsuite/ChangeLog:

* rust/compile/for_expr.rs: New test.
* rust/compile/issue-407-2.rs: Update compiler output.
* rust/compile/issue-407.rs: Update compiler output.
* rust/compile/issue-867.rs: Update compiler output.
* rust/compile/issue-2189.rs: New test.
* rust/compile/macro_call_statement.rs: New test.
* rust/compile/stmt_with_block_dot.rs: New test.
* rust/compile/torture/loop8.rs: New test.

Signed-off-by: Matthew Jasper <mjjasper1@gmail.com>
13 files changed:
gcc/rust/ast/rust-ast.h
gcc/rust/expand/rust-macro-expand.cc
gcc/rust/hir/tree/rust-hir.h
gcc/rust/parse/rust-parse-impl.h
gcc/rust/parse/rust-parse.h
gcc/testsuite/rust/compile/for_expr.rs [new file with mode: 0644]
gcc/testsuite/rust/compile/issue-2189.rs [new file with mode: 0644]
gcc/testsuite/rust/compile/issue-407-2.rs
gcc/testsuite/rust/compile/issue-407.rs
gcc/testsuite/rust/compile/issue-867.rs
gcc/testsuite/rust/compile/macro_call_statement.rs [new file with mode: 0644]
gcc/testsuite/rust/compile/stmt_with_block_dot.rs [new file with mode: 0644]
gcc/testsuite/rust/compile/torture/loop8.rs [new file with mode: 0644]

index 1591469f6d8807b1060c1831a7f5147e07b685be..a551b461a6a03a49c35f304da5d7a51732b939c7 100644 (file)
@@ -960,10 +960,6 @@ public:
    * for some?
    *  - evaluate() - evaluates expression if constant? can_evaluate()? */
 
-  /* HACK: downcasting without dynamic_cast (if possible) via polymorphism -
-   * overrided in subclasses of ExprWithoutBlock */
-  virtual ExprWithoutBlock *as_expr_without_block () const { return nullptr; }
-
   virtual std::string as_string () const = 0;
 
   virtual ~Expr () {}
@@ -986,6 +982,10 @@ public:
 
   virtual Expr *to_stmt () const { return clone_expr_impl (); }
 
+  // TODO: think of less hacky way to implement this kind of thing
+  // Sets outer attributes.
+  virtual void set_outer_attrs (std::vector<Attribute>) = 0;
+
 protected:
   // Constructor
   Expr () : node_id (Analysis::Mappings::get ()->get_next_node_id ()) {}
@@ -993,10 +993,6 @@ protected:
   // Clone function implementation as pure virtual method
   virtual Expr *clone_expr_impl () const = 0;
 
-  // TODO: think of less hacky way to implement this kind of thing
-  // Sets outer attributes.
-  virtual void set_outer_attrs (std::vector<Attribute>) = 0;
-
   NodeId node_id;
 };
 
@@ -1023,13 +1019,6 @@ public:
   {
     return std::unique_ptr<ExprWithoutBlock> (clone_expr_without_block_impl ());
   }
-
-  /* downcasting hack from expr to use pratt parsing with
-   * parse_expr_without_block */
-  ExprWithoutBlock *as_expr_without_block () const final override
-  {
-    return clone_expr_without_block_impl ();
-  }
 };
 
 /* HACK: IdentifierExpr, delete when figure out identifier vs expr problem in
index 5f6c50e29f4713beb79466f8a350fcc1c65fa552..f347335f2efedcfe1bc69ba1a164fa125ee9743a 100644 (file)
@@ -1034,6 +1034,8 @@ MacroExpander::transcribe_rule (
   bool reached_end_of_stream = did_delimit && parser.skip_token (END_OF_FILE);
   if (!reached_end_of_stream)
     {
+      // FIXME: rustc has some cases it accepts this with a warning due to
+      // backwards compatibility.
       const_TokenPtr current_token = parser.peek_current_token ();
       rust_error_at (current_token->get_locus (),
                     "tokens here and after are unparsed");
index 31f14941e463f54ff611e4bf1720cedbf88dae78..0258f5f24fbb151b561943e22215cf8f33987701 100644 (file)
@@ -288,10 +288,6 @@ public:
     return std::unique_ptr<Expr> (clone_expr_impl ());
   }
 
-  /* HACK: downcasting without dynamic_cast (if possible) via polymorphism -
-   * overrided in subclasses of ExprWithoutBlock */
-  virtual ExprWithoutBlock *as_expr_without_block () const { return nullptr; }
-
   // TODO: make pure virtual if move out outer attributes to derived classes
   virtual std::string as_string () const;
 
@@ -354,13 +350,6 @@ public:
     return std::unique_ptr<ExprWithoutBlock> (clone_expr_without_block_impl ());
   }
 
-  /* downcasting hack from expr to use pratt parsing with
-   * parse_expr_without_block */
-  ExprWithoutBlock *as_expr_without_block () const override
-  {
-    return clone_expr_without_block_impl ();
-  }
-
   BlockType get_block_expr_type () const final override
   {
     return BlockType::WITHOUT_BLOCK;
index f4df6fa8a1f5c61603f4eacf2358993bbcb5084a..f5f3c7bb527affa6f03fd196d3405743f2da1609 100644 (file)
@@ -4019,7 +4019,6 @@ AST::Lifetime
 Parser<ManagedTokenSource>::parse_lifetime ()
 {
   const_TokenPtr lifetime_tok = lexer.peek_token ();
-  Location locus = lifetime_tok->get_locus ();
   // create error lifetime if doesn't exist
   if (lifetime_tok->get_id () != LIFETIME)
     {
@@ -4027,7 +4026,15 @@ Parser<ManagedTokenSource>::parse_lifetime ()
     }
   lexer.skip_token ();
 
-  std::string lifetime_ident = lifetime_tok->get_str ();
+  return lifetime_from_token (lifetime_tok);
+}
+
+template <typename ManagedTokenSource>
+AST::Lifetime
+Parser<ManagedTokenSource>::lifetime_from_token (const_TokenPtr tok)
+{
+  Location locus = tok->get_locus ();
+  std::string lifetime_ident = tok->get_str ();
 
   if (lifetime_ident == "'static")
     {
@@ -6218,13 +6225,6 @@ Parser<ManagedTokenSource>::parse_stmt (ParseRestrictions restrictions)
          return parse_vis_item (std::move (outer_attrs));
        }
       break;
-    case SUPER:
-    case SELF:
-    case CRATE:
-    case DOLLAR_SIGN:
-      // almost certainly macro invocation semi
-      return parse_macro_invocation_semi (std::move (outer_attrs));
-      break;
     // crappy hack to do union "keyword"
     case IDENTIFIER:
       if (t->get_str () == "union"
@@ -7228,315 +7228,42 @@ Parser<ManagedTokenSource>::parse_method ()
                      AST::Visibility::create_error (), AST::AttrVec (), locus);
 }
 
-/* Parses an expression statement (disambiguates to expression with or without
- * block statement). */
+/* Parses an expression statement. */
 template <typename ManagedTokenSource>
 std::unique_ptr<AST::ExprStmt>
 Parser<ManagedTokenSource>::parse_expr_stmt (AST::AttrVec outer_attrs,
                                             ParseRestrictions restrictions)
 {
-  /* potential thoughts - define new virtual method "has_block()" on expr.
-   * parse expr and then determine whether semicolon is needed as a result of
-   * this method. but then this would require dynamic_cast, which is not
-   * allowed. */
-
-  /* okay new thought - big switch to disambiguate exprs with blocks - either
-   * block expr, async block expr, unsafe block expr, loop expr, if expr, if
-   * let expr, or match expr. So all others are exprs without block. */
-  /* new thought: possible initial tokens: 'loop', 'while', 'for', lifetime
-   * (and then ':' and then loop), 'if', 'match', '{', 'async', 'unsafe' (and
-   * then
-   * '{')). This seems to have no ambiguity. */
-
-  const_TokenPtr t = lexer.peek_token ();
-  /* TODO: should the switch just directly call the individual parse methods
-   * rather than adding another layer of indirection with
-   * parse_expr_stmt_with_block()? */
-  switch (t->get_id ())
-    {
-    case LOOP:
-    case WHILE:
-    case FOR:
-    case IF:
-    case MATCH_TOK:
-    case LEFT_CURLY:
-    case ASYNC:
-      // expression with block
-      return parse_expr_stmt_with_block (std::move (outer_attrs));
-      case LIFETIME: {
-       /* FIXME: are there any expressions without blocks that can have
-        * lifetime as their first token? Or is loop expr the only one? */
-       // safe side for now:
-       if (lexer.peek_token (1)->get_id () == COLON
-           && lexer.peek_token (2)->get_id () == LOOP)
-         {
-           return parse_expr_stmt_with_block (std::move (outer_attrs));
-         }
-       else
-         {
-           return parse_expr_stmt_without_block (std::move (outer_attrs),
-                                                 restrictions);
-         }
-      }
-      case UNSAFE: {
-       // unsafe block
-       // https://doc.rust-lang.org/reference/unsafe-keyword.html
-       return parse_expr_stmt_with_block (std::move (outer_attrs));
-      }
-    default:
-      // not a parse expr with block, so must be expr without block
-      /* TODO: if possible, be more selective about possible expr without
-       * block initial tokens in order to prevent more syntactical errors at
-       * parse time. */
-      return parse_expr_stmt_without_block (std::move (outer_attrs),
-                                           restrictions);
-    }
-}
-
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ExprWithBlock>
-Parser<ManagedTokenSource>::parse_expr_with_block (AST::AttrVec outer_attrs)
-{
-  std::unique_ptr<AST::ExprWithBlock> expr_parsed = nullptr;
-
-  const_TokenPtr t = lexer.peek_token ();
-  switch (t->get_id ())
-    {
-    case IF:
-      // if or if let, so more lookahead to find out
-      if (lexer.peek_token (1)->get_id () == LET)
-       {
-         // if let expr
-         expr_parsed = parse_if_let_expr (std::move (outer_attrs));
-         break;
-       }
-      else
-       {
-         // if expr
-         expr_parsed = parse_if_expr (std::move (outer_attrs));
-         break;
-       }
-    case LOOP:
-      // infinite loop
-      expr_parsed = parse_loop_expr (std::move (outer_attrs));
-      break;
-    case FOR:
-      // "for" iterator loop
-      expr_parsed = parse_for_loop_expr (std::move (outer_attrs));
-      break;
-      case WHILE: {
-       // while or while let, so more lookahead to find out
-       if (lexer.peek_token (1)->get_id () == LET)
-         {
-           // while let loop expr
-           expr_parsed = parse_while_let_loop_expr (std::move (outer_attrs));
-           break;
-         }
-       else
-         {
-           // while loop expr
-           expr_parsed = parse_while_loop_expr (std::move (outer_attrs));
-           break;
-         }
-      }
-    case MATCH_TOK:
-      // match expression
-      expr_parsed = parse_match_expr (std::move (outer_attrs));
-      break;
-    case LEFT_CURLY:
-      // block expression
-      expr_parsed = parse_block_expr (std::move (outer_attrs));
-      break;
-    case ASYNC:
-      // async block expression
-      expr_parsed = parse_async_block_expr (std::move (outer_attrs));
-      break;
-    case UNSAFE:
-      // unsafe block expression
-      expr_parsed = parse_unsafe_block_expr (std::move (outer_attrs));
-      break;
-    case LIFETIME:
-      // some kind of loop expr (with loop label)
-      expr_parsed = parse_labelled_loop_expr (std::move (outer_attrs));
-      break;
-    default:
-      add_error (Error (
-       t->get_locus (),
-       "could not recognise expr beginning with %qs as an expr with block in"
-       " parsing expr statement",
-       t->get_token_description ()));
-
-      skip_after_next_block ();
-      return nullptr;
-    }
-
-  // ensure expr parsed exists
-  if (expr_parsed == nullptr)
-    {
-      Error error (t->get_locus (),
-                  "failed to parse expr with block in parsing expr statement");
-      add_error (std::move (error));
-
-      skip_after_end_block ();
-      return nullptr;
-    }
-
-  return expr_parsed;
-}
-
-/* Parses a expression statement containing an expression with block.
- * Disambiguates internally. */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ExprStmt>
-Parser<ManagedTokenSource>::parse_expr_stmt_with_block (
-  AST::AttrVec outer_attrs)
-{
-  auto expr_parsed = parse_expr_with_block (std::move (outer_attrs));
-  auto locus = expr_parsed->get_locus ();
-
-  // return expr stmt created from expr
-  return std::unique_ptr<AST::ExprStmt> (
-    new AST::ExprStmt (std::move (expr_parsed), locus,
-                      lexer.peek_token ()->get_id () == SEMICOLON));
-}
-
-/* Parses an expression statement containing an expression without block.
- * Disambiguates further. */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ExprStmt>
-Parser<ManagedTokenSource>::parse_expr_stmt_without_block (
-  AST::AttrVec outer_attrs, ParseRestrictions restrictions)
-{
-  /* TODO: maybe move more logic for expr without block in here for better
-   * error handling */
-
-  // attempt to parse via parse_expr_without_block - seems to work
-  std::unique_ptr<AST::ExprWithoutBlock> expr = nullptr;
   Location locus = lexer.peek_token ()->get_locus ();
 
   restrictions.expr_can_be_stmt = true;
 
-  expr = parse_expr_without_block (std::move (outer_attrs), restrictions);
+  // attempt to parse via parse_expr_without_block - seems to work
+  std::unique_ptr<AST::Expr> expr
+    = parse_expr (std::move (outer_attrs), restrictions);
   if (expr == nullptr)
     {
       // expr is required, error
       Error error (lexer.peek_token ()->get_locus (),
-                  "failed to parse expr without block in expr statement");
+                  "failed to parse expr in expr statement");
       add_error (std::move (error));
 
       skip_after_semicolon ();
       return nullptr;
     }
 
-  if (restrictions.consume_semi)
-    if (!skip_token (SEMICOLON))
-      return nullptr;
-
-  return std::unique_ptr<AST::ExprStmt> (
-    new AST::ExprStmt (std::move (expr), locus, true));
-}
-
-/* Parses an expression without a block associated with it (further
- * disambiguates). */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ExprWithoutBlock>
-Parser<ManagedTokenSource>::parse_expr_without_block (
-  AST::AttrVec outer_attrs, ParseRestrictions restrictions)
-{
-  /* Notes on types of expr without block:
-   *  - literal expr          tokens that are literals
-   *  - path expr             path_in_expr or qual_path_in_expr
-   *  - operator expr         many different types
-   *     unary:
-   *      borrow expr         ( '&' | '&&' ) 'mut'? expr
-   *      dereference expr    '*' expr
-   *      error propagation   expr '?'
-   *      negation            '-' expr
-   *      not                 '!' expr
-   *     binary: all start with expr
-   *  - grouped/paren expr    '(' inner_attributes expr ')'
-   *  - array expr            '[' inner_attributes array_elems? ']'
-   *  - await expr            expr '.' 'await'
-   *  - (array/slice) index expr  expr '[' expr ']'
-   *  - tuple expr            '(' inner_attributes tuple_elems? ')'
-   *      note that a single elem tuple is distinguished from a grouped expr
-   * by a trailing comma, i.e. a grouped expr is preferred over a tuple expr
-   *  - tuple index expr      expr '.' tuple_index
-   *  - struct expr           path_in_expr (and optional other stuff)
-   *  - enum variant expr     path_in_expr (and optional other stuff)
-   *      this means that there is no syntactic difference between an enum
-   * variant and a struct
-   *      - only name resolution can tell the difference. Thus, maybe rework
-   * AST to take this into account ("struct or enum" nodes?)
-   *  - (function) call expr  expr '(' call_params? ')'
-   *  - method call expr      expr '.' path_expr_segment '(' call_params? ')'
-   *  - field expr            expr '.' identifier
-   *      note that method call expr is preferred, i.e. field expr must not be
-   * followed by parenthesised expression sequence.
-   *  - closure expr          'move'? ( '||' | '|' closure_params? '|' ) (
-   * expr | '->' type_no_bounds block_expr )
-   *  - continue expr         'continue' labelled_lifetime?
-   *  - break expr            'break' labelled_lifetime? expr?
-   *  - range expr            many different types but all involve '..' or
-   * '..='
-   *  - return expr           'return' as 1st tok
-   *  - macro invocation      identifier then :: or identifier then !
-   * (simple_path '!')
-   *
-   * any that have rules beginning with 'expr' should probably be
-   * pratt-parsed,
-   * with parsing type to use determined by token AND lookahead. */
+  bool has_semi = false;
 
-  // ok well at least can do easy ones
-  const_TokenPtr t = lexer.peek_token ();
-  switch (t->get_id ())
+  if (restrictions.consume_semi)
     {
-    case RETURN_TOK:
-      // return expr
-      return parse_return_expr (std::move (outer_attrs));
-    case BREAK:
-      // break expr
-      return parse_break_expr (std::move (outer_attrs));
-    case CONTINUE:
-      // continue expr
-      return parse_continue_expr (std::move (outer_attrs));
-    case MOVE:
-      // closure expr (though not all closure exprs require this)
-      return parse_closure_expr (std::move (outer_attrs));
-      default: {
-       /* HACK: piggyback on pratt parsed expr and abuse polymorphism to
-        * essentially downcast */
-
-       std::unique_ptr<AST::Expr> expr
-         = parse_expr (std::move (outer_attrs), restrictions);
-
-       if (expr == nullptr)
-         {
-           Error error (t->get_locus (),
-                        "failed to parse expression for expression without "
-                        "block (pratt-parsed expression is null)");
-           add_error (std::move (error));
-
-           return nullptr;
-         }
-
-       std::unique_ptr<AST::ExprWithoutBlock> expr_without_block (
-         expr->as_expr_without_block ());
-
-       if (expr_without_block != nullptr)
-         {
-           return expr_without_block;
-         }
-       else
-         {
-           Error error (t->get_locus (),
-                        "converted expr without block is null");
-           add_error (std::move (error));
-
-           return nullptr;
-         }
-      }
+      if (skip_token (SEMICOLON))
+       has_semi = true;
+      else if (expr->is_expr_without_block ())
+       return nullptr;
     }
+
+  return std::unique_ptr<AST::ExprStmt> (
+    new AST::ExprStmt (std::move (expr), locus, has_semi));
 }
 
 // Parses a block expression, including the curly braces at start and end.
@@ -7565,12 +7292,12 @@ Parser<ManagedTokenSource>::parse_block_expr (AST::AttrVec outer_attrs,
   const_TokenPtr t = lexer.peek_token ();
   while (t->get_id () != RIGHT_CURLY)
     {
-      ExprOrStmt expr_or_stmt = parse_stmt_or_expr_without_block ();
+      ExprOrStmt expr_or_stmt = parse_stmt_or_expr ();
       if (expr_or_stmt.is_error ())
        {
-         Error error (t->get_locus (),
-                      "failed to parse statement or expression without "
-                      "block in block expression");
+         Error error (
+           t->get_locus (),
+           "failed to parse statement or expression in block expression");
          add_error (std::move (error));
 
          return nullptr;
@@ -7926,18 +7653,17 @@ Parser<ManagedTokenSource>::parse_continue_expr (AST::AttrVec outer_attrs,
 // Parses a loop label used in loop expressions.
 template <typename ManagedTokenSource>
 AST::LoopLabel
-Parser<ManagedTokenSource>::parse_loop_label ()
+Parser<ManagedTokenSource>::parse_loop_label (const_TokenPtr tok)
 {
   // parse lifetime - if doesn't exist, assume no label
-  const_TokenPtr t = lexer.peek_token ();
-  if (t->get_id () != LIFETIME)
+  if (tok->get_id () != LIFETIME)
     {
       // not necessarily an error
       return AST::LoopLabel::error ();
     }
   /* FIXME: check for named lifetime requirement here? or check in semantic
    * analysis phase? */
-  AST::Lifetime label = parse_lifetime ();
+  AST::Lifetime label = lifetime_from_token (tok);
 
   if (!skip_token (COLON))
     {
@@ -7945,7 +7671,7 @@ Parser<ManagedTokenSource>::parse_loop_label ()
       return AST::LoopLabel::error ();
     }
 
-  return AST::LoopLabel (std::move (label), t->get_locus ());
+  return AST::LoopLabel (std::move (label), tok->get_locus ());
 }
 
 /* Parses an if expression of any kind, including with else, else if, else if
@@ -8496,7 +8222,7 @@ Parser<ManagedTokenSource>::parse_for_loop_expr (AST::AttrVec outer_attrs,
     locus = lexer.peek_token ()->get_locus ();
   else
     locus = label.get_locus ();
-  skip_token (FOR);
+  maybe_skip_token (FOR);
 
   // parse pattern, which is required
   std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
@@ -8553,18 +8279,19 @@ Parser<ManagedTokenSource>::parse_for_loop_expr (AST::AttrVec outer_attrs,
 // Parses a loop expression with label (any kind of loop - disambiguates).
 template <typename ManagedTokenSource>
 std::unique_ptr<AST::BaseLoopExpr>
-Parser<ManagedTokenSource>::parse_labelled_loop_expr (AST::AttrVec outer_attrs)
+Parser<ManagedTokenSource>::parse_labelled_loop_expr (const_TokenPtr tok,
+                                                     AST::AttrVec outer_attrs)
 {
   /* TODO: decide whether it should not work if there is no label, or parse it
    * with no label at the moment, I will make it not work with no label
    * because that's the implication. */
 
-  if (lexer.peek_token ()->get_id () != LIFETIME)
+  if (tok->get_id () != LIFETIME)
     {
-      Error error (lexer.peek_token ()->get_locus (),
+      Error error (tok->get_locus (),
                   "expected lifetime in labelled loop expr (to parse loop "
                   "label) - found %qs",
-                  lexer.peek_token ()->get_token_description ());
+                  tok->get_token_description ());
       add_error (std::move (error));
 
       // skip?
@@ -8572,7 +8299,7 @@ Parser<ManagedTokenSource>::parse_labelled_loop_expr (AST::AttrVec outer_attrs)
     }
 
   // parse loop label (required)
-  AST::LoopLabel label = parse_loop_label ();
+  AST::LoopLabel label = parse_loop_label (tok);
   if (label.is_error ())
     {
       Error error (lexer.peek_token ()->get_locus (),
@@ -8683,10 +8410,9 @@ Parser<ManagedTokenSource>::parse_match_expr (AST::AttrVec outer_attrs,
       restrictions.expr_can_be_stmt = true;
       restrictions.consume_semi = false;
 
-      std::unique_ptr<AST::ExprStmt> expr_stmt
-       = parse_expr_stmt ({}, restrictions);
+      std::unique_ptr<AST::Expr> expr = parse_expr ({}, restrictions);
 
-      if (expr_stmt == nullptr)
+      if (expr == nullptr)
        {
          Error error (lexer.peek_token ()->get_locus (),
                       "failed to parse expr in match arm in match expr");
@@ -8696,9 +8422,7 @@ Parser<ManagedTokenSource>::parse_match_expr (AST::AttrVec outer_attrs,
          return nullptr;
        }
 
-      std::unique_ptr<AST::Expr> expr = expr_stmt->get_expr ()->clone_expr ();
-      bool is_expr_without_block
-       = expr_stmt->get_expr ()->is_expr_without_block ();
+      bool is_expr_without_block = expr->is_expr_without_block ();
 
       match_arms.push_back (AST::MatchCase (std::move (arm), std::move (expr)));
 
@@ -11759,38 +11483,12 @@ Parser<ManagedTokenSource>::parse_struct_pattern_field_partial (
     }
 }
 
-template <typename ManagedTokenSource>
-ExprOrStmt
-Parser<ManagedTokenSource>::parse_stmt_or_expr_with_block (
-  AST::AttrVec outer_attrs)
-{
-  auto expr = parse_expr_with_block (std::move (outer_attrs));
-  if (expr == nullptr)
-    return ExprOrStmt::create_error ();
-
-  auto tok = lexer.peek_token ();
-
-  // tail expr in a block expr
-  if (tok->get_id () == RIGHT_CURLY)
-    return ExprOrStmt (std::move (expr));
-
-  // internal block expr must either have semicolons followed, or evaluate to
-  // ()
-  auto locus = expr->get_locus ();
-  std::unique_ptr<AST::ExprStmt> stmt (
-    new AST::ExprStmt (std::move (expr), locus, tok->get_id () == SEMICOLON));
-  if (tok->get_id () == SEMICOLON)
-    lexer.skip_token ();
-
-  return ExprOrStmt (std::move (stmt));
-}
-
 /* Parses a statement or expression (depending on whether a trailing semicolon
  * exists). Useful for block expressions where it cannot be determined through
  * lookahead whether it is a statement or expression to be parsed. */
 template <typename ManagedTokenSource>
 ExprOrStmt
-Parser<ManagedTokenSource>::parse_stmt_or_expr_without_block ()
+Parser<ManagedTokenSource>::parse_stmt_or_expr ()
 {
   // quick exit for empty statement
   const_TokenPtr t = lexer.peek_token ();
@@ -11804,6 +11502,8 @@ Parser<ManagedTokenSource>::parse_stmt_or_expr_without_block ()
 
   // parse outer attributes
   AST::AttrVec outer_attrs = parse_outer_attributes ();
+  ParseRestrictions restrictions;
+  restrictions.expr_can_be_stmt = true;
 
   // parsing this will be annoying because of the many different possibilities
   /* best may be just to copy paste in parse_item switch, and failing that try
@@ -11855,8 +11555,8 @@ Parser<ManagedTokenSource>::parse_stmt_or_expr_without_block ()
        switch (t2->get_id ())
          {
            case LEFT_CURLY: {
-             // unsafe block
-             return parse_stmt_or_expr_with_block (std::move (outer_attrs));
+             // unsafe block: parse as expression
+             break;
            }
          case AUTO:
            case TRAIT: {
@@ -11887,75 +11587,14 @@ Parser<ManagedTokenSource>::parse_stmt_or_expr_without_block ()
            // skip somewhere?
            return ExprOrStmt::create_error ();
          }
-      }
-    case SUPER:
-    case SELF:
-    case CRATE:
-      case DOLLAR_SIGN: {
-       /* path-based thing so struct/enum or path or macro invocation of a
-        * kind. however, the expressions are composable (i think) */
-
-       std::unique_ptr<AST::ExprWithoutBlock> expr
-         = parse_expr_without_block ();
-
-       if (lexer.peek_token ()->get_id () == SEMICOLON)
-         {
-           // must be expression statement
-           lexer.skip_token ();
-
-           std::unique_ptr<AST::ExprStmt> stmt (
-             new AST::ExprStmt (std::move (expr), t->get_locus (), true));
-           return ExprOrStmt (std::move (stmt));
-         }
-
-       // return expression
-       return ExprOrStmt (std::move (expr));
+       break;
       }
       /* FIXME: this is either a macro invocation or macro invocation semi.
        * start parsing to determine which one it is. */
       // FIXME: or this is another path-based thing - struct/enum or path
       // itself return parse_path_based_stmt_or_expr(std::move(outer_attrs));
       // FIXME: old code there
-    case LOOP:
-    case WHILE:
-    case FOR:
-    case IF:
-    case MATCH_TOK:
-    case LEFT_CURLY:
-      case ASYNC: {
-       return parse_stmt_or_expr_with_block (std::move (outer_attrs));
-      }
-      case LIFETIME: {
-       /* FIXME: are there any expressions without blocks that can have
-        * lifetime as their first token? Or is loop expr the only one? */
-       // safe side for now:
-       const_TokenPtr t2 = lexer.peek_token (2);
-       if (lexer.peek_token (1)->get_id () == COLON
-           && (t2->get_id () == LOOP || t2->get_id () == WHILE
-               || t2->get_id () == FOR))
-         {
-           return parse_stmt_or_expr_with_block (std::move (outer_attrs));
-         }
-       else
-         {
-           // should be expr without block
-           std::unique_ptr<AST::ExprWithoutBlock> expr
-             = parse_expr_without_block (std::move (outer_attrs));
 
-           if (lexer.peek_token ()->get_id () == SEMICOLON)
-             {
-               // must be expression statement
-               lexer.skip_token ();
-
-               std::unique_ptr<AST::ExprStmt> stmt (
-                 new AST::ExprStmt (std::move (expr), t->get_locus (), true));
-               return ExprOrStmt (std::move (stmt));
-             }
-
-           // return expression
-           return ExprOrStmt (std::move (expr));
-         }
-      }
     // crappy hack to do union "keyword"
     case IDENTIFIER:
       if (t->get_str () == "union"
@@ -11973,62 +11612,49 @@ Parser<ManagedTokenSource>::parse_stmt_or_expr_without_block ()
            parse_macro_rules_def (std::move (outer_attrs)));
          return ExprOrStmt (std::move (item));
        }
-      else if (lexer.peek_token (1)->get_id () == SCOPE_RESOLUTION
-              || lexer.peek_token (1)->get_id () == EXCLAM
-              || lexer.peek_token (1)->get_id () == LEFT_CURLY)
+      else
        {
-         /* path (probably) or macro invocation or struct or enum, so
-          * probably a macro invocation semi decide how to parse - probably
-          * parse path and then get macro from it */
+         break;
+       }
+    default:
+      break;
+    }
 
-         // FIXME: old code was good until composability was required
-         // return parse_path_based_stmt_or_expr(std::move(outer_attrs));
-         std::unique_ptr<AST::ExprWithoutBlock> expr
-           = parse_expr_without_block (std::move (outer_attrs));
+  /* expression statement or expression itself - parse
+   * expression then make it statement if semi afterwards */
 
-         if (lexer.peek_token ()->get_id () == SEMICOLON)
-           {
-             // must be expression statement
-             lexer.skip_token ();
+  std::unique_ptr<AST::Expr> expr
+    = parse_expr (std::move (outer_attrs), restrictions);
 
-             std::unique_ptr<AST::ExprStmt> stmt (
-               new AST::ExprStmt (std::move (expr), t->get_locus (), true));
-             return ExprOrStmt (std::move (stmt));
-           }
+  const_TokenPtr after_expr = lexer.peek_token ();
+  if (after_expr->get_id () == SEMICOLON)
+    {
+      // must be expression statement
+      lexer.skip_token ();
 
-         // return expression
-         return ExprOrStmt (std::move (expr));
+      if (expr)
+       {
+         std::unique_ptr<AST::ExprStmt> stmt (
+           new AST::ExprStmt (std::move (expr), t->get_locus (), true));
+         return ExprOrStmt (std::move (stmt));
        }
-      gcc_fallthrough ();
-      // TODO: find out how to disable gcc "implicit fallthrough" warning
-      default: {
-       /* expression statement (without block) or expression itself - parse
-        * expression then make it statement if semi afterwards */
-
-       std::unique_ptr<AST::ExprWithoutBlock> expr
-         = parse_expr_without_block (std::move (outer_attrs));
-
-       if (lexer.peek_token ()->get_id () == SEMICOLON)
-         {
-           // must be expression statement
-           lexer.skip_token ();
-
-           if (expr)
-             {
-               std::unique_ptr<AST::ExprStmt> stmt (
-                 new AST::ExprStmt (std::move (expr), t->get_locus (), true));
-               return ExprOrStmt (std::move (stmt));
-             }
-           else
-             {
-               return ExprOrStmt::create_error ();
-             }
-         }
+      else
+       {
+         return ExprOrStmt::create_error ();
+       }
+    }
 
-       // return expression
-       return ExprOrStmt (std::move (expr));
-      }
+  if (expr && !expr->is_expr_without_block ()
+      && after_expr->get_id () != RIGHT_CURLY)
+    {
+      // block expression statement.
+      std::unique_ptr<AST::ExprStmt> stmt (
+       new AST::ExprStmt (std::move (expr), t->get_locus (), false));
+      return ExprOrStmt (std::move (stmt));
     }
+
+  // return expression
+  return ExprOrStmt (std::move (expr));
 }
 
 /* Parses a statement or expression beginning with a path (i.e. macro,
@@ -12655,9 +12281,7 @@ Parser<ManagedTokenSource>::skip_after_end_attribute ()
 }
 
 /* Pratt parser impl of parse_expr. FIXME: this is only provisional and
- * probably will be changed.
- * FIXME: this may only parse expressions without blocks as they are the only
- * expressions to have precedence? */
+ * probably will be changed. */
 template <typename ManagedTokenSource>
 std::unique_ptr<AST::Expr>
 Parser<ManagedTokenSource>::parse_expr (int right_binding_power,
@@ -12686,6 +12310,9 @@ Parser<ManagedTokenSource>::parse_expr (int right_binding_power,
 
   lexer.skip_token ();
 
+  bool expr_can_be_stmt = restrictions.expr_can_be_stmt;
+  restrictions.expr_can_be_stmt = false;
+
   // parse null denotation (unary part of expression)
   std::unique_ptr<AST::Expr> expr
     = null_denotation (current_token, {}, restrictions);
@@ -12697,12 +12324,23 @@ Parser<ManagedTokenSource>::parse_expr (int right_binding_power,
       return nullptr;
     }
 
+  current_token = lexer.peek_token ();
+
+  if (expr_can_be_stmt && !expr->is_expr_without_block ()
+      && current_token->get_id () != DOT
+      && current_token->get_id () != QUESTION_MARK)
+    {
+      rust_debug ("statement expression with block");
+      expr->set_outer_attrs (std::move (outer_attrs));
+      return expr;
+    }
+
   // stop parsing if find lower priority token - parse higher priority first
-  while (right_binding_power < left_binding_power (lexer.peek_token ()))
+  while (right_binding_power < left_binding_power (current_token))
     {
-      current_token = lexer.peek_token ();
       lexer.skip_token ();
 
+      // FIXME attributes should generally be applied to the null denotation.
       expr = left_denotation (current_token, std::move (expr),
                              std::move (outer_attrs), restrictions);
 
@@ -12713,6 +12351,8 @@ Parser<ManagedTokenSource>::parse_expr (int right_binding_power,
 
          return nullptr;
        }
+
+      current_token = lexer.peek_token ();
     }
 
   return expr;
@@ -12727,10 +12367,7 @@ Parser<ManagedTokenSource>::parse_expr (AST::AttrVec outer_attrs,
   return parse_expr (LBP_LOWEST, std::move (outer_attrs), restrictions);
 }
 
-/* Determines action to take when finding token at beginning of expression.
- * FIXME: this may only apply to precedence-capable expressions (which are all
- * expressions without blocks), so make return type ExprWithoutBlock? It would
- * simplify stuff. */
+/* Determines action to take when finding token at beginning of expression. */
 template <typename ManagedTokenSource>
 std::unique_ptr<AST::Expr>
 Parser<ManagedTokenSource>::null_denotation (const_TokenPtr tok,
@@ -13144,6 +12781,8 @@ Parser<ManagedTokenSource>::null_denotation (const_TokenPtr tok,
          // if expr
          return parse_if_expr (std::move (outer_attrs), tok->get_locus ());
        }
+    case LIFETIME:
+      return parse_labelled_loop_expr (tok, std::move (outer_attrs));
     case LOOP:
       return parse_loop_expr (std::move (outer_attrs), AST::LoopLabel::error (),
                              tok->get_locus ());
@@ -13158,6 +12797,9 @@ Parser<ManagedTokenSource>::null_denotation (const_TokenPtr tok,
                                        AST::LoopLabel::error (),
                                        tok->get_locus ());
        }
+    case FOR:
+      return parse_for_loop_expr (std::move (outer_attrs),
+                                 AST::LoopLabel::error ());
     case MATCH_TOK:
       // also an expression with block
       return parse_match_expr (std::move (outer_attrs), tok->get_locus ());
@@ -14700,7 +14342,9 @@ Parser<ManagedTokenSource>::parse_macro_invocation_partial (
 
   return AST::MacroInvocation::Regular (
     AST::MacroInvocData (std::move (converted_path), std::move (tok_tree)),
-    std::move (outer_attrs), macro_locus, restrictions.expr_can_be_stmt);
+    std::move (outer_attrs), macro_locus,
+    restrictions.expr_can_be_stmt
+      && lexer.peek_token ()->get_id () == SEMICOLON);
 }
 
 /* Parses a struct expr struct with a path in expression already parsed (but
index a092290d880069c63c6e6799961c715d5534617d..1e7e5262eec5882b81948488de9cd64591161ff0 100644 (file)
@@ -276,6 +276,7 @@ private:
   template <typename EndTokenPred>
   std::vector<AST::Lifetime> parse_lifetime_bounds (EndTokenPred is_end_token);
   AST::Lifetime parse_lifetime ();
+  AST::Lifetime lifetime_from_token (const_TokenPtr tok);
   std::unique_ptr<AST::ExternalTypeItem>
   parse_external_type_item (AST::Visibility vis, AST::AttrVec outer_attrs);
   std::unique_ptr<AST::TypeAlias> parse_type_alias (AST::Visibility vis,
@@ -531,13 +532,6 @@ private:
     AST::AttrVec outer_attrs,
     ParseRestrictions restrictions = ParseRestrictions ());
 
-  // Expression-related (non-Pratt parsed)
-  std::unique_ptr<AST::ExprWithBlock>
-  parse_expr_with_block (AST::AttrVec outer_attrs);
-  std::unique_ptr<AST::ExprWithoutBlock>
-  parse_expr_without_block (AST::AttrVec outer_attrs = AST::AttrVec (),
-                           ParseRestrictions restrictions
-                           = ParseRestrictions ());
   // When given a pratt_parsed_loc, use it as the location of the
   // first token parsed in the expression (the parsing of that first
   // token should be skipped).
@@ -569,8 +563,9 @@ private:
   std::vector<std::unique_ptr<AST::Pattern> >
   parse_match_arm_patterns (TokenId end_token_id);
   std::unique_ptr<AST::BaseLoopExpr>
-  parse_labelled_loop_expr (AST::AttrVec outer_attrs = AST::AttrVec ());
-  AST::LoopLabel parse_loop_label ();
+  parse_labelled_loop_expr (const_TokenPtr tok,
+                           AST::AttrVec outer_attrs = AST::AttrVec ());
+  AST::LoopLabel parse_loop_label (const_TokenPtr tok);
   std::unique_ptr<AST::AsyncBlockExpr>
   parse_async_block_expr (AST::AttrVec outer_attrs = AST::AttrVec ());
   std::unique_ptr<AST::GroupedExpr> parse_grouped_expr (AST::AttrVec outer_attrs
@@ -637,14 +632,7 @@ private:
   std::unique_ptr<AST::ExprStmt> parse_expr_stmt (AST::AttrVec outer_attrs,
                                                  ParseRestrictions restrictions
                                                  = ParseRestrictions ());
-  std::unique_ptr<AST::ExprStmt>
-  parse_expr_stmt_with_block (AST::AttrVec outer_attrs);
-  std::unique_ptr<AST::ExprStmt>
-  parse_expr_stmt_without_block (AST::AttrVec outer_attrs,
-                                ParseRestrictions restrictions
-                                = ParseRestrictions ());
-  ExprOrStmt parse_stmt_or_expr_without_block ();
-  ExprOrStmt parse_stmt_or_expr_with_block (AST::AttrVec outer_attrs);
+  ExprOrStmt parse_stmt_or_expr ();
   ExprOrStmt parse_macro_invocation_maybe_semi (AST::AttrVec outer_attrs);
   ExprOrStmt parse_path_based_stmt_or_expr (AST::AttrVec outer_attrs);
 
diff --git a/gcc/testsuite/rust/compile/for_expr.rs b/gcc/testsuite/rust/compile/for_expr.rs
new file mode 100644 (file)
index 0000000..95dcf2e
--- /dev/null
@@ -0,0 +1,6 @@
+// { dg-options "-fsyntax-only" }
+
+fn main() {
+    for a in 0..10 {}
+    (for b in 0..10 {})
+}
diff --git a/gcc/testsuite/rust/compile/issue-2189.rs b/gcc/testsuite/rust/compile/issue-2189.rs
new file mode 100644 (file)
index 0000000..96bb9c1
--- /dev/null
@@ -0,0 +1,5 @@
+#[allow(unused_unsafe)]
+
+pub fn foo() {
+    unsafe { (0, 1) }.0;
+}
index cb8027b9c7f8863f27324e37b46c13fc98d7da5f..2d60d6d72b89ae02056e3635084e958e58e10475 100644 (file)
@@ -1,21 +1,18 @@
 // #407
 pub fn loopy()  {
     let mut a = 1;
-    // { dg-error {failed to parse expr with block in parsing expr statement} "" { target *-*-* } .+2 }
-    // { dg-error {failed to parse statement or expression without block in block expression} "" { target *-*-* } .+1 }
     loop {
-        // { dg-error {failed to parse expr with block in parsing expr statement} "" { target *-*-* } .+2 }
-        // { dg-error {failed to parse statement or expression without block in block expression} "" { target *-*-* } .+1 }
-       if a < 40 {
-           a + = 1; // { dg-error "found unexpected token '=' in null denotation" }
-            // { dg-error {failed to parse expression for expression without block \(pratt-parsed expression is null\)} "" { target *-*-* } .-1 }
-            // { dg-error {failed to parse statement or expression without block in block expression} "" { target *-*-* } .-2 }
-            // { dg-error {failed to parse if body block expression in if expression} "" { target *-*-* } .-3 }
-            // { dg-error {could not parse loop body in \(infinite\) loop expression} "" { target *-*-* } .+1 }
-       } else {
-           break;
-       }
+        // { dg-error {failed to parse statement or expression in block expression} "" { target *-*-* } .-1 }
+        if a < 40 {
+            // { dg-error {failed to parse statement or expression in block expression} "" { target *-*-* } .-1 }
+            a + = 1; // { dg-error "found unexpected token '=' in null denotation" }
+            // { dg-error {failed to parse statement or expression in block expression} "" { target *-*-* } .-1 }
+            // { dg-error {failed to parse if body block expression in if expression} "" { target *-*-* } .-2 }
+            // { dg-error {could not parse loop body in \(infinite\) loop expression} "" { target *-*-* } .-3 }
+            // { dg-error {unrecognised token 'integer literal' for start of item} "" { target *-*-* } .-4 }
+            // { dg-error {failed to parse item in crate} "" { target *-*-* } .-5 }
+        } else {
+            break;
+        }
     }
 }
-// { dg-error {unrecognised token '\}' for start of item} "" { target *-*-* } .-1 }
-// { dg-error {failed to parse item in crate} "" { target *-*-* } .-2 }
index 530b7ddfc125d2ed662f0135a4893e0657a17b95..bd0d3069b10993942ba7aa7de9e77aba9a0ea1bc 100644 (file)
@@ -2,8 +2,7 @@
 fn test()  {
     let mut a = 1;
     a + = 1; // { dg-error "found unexpected token '=' in null denotation" }
-    // { dg-error {failed to parse expression for expression without block \(pratt-parsed expression is null\)} "" { target *-*-* } .-1 }
-    // { dg-error {failed to parse statement or expression without block in block expression} "" { target *-*-* } .-2 }
-    // { dg-error {unrecognised token 'integer literal' for start of item} "" { target *-*-* } .-3 }
-    // { dg-error {failed to parse item in crate} "" { target *-*-* } .-4 }
+    // { dg-error {failed to parse statement or expression in block expression} "" { target *-*-* } .-1 }
+    // { dg-error {unrecognised token 'integer literal' for start of item} "" { target *-*-* } .-2 }
+    // { dg-error {failed to parse item in crate} "" { target *-*-* } .-3 }
 }
index 8e120685c1a3af1714a8ef14287ff34d52e57c1d..e5f9b29bd59550f96dbbef6d193506b48672ee1f 100644 (file)
@@ -2,7 +2,7 @@ fn main() {
     let _ = 42;
     let a = _ + 123; // { dg-error "use of '_' is not allowed on the right-side of an assignment" }
                      // { dg-error {failed to parse expression in let statement} "" { target *-*-* } .-1  }
-                     // { dg-error {failed to parse statement or expression without block in block expression} "" { target *-*-* } .-2 }
+                     // { dg-error {failed to parse statement or expression in block expression} "" { target *-*-* } .-2 }
                      // { dg-error {unrecognised token '\}' for start of item} "" { target *-*-* } .+2 }
                      // { dg-error {failed to parse item in crate} "" { target *-*-* } .+1 }
 }
diff --git a/gcc/testsuite/rust/compile/macro_call_statement.rs b/gcc/testsuite/rust/compile/macro_call_statement.rs
new file mode 100644 (file)
index 0000000..3d18cc9
--- /dev/null
@@ -0,0 +1,11 @@
+fn f() {}
+
+macro_rules! panic {
+    () => (
+        crate::f()
+    );
+}
+
+fn main() {
+    panic!();
+}
diff --git a/gcc/testsuite/rust/compile/stmt_with_block_dot.rs b/gcc/testsuite/rust/compile/stmt_with_block_dot.rs
new file mode 100644 (file)
index 0000000..ae623c3
--- /dev/null
@@ -0,0 +1,20 @@
+pub struct A(i32, i32);
+
+trait Clone {
+    fn clone(&self) -> Self;
+}
+
+impl Clone for A {
+    fn clone(&self) -> A {
+        A(self.0, self.1)
+    }
+}
+
+pub fn foo(c: bool, d: i32) {
+    { (0, 1) }.0 + 1;
+    if c { A(4, 5) } else { A(12, 23) }.clone().0 * 5;
+    match d { 1 => A(-5, 0), _ => A(-23, -6) }.clone();
+    match d {
+        _ => { (4, 5) }.1 / 3,
+    };
+}
diff --git a/gcc/testsuite/rust/compile/torture/loop8.rs b/gcc/testsuite/rust/compile/torture/loop8.rs
new file mode 100644 (file)
index 0000000..ef6ea94
--- /dev/null
@@ -0,0 +1,7 @@
+fn main() {
+    ('outer: loop {
+        'inner: loop {
+            break 'outer;
+        }
+    })
+}