From: Matthew Jasper Date: Wed, 10 May 2023 01:08:37 +0000 (+0100) Subject: gccrs: Fix parsing of block expressions followed by `.` X-Git-Tag: basepoints/gcc-15~2546 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=48408712cc8cc39cba50c10938a284661ddece87;p=thirdparty%2Fgcc.git gccrs: Fix parsing of block expressions followed by `.` `{ ... }.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 --- diff --git a/gcc/rust/ast/rust-ast.h b/gcc/rust/ast/rust-ast.h index 1591469f6d88..a551b461a6a0 100644 --- a/gcc/rust/ast/rust-ast.h +++ b/gcc/rust/ast/rust-ast.h @@ -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) = 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) = 0; - NodeId node_id; }; @@ -1023,13 +1019,6 @@ public: { return std::unique_ptr (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 diff --git a/gcc/rust/expand/rust-macro-expand.cc b/gcc/rust/expand/rust-macro-expand.cc index 5f6c50e29f47..f347335f2efe 100644 --- a/gcc/rust/expand/rust-macro-expand.cc +++ b/gcc/rust/expand/rust-macro-expand.cc @@ -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"); diff --git a/gcc/rust/hir/tree/rust-hir.h b/gcc/rust/hir/tree/rust-hir.h index 31f14941e463..0258f5f24fbb 100644 --- a/gcc/rust/hir/tree/rust-hir.h +++ b/gcc/rust/hir/tree/rust-hir.h @@ -288,10 +288,6 @@ public: return std::unique_ptr (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 (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; diff --git a/gcc/rust/parse/rust-parse-impl.h b/gcc/rust/parse/rust-parse-impl.h index f4df6fa8a1f5..f5f3c7bb527a 100644 --- a/gcc/rust/parse/rust-parse-impl.h +++ b/gcc/rust/parse/rust-parse-impl.h @@ -4019,7 +4019,6 @@ AST::Lifetime Parser::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::parse_lifetime () } lexer.skip_token (); - std::string lifetime_ident = lifetime_tok->get_str (); + return lifetime_from_token (lifetime_tok); +} + +template +AST::Lifetime +Parser::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::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::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 std::unique_ptr Parser::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 -std::unique_ptr -Parser::parse_expr_with_block (AST::AttrVec outer_attrs) -{ - std::unique_ptr 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 -std::unique_ptr -Parser::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 ( - 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 -std::unique_ptr -Parser::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 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 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 ( - new AST::ExprStmt (std::move (expr), locus, true)); -} - -/* Parses an expression without a block associated with it (further - * disambiguates). */ -template -std::unique_ptr -Parser::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 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 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 ( + 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::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::parse_continue_expr (AST::AttrVec outer_attrs, // Parses a loop label used in loop expressions. template AST::LoopLabel -Parser::parse_loop_label () +Parser::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::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::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 pattern = parse_pattern (); @@ -8553,18 +8279,19 @@ Parser::parse_for_loop_expr (AST::AttrVec outer_attrs, // Parses a loop expression with label (any kind of loop - disambiguates). template std::unique_ptr -Parser::parse_labelled_loop_expr (AST::AttrVec outer_attrs) +Parser::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::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::parse_match_expr (AST::AttrVec outer_attrs, restrictions.expr_can_be_stmt = true; restrictions.consume_semi = false; - std::unique_ptr expr_stmt - = parse_expr_stmt ({}, restrictions); + std::unique_ptr 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::parse_match_expr (AST::AttrVec outer_attrs, return nullptr; } - std::unique_ptr 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::parse_struct_pattern_field_partial ( } } -template -ExprOrStmt -Parser::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 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 ExprOrStmt -Parser::parse_stmt_or_expr_without_block () +Parser::parse_stmt_or_expr () { // quick exit for empty statement const_TokenPtr t = lexer.peek_token (); @@ -11804,6 +11502,8 @@ Parser::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::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::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 expr - = parse_expr_without_block (); - - if (lexer.peek_token ()->get_id () == SEMICOLON) - { - // must be expression statement - lexer.skip_token (); - - std::unique_ptr 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 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 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::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 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 expr + = parse_expr (std::move (outer_attrs), restrictions); - std::unique_ptr 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 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 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 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 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::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 std::unique_ptr Parser::parse_expr (int right_binding_power, @@ -12686,6 +12310,9 @@ Parser::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 expr = null_denotation (current_token, {}, restrictions); @@ -12697,12 +12324,23 @@ Parser::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::parse_expr (int right_binding_power, return nullptr; } + + current_token = lexer.peek_token (); } return expr; @@ -12727,10 +12367,7 @@ Parser::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 std::unique_ptr Parser::null_denotation (const_TokenPtr tok, @@ -13144,6 +12781,8 @@ Parser::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::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::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 diff --git a/gcc/rust/parse/rust-parse.h b/gcc/rust/parse/rust-parse.h index a092290d8800..1e7e5262eec5 100644 --- a/gcc/rust/parse/rust-parse.h +++ b/gcc/rust/parse/rust-parse.h @@ -276,6 +276,7 @@ private: template std::vector parse_lifetime_bounds (EndTokenPred is_end_token); AST::Lifetime parse_lifetime (); + AST::Lifetime lifetime_from_token (const_TokenPtr tok); std::unique_ptr parse_external_type_item (AST::Visibility vis, AST::AttrVec outer_attrs); std::unique_ptr 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 - parse_expr_with_block (AST::AttrVec outer_attrs); - std::unique_ptr - 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 > parse_match_arm_patterns (TokenId end_token_id); std::unique_ptr - 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 parse_async_block_expr (AST::AttrVec outer_attrs = AST::AttrVec ()); std::unique_ptr parse_grouped_expr (AST::AttrVec outer_attrs @@ -637,14 +632,7 @@ private: std::unique_ptr parse_expr_stmt (AST::AttrVec outer_attrs, ParseRestrictions restrictions = ParseRestrictions ()); - std::unique_ptr - parse_expr_stmt_with_block (AST::AttrVec outer_attrs); - std::unique_ptr - 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 index 000000000000..95dcf2ef5055 --- /dev/null +++ b/gcc/testsuite/rust/compile/for_expr.rs @@ -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 index 000000000000..96bb9c167734 --- /dev/null +++ b/gcc/testsuite/rust/compile/issue-2189.rs @@ -0,0 +1,5 @@ +#[allow(unused_unsafe)] + +pub fn foo() { + unsafe { (0, 1) }.0; +} diff --git a/gcc/testsuite/rust/compile/issue-407-2.rs b/gcc/testsuite/rust/compile/issue-407-2.rs index cb8027b9c7f8..2d60d6d72b89 100644 --- a/gcc/testsuite/rust/compile/issue-407-2.rs +++ b/gcc/testsuite/rust/compile/issue-407-2.rs @@ -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 } diff --git a/gcc/testsuite/rust/compile/issue-407.rs b/gcc/testsuite/rust/compile/issue-407.rs index 530b7ddfc125..bd0d3069b109 100644 --- a/gcc/testsuite/rust/compile/issue-407.rs +++ b/gcc/testsuite/rust/compile/issue-407.rs @@ -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 } } diff --git a/gcc/testsuite/rust/compile/issue-867.rs b/gcc/testsuite/rust/compile/issue-867.rs index 8e120685c1a3..e5f9b29bd595 100644 --- a/gcc/testsuite/rust/compile/issue-867.rs +++ b/gcc/testsuite/rust/compile/issue-867.rs @@ -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 index 000000000000..3d18cc94a226 --- /dev/null +++ b/gcc/testsuite/rust/compile/macro_call_statement.rs @@ -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 index 000000000000..ae623c345579 --- /dev/null +++ b/gcc/testsuite/rust/compile/stmt_with_block_dot.rs @@ -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 index 000000000000..ef6ea94d4cea --- /dev/null +++ b/gcc/testsuite/rust/compile/torture/loop8.rs @@ -0,0 +1,7 @@ +fn main() { + ('outer: loop { + 'inner: loop { + break 'outer; + } + }) +}