From c5fdf8cc544f9647e2cf28a2da431bfa1faacd15 Mon Sep 17 00:00:00 2001 From: lrh2000 Date: Sat, 10 Apr 2021 22:53:32 +0800 Subject: [PATCH 1/2] The trailing expression is not necessarily without a block --- gcc/rust/ast/rust-expr.h | 10 +-- gcc/rust/ast/rust-stmt.h | 9 ++- gcc/rust/backend/rust-compile.cc | 5 +- gcc/rust/hir/rust-ast-lower-stmt.h | 3 +- gcc/rust/hir/tree/rust-hir-expr.h | 10 +-- gcc/rust/hir/tree/rust-hir-stmt.h | 9 ++- gcc/rust/hir/tree/rust-hir.h | 2 + gcc/rust/parse/rust-parse-impl.h | 61 +++++++++++++------ gcc/rust/parse/rust-parse.h | 14 ++--- gcc/rust/typecheck/rust-hir-type-check.cc | 6 +- gcc/testsuite/rust.test/compile/unused.rs | 2 +- .../xfail_compile/implicit_returns_err1.rs | 4 +- .../xfail_compile/implicit_returns_err3.rs | 3 +- .../xfail_compile/stmt_with_block_err1.rs | 17 ++++++ 14 files changed, 105 insertions(+), 50 deletions(-) create mode 100644 gcc/testsuite/rust.test/xfail_compile/stmt_with_block_err1.rs diff --git a/gcc/rust/ast/rust-expr.h b/gcc/rust/ast/rust-expr.h index dde833fe7fdf..eb56b657983c 100644 --- a/gcc/rust/ast/rust-expr.h +++ b/gcc/rust/ast/rust-expr.h @@ -2828,7 +2828,7 @@ class BlockExpr : public ExprWithBlock std::vector outer_attrs; std::vector inner_attrs; std::vector > statements; - std::unique_ptr expr; + std::unique_ptr expr; Location locus; bool marked_for_strip = false; @@ -2842,7 +2842,7 @@ class BlockExpr : public ExprWithBlock bool has_tail_expr () const { return expr != nullptr; } BlockExpr (std::vector > block_statements, - std::unique_ptr block_expr, + std::unique_ptr block_expr, std::vector inner_attribs, std::vector outer_attribs, Location locus) : outer_attrs (std::move (outer_attribs)), @@ -2859,7 +2859,7 @@ class BlockExpr : public ExprWithBlock { // guard to protect from null pointer dereference if (other.expr != nullptr) - expr = other.expr->clone_expr_without_block (); + expr = other.expr->clone_expr (); statements.reserve (other.statements.size ()); for (const auto &e : other.statements) @@ -2877,7 +2877,7 @@ class BlockExpr : public ExprWithBlock // guard to protect from null pointer dereference if (other.expr != nullptr) - expr = other.expr->clone_expr_without_block (); + expr = other.expr->clone_expr (); else expr = nullptr; @@ -2929,7 +2929,7 @@ class BlockExpr : public ExprWithBlock std::vector > &get_statements () { return statements; } // TODO: is this better? Or is a "vis_block" better? - std::unique_ptr &get_tail_expr () + std::unique_ptr &get_tail_expr () { rust_assert (has_tail_expr ()); return expr; diff --git a/gcc/rust/ast/rust-stmt.h b/gcc/rust/ast/rust-stmt.h index c840112da11a..72946774a1f3 100644 --- a/gcc/rust/ast/rust-stmt.h +++ b/gcc/rust/ast/rust-stmt.h @@ -269,14 +269,17 @@ class ExprStmtWithoutBlock : public ExprStmt class ExprStmtWithBlock : public ExprStmt { std::unique_ptr expr; + bool semicolon_followed; public: std::string as_string () const override; std::vector locals; - ExprStmtWithBlock (std::unique_ptr expr, Location locus) - : ExprStmt (locus), expr (std::move (expr)) + ExprStmtWithBlock (std::unique_ptr expr, Location locus, + bool semicolon_followed) + : ExprStmt (locus), expr (std::move (expr)), + semicolon_followed (semicolon_followed) {} // Copy constructor with clone @@ -318,6 +321,8 @@ class ExprStmtWithBlock : public ExprStmt return expr; } + bool is_semicolon_followed () const { return semicolon_followed; } + protected: /* Use covariance to implement clone function as returning this object rather * than base */ diff --git a/gcc/rust/backend/rust-compile.cc b/gcc/rust/backend/rust-compile.cc index a2f524725a66..d0e0c6662b84 100644 --- a/gcc/rust/backend/rust-compile.cc +++ b/gcc/rust/backend/rust-compile.cc @@ -416,10 +416,11 @@ HIRCompileBase::compile_function_body ( // dead code elimination should remove any bad trailing expressions Bexpression *compiled_expr = CompileExpr::Compile (function_body->expr.get (), ctx); - rust_assert (compiled_expr != nullptr); if (has_return_type) { + rust_assert (compiled_expr != nullptr); + std::vector retstmts; retstmts.push_back (compiled_expr); @@ -428,7 +429,7 @@ HIRCompileBase::compile_function_body ( function_body->get_final_expr ()->get_locus_slow ()); ctx->add_statement (ret); } - else + else if (compiled_expr) { Bstatement *final_stmt = ctx->get_backend ()->expression_statement (fndecl, compiled_expr); diff --git a/gcc/rust/hir/rust-ast-lower-stmt.h b/gcc/rust/hir/rust-ast-lower-stmt.h index e15714c7218d..2d3f59d8cf71 100644 --- a/gcc/rust/hir/rust-ast-lower-stmt.h +++ b/gcc/rust/hir/rust-ast-lower-stmt.h @@ -59,7 +59,8 @@ class ASTLoweringStmt : public ASTLoweringBase translated = new HIR::ExprStmtWithBlock (mapping, std::unique_ptr (expr), - stmt.get_locus ()); + stmt.get_locus (), + !stmt.is_semicolon_followed ()); mappings->insert_location (crate_num, mapping.get_hirid (), stmt.get_locus ()); mappings->insert_hir_stmt (crate_num, mapping.get_hirid (), translated); diff --git a/gcc/rust/hir/tree/rust-hir-expr.h b/gcc/rust/hir/tree/rust-hir-expr.h index 1748c6ae7c8c..26f36c4d12a3 100644 --- a/gcc/rust/hir/tree/rust-hir-expr.h +++ b/gcc/rust/hir/tree/rust-hir-expr.h @@ -2498,7 +2498,7 @@ class BlockExpr : public ExprWithBlock std::vector inner_attrs; std::vector > statements; - std::unique_ptr expr; // inlined from Statements + std::unique_ptr expr; // inlined from Statements bool tail_reachable; Location locus; @@ -2515,7 +2515,7 @@ class BlockExpr : public ExprWithBlock BlockExpr (Analysis::NodeMapping mappings, std::vector > block_statements, - std::unique_ptr block_expr, bool tail_reachable, + std::unique_ptr block_expr, bool tail_reachable, std::vector inner_attribs, std::vector outer_attribs, Location locus) : ExprWithBlock (std::move (mappings), std::move (outer_attribs)), @@ -2531,7 +2531,7 @@ class BlockExpr : public ExprWithBlock { // guard to protect from null pointer dereference if (other.expr != nullptr) - expr = other.expr->clone_expr_without_block (); + expr = other.expr->clone_expr (); statements.reserve (other.statements.size ()); for (const auto &e : other.statements) @@ -2543,7 +2543,7 @@ class BlockExpr : public ExprWithBlock { ExprWithBlock::operator= (other); // statements = other.statements; - expr = other.expr->clone_expr_without_block (); + expr = other.expr->clone_expr (); inner_attrs = other.inner_attrs; locus = other.locus; // outer_attrs = other.outer_attrs; @@ -2589,7 +2589,7 @@ class BlockExpr : public ExprWithBlock return statements[statements.size () - 1]->get_locus_slow (); } - std::unique_ptr &get_final_expr () { return expr; } + std::unique_ptr &get_final_expr () { return expr; } std::vector > &get_statements () { return statements; } diff --git a/gcc/rust/hir/tree/rust-hir-stmt.h b/gcc/rust/hir/tree/rust-hir-stmt.h index 3384aaac9ac0..e34fa7e8d99d 100644 --- a/gcc/rust/hir/tree/rust-hir-stmt.h +++ b/gcc/rust/hir/tree/rust-hir-stmt.h @@ -193,13 +193,16 @@ class ExprStmtWithoutBlock : public ExprStmt class ExprStmtWithBlock : public ExprStmt { std::unique_ptr expr; + bool must_be_unit; public: std::string as_string () const override; ExprStmtWithBlock (Analysis::NodeMapping mappings, - std::unique_ptr expr, Location locus) - : ExprStmt (std::move (mappings), locus), expr (std::move (expr)) + std::unique_ptr expr, Location locus, + bool must_be_unit) + : ExprStmt (std::move (mappings), locus), expr (std::move (expr)), + must_be_unit (must_be_unit) {} // Copy constructor with clone @@ -224,6 +227,8 @@ class ExprStmtWithBlock : public ExprStmt ExprWithBlock *get_expr () { return expr.get (); } + bool is_unit_check_needed () const override { return must_be_unit; } + protected: /* Use covariance to implement clone function as returning this object rather * than base */ diff --git a/gcc/rust/hir/tree/rust-hir.h b/gcc/rust/hir/tree/rust-hir.h index 51986353d7a1..9bda63a7ddcb 100644 --- a/gcc/rust/hir/tree/rust-hir.h +++ b/gcc/rust/hir/tree/rust-hir.h @@ -723,6 +723,8 @@ class Stmt * methods. */ virtual Location get_locus_slow () const { return Location (); } + virtual bool is_unit_check_needed () const { return false; } + const Analysis::NodeMapping &get_mappings () const { return mappings; } protected: diff --git a/gcc/rust/parse/rust-parse-impl.h b/gcc/rust/parse/rust-parse-impl.h index 96971697896e..6d38ace7825c 100644 --- a/gcc/rust/parse/rust-parse-impl.h +++ b/gcc/rust/parse/rust-parse-impl.h @@ -7022,11 +7022,9 @@ Parser::parse_expr_stmt ( } } -/* Parses a expression statement containing an expression with block. - * Disambiguates internally. */ template -std::unique_ptr -Parser::parse_expr_stmt_with_block ( +std::unique_ptr +Parser::parse_expr_with_block ( std::vector outer_attrs) { std::unique_ptr expr_parsed = nullptr; @@ -7113,9 +7111,23 @@ Parser::parse_expr_stmt_with_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 ( + std::vector 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::ExprStmtWithBlock (std::move (expr_parsed), t->get_locus ())); + new AST::ExprStmtWithBlock (std::move (expr_parsed), locus, + lexer.peek_token ()->get_id () == SEMICOLON)); } /* Parses an expression statement containing an expression without block. @@ -7286,7 +7298,7 @@ Parser::parse_block_expr ( // parse statements and expression std::vector> stmts; - std::unique_ptr expr = nullptr; + std::unique_ptr expr = nullptr; const_TokenPtr t = lexer.peek_token (); while (t->get_id () != RIGHT_CURLY) @@ -11438,6 +11450,29 @@ Parser::parse_struct_pattern_field_partial ( } } +template +ExprOrStmt +Parser::parse_stmt_or_expr_with_block ( + std::vector outer_attrs) +{ + auto expr = parse_expr_with_block (std::move (outer_attrs)); + 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_slow (); + std::unique_ptr stmt ( + new AST::ExprStmtWithBlock (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. */ @@ -11508,9 +11543,7 @@ Parser::parse_stmt_or_expr_without_block () { case LEFT_CURLY: { // unsafe block - std::unique_ptr stmt ( - parse_expr_stmt_with_block (std::move (outer_attrs))); - return ExprOrStmt (std::move (stmt)); + return parse_stmt_or_expr_with_block (std::move (outer_attrs)); } case TRAIT: { // unsafe trait @@ -11577,11 +11610,7 @@ Parser::parse_stmt_or_expr_without_block () case MATCH_TOK: case LEFT_CURLY: case ASYNC: { - // all expressions with block, so cannot be final expr without block in - // function - std::unique_ptr stmt ( - parse_expr_stmt_with_block (std::move (outer_attrs))); - return ExprOrStmt (std::move (stmt)); + return parse_stmt_or_expr_with_block (std::move (outer_attrs)); } case LIFETIME: { /* FIXME: are there any expressions without blocks that can have @@ -11592,9 +11621,7 @@ Parser::parse_stmt_or_expr_without_block () && (t2->get_id () == LOOP || t2->get_id () == WHILE || t2->get_id () == FOR)) { - std::unique_ptr stmt ( - parse_expr_stmt_with_block (std::move (outer_attrs))); - return ExprOrStmt (std::move (stmt)); + return parse_stmt_or_expr_with_block (std::move (outer_attrs)); } else { diff --git a/gcc/rust/parse/rust-parse.h b/gcc/rust/parse/rust-parse.h index f6faa962fce4..47f55ea65cb8 100644 --- a/gcc/rust/parse/rust-parse.h +++ b/gcc/rust/parse/rust-parse.h @@ -27,7 +27,7 @@ namespace Rust { * probably take up the same amount of space. */ struct ExprOrStmt { - std::unique_ptr expr; + std::unique_ptr expr; std::unique_ptr stmt; /* I was going to resist the urge to make this a real class and make it POD, @@ -35,9 +35,7 @@ struct ExprOrStmt * constructor. */ // expression constructor - ExprOrStmt (std::unique_ptr expr) - : expr (std::move (expr)) - {} + ExprOrStmt (std::unique_ptr expr) : expr (std::move (expr)) {} // statement constructor ExprOrStmt (std::unique_ptr stmt) : stmt (std::move (stmt)) {} @@ -63,9 +61,7 @@ struct ExprOrStmt private: // private constructor only used for creating error state expr or stmt objects - ExprOrStmt (AST::ExprWithoutBlock *expr, AST::Stmt *stmt) - : expr (expr), stmt (stmt) - {} + ExprOrStmt (AST::Expr *expr, AST::Stmt *stmt) : expr (expr), stmt (stmt) {} // make this work: have a disambiguation specifically for known statements // (i.e. ';' and 'let'). then, have a special "parse expr or stmt" function @@ -487,6 +483,8 @@ template class Parser ParseRestrictions restrictions = ParseRestrictions ()); // Expression-related (non-Pratt parsed) + std::unique_ptr + parse_expr_with_block (std::vector outer_attrs); std::unique_ptr parse_expr_without_block (std::vector outer_attrs = std::vector ()); @@ -592,6 +590,8 @@ template class Parser parse_expr_stmt_without_block (std::vector outer_attrs); ExprOrStmt parse_stmt_or_expr_without_block (); ExprOrStmt + parse_stmt_or_expr_with_block (std::vector outer_attrs); + ExprOrStmt parse_macro_invocation_maybe_semi (std::vector outer_attrs); ExprOrStmt parse_path_based_stmt_or_expr (std::vector outer_attrs); diff --git a/gcc/rust/typecheck/rust-hir-type-check.cc b/gcc/rust/typecheck/rust-hir-type-check.cc index c8394c8dcbe5..f1503bec99ec 100644 --- a/gcc/rust/typecheck/rust-hir-type-check.cc +++ b/gcc/rust/typecheck/rust-hir-type-check.cc @@ -143,10 +143,10 @@ TypeCheckExpr::visit (HIR::BlockExpr &expr) delete block_tyty; block_tyty = resolved; } - else if (!resolved->is_unit ()) + else if (s->is_unit_check_needed () && !resolved->is_unit ()) { - rust_error_at (s->get_locus_slow (), "expected () got %s", - resolved->as_string ().c_str ()); + auto unit = new TyTy::TupleType (s->get_mappings ().get_hirid ()); + resolved = unit->unify (resolved); } return true; diff --git a/gcc/testsuite/rust.test/compile/unused.rs b/gcc/testsuite/rust.test/compile/unused.rs index a4987b5a3b19..0564aa1854ba 100644 --- a/gcc/testsuite/rust.test/compile/unused.rs +++ b/gcc/testsuite/rust.test/compile/unused.rs @@ -14,4 +14,4 @@ fn f() { fn main() { f(); -} \ No newline at end of file +} diff --git a/gcc/testsuite/rust.test/xfail_compile/implicit_returns_err1.rs b/gcc/testsuite/rust.test/xfail_compile/implicit_returns_err1.rs index 37eb562597fc..973ba80fb866 100644 --- a/gcc/testsuite/rust.test/xfail_compile/implicit_returns_err1.rs +++ b/gcc/testsuite/rust.test/xfail_compile/implicit_returns_err1.rs @@ -1,7 +1,5 @@ -// { dg-error "expected .* got .*" "" { target { *-*-* } } 0 } - fn test(x: i32) -> i32 { - if x > 1 { + if x > 1 { // { dg-error "expected .... got .." } 1 } else { 2 diff --git a/gcc/testsuite/rust.test/xfail_compile/implicit_returns_err3.rs b/gcc/testsuite/rust.test/xfail_compile/implicit_returns_err3.rs index 2a64fcf9f485..37b1c62414c5 100644 --- a/gcc/testsuite/rust.test/xfail_compile/implicit_returns_err3.rs +++ b/gcc/testsuite/rust.test/xfail_compile/implicit_returns_err3.rs @@ -1,5 +1,4 @@ -// { dg-error "expected .* got .*" "" { target { *-*-* } } 0 } -fn test(x: i32) -> i32 { +fn test(x: i32) -> i32 { // { dg-error "expected .i32. got ...." } if x > 1 { 1 } diff --git a/gcc/testsuite/rust.test/xfail_compile/stmt_with_block_err1.rs b/gcc/testsuite/rust.test/xfail_compile/stmt_with_block_err1.rs new file mode 100644 index 000000000000..8780d0feeac6 --- /dev/null +++ b/gcc/testsuite/rust.test/xfail_compile/stmt_with_block_err1.rs @@ -0,0 +1,17 @@ +fn test(x: i32) -> i32 { + if x > 1 { // { dg-error "expected .... got .." } + 1 + } else { + 2 + } + + { // { dg-error "expected .... got .." } + 3 + } + + 3 +} + +fn main() { + let a = test(0); +} From 3aeb9f47187f9b7ad55e32bcd273556823f383c6 Mon Sep 17 00:00:00 2001 From: lrh2000 Date: Sat, 10 Apr 2021 22:53:32 +0800 Subject: [PATCH 2/2] Introduce limited support for the never type --- gcc/rust/backend/rust-compile-context.h | 5 +++ gcc/rust/backend/rust-compile-tyty.h | 5 +++ gcc/rust/backend/rust-compile.cc | 8 ++-- gcc/rust/hir/rust-ast-lower.cc | 2 +- gcc/rust/hir/tree/rust-hir-expr.h | 2 +- gcc/rust/typecheck/rust-hir-type-check-expr.h | 38 ++++++++++++------ gcc/rust/typecheck/rust-hir-type-check-item.h | 3 +- gcc/rust/typecheck/rust-hir-type-check.cc | 29 ++++---------- gcc/rust/typecheck/rust-substitution-mapper.h | 4 ++ gcc/rust/typecheck/rust-tycheck-dump.h | 2 +- gcc/rust/typecheck/rust-tyty-call.h | 2 + gcc/rust/typecheck/rust-tyty-cmp.h | 17 ++++++++ gcc/rust/typecheck/rust-tyty-rules.h | 23 +++++++++++ gcc/rust/typecheck/rust-tyty-visitor.h | 1 + gcc/rust/typecheck/rust-tyty.cc | 32 +++++++++++++++ gcc/rust/typecheck/rust-tyty.h | 39 ++++++++++++++++++ .../rust.test/compile/block_expr5.rs | 40 +++++++++++++++++++ gcc/testsuite/rust.test/compile/func1.rs | 7 ++++ gcc/testsuite/rust.test/compile/func2.rs | 20 ++++++++++ .../rust.test/compile/never_type1.rs | 22 ++++++++++ .../rust.test/compile/stmt_with_block1.rs | 13 ++++++ .../rust.test/xfail_compile/break1.rs | 5 +-- .../rust.test/xfail_compile/func1.rs | 2 +- .../rust.test/xfail_compile/func4.rs | 6 +++ .../rust.test/xfail_compile/func5.rs | 7 ++++ .../xfail_compile/implicit_returns_err2.rs | 3 +- .../xfail_compile/implicit_returns_err4.rs | 10 +++++ .../xfail_compile/never_type_err1.rs | 14 +++++++ .../xfail_compile/never_type_err2.rs | 4 ++ 29 files changed, 316 insertions(+), 49 deletions(-) create mode 100644 gcc/testsuite/rust.test/compile/block_expr5.rs create mode 100644 gcc/testsuite/rust.test/compile/func1.rs create mode 100644 gcc/testsuite/rust.test/compile/func2.rs create mode 100644 gcc/testsuite/rust.test/compile/never_type1.rs create mode 100644 gcc/testsuite/rust.test/compile/stmt_with_block1.rs create mode 100644 gcc/testsuite/rust.test/xfail_compile/func4.rs create mode 100644 gcc/testsuite/rust.test/xfail_compile/func5.rs create mode 100644 gcc/testsuite/rust.test/xfail_compile/implicit_returns_err4.rs create mode 100644 gcc/testsuite/rust.test/xfail_compile/never_type_err1.rs create mode 100644 gcc/testsuite/rust.test/xfail_compile/never_type_err2.rs diff --git a/gcc/rust/backend/rust-compile-context.h b/gcc/rust/backend/rust-compile-context.h index 886657568ae7..2024a6f91124 100644 --- a/gcc/rust/backend/rust-compile-context.h +++ b/gcc/rust/backend/rust-compile-context.h @@ -503,6 +503,11 @@ class TyTyResolveCompile : public TyTy::TyVisitor translated = compiled_type; } + void visit (TyTy::NeverType &) override + { + translated = ctx->get_backend ()->void_type (); + } + private: TyTyResolveCompile (Context *ctx) : ctx (ctx), translated (nullptr) {} diff --git a/gcc/rust/backend/rust-compile-tyty.h b/gcc/rust/backend/rust-compile-tyty.h index 28db28971847..ba98ac014529 100644 --- a/gcc/rust/backend/rust-compile-tyty.h +++ b/gcc/rust/backend/rust-compile-tyty.h @@ -222,6 +222,11 @@ class TyTyCompile : public TyTy::TyVisitor = backend->named_type ("str", raw_str, Linemap::predeclared_location ()); } + void visit (TyTy::NeverType &) override + { + translated = backend->void_type (); + } + private: TyTyCompile (::Backend *backend) : backend (backend), translated (nullptr), diff --git a/gcc/rust/backend/rust-compile.cc b/gcc/rust/backend/rust-compile.cc index d0e0c6662b84..9375dd021f45 100644 --- a/gcc/rust/backend/rust-compile.cc +++ b/gcc/rust/backend/rust-compile.cc @@ -256,7 +256,7 @@ CompileBlock::visit (HIR::BlockExpr &expr) } } - if (expr.has_expr () && expr.tail_expr_reachable ()) + if (expr.has_expr ()) { // the previous passes will ensure this is a valid return // dead code elimination should remove any bad trailing expressions @@ -410,17 +410,15 @@ HIRCompileBase::compile_function_body ( } } - if (function_body->has_expr () && function_body->tail_expr_reachable ()) + if (function_body->has_expr ()) { // the previous passes will ensure this is a valid return // dead code elimination should remove any bad trailing expressions Bexpression *compiled_expr = CompileExpr::Compile (function_body->expr.get (), ctx); - if (has_return_type) + if (has_return_type && compiled_expr) { - rust_assert (compiled_expr != nullptr); - std::vector retstmts; retstmts.push_back (compiled_expr); diff --git a/gcc/rust/hir/rust-ast-lower.cc b/gcc/rust/hir/rust-ast-lower.cc index 0bacdd216d7b..86b10d5b39c9 100644 --- a/gcc/rust/hir/rust-ast-lower.cc +++ b/gcc/rust/hir/rust-ast-lower.cc @@ -90,7 +90,7 @@ ASTLoweringBlock::visit (AST::BlockExpr &expr) return true; }); - bool tail_reachable = expr.has_tail_expr () && !block_did_terminate; + bool tail_reachable = !block_did_terminate; if (expr.has_tail_expr () && block_did_terminate) { // warning unreachable tail expressions diff --git a/gcc/rust/hir/tree/rust-hir-expr.h b/gcc/rust/hir/tree/rust-hir-expr.h index 26f36c4d12a3..498c8dece1e8 100644 --- a/gcc/rust/hir/tree/rust-hir-expr.h +++ b/gcc/rust/hir/tree/rust-hir-expr.h @@ -2511,7 +2511,7 @@ class BlockExpr : public ExprWithBlock // Returns whether the block contains an expression bool has_expr () const { return expr != nullptr; } - bool tail_expr_reachable () const { return tail_reachable; } + bool is_tail_reachable () const { return tail_reachable; } BlockExpr (Analysis::NodeMapping mappings, std::vector > block_statements, diff --git a/gcc/rust/typecheck/rust-hir-type-check-expr.h b/gcc/rust/typecheck/rust-hir-type-check-expr.h index 6b6eed07df89..c18ad2a90dcf 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-expr.h +++ b/gcc/rust/typecheck/rust-hir-type-check-expr.h @@ -145,16 +145,15 @@ class TypeCheckExpr : public TypeCheckBase void visit (HIR::ReturnExpr &expr) override { - if (!expr.has_return_expr ()) - { - infered = new TyTy::TupleType (expr.get_mappings ().get_hirid ()); - return; - } - auto fn_return_tyty = context->peek_return_type (); rust_assert (fn_return_tyty != nullptr); - auto expr_ty = TypeCheckExpr::Resolve (expr.get_expr (), false); + TyTy::BaseType *expr_ty; + if (expr.has_return_expr ()) + expr_ty = TypeCheckExpr::Resolve (expr.get_expr (), false); + else + expr_ty = new TyTy::TupleType (expr.get_mappings ().get_hirid ()); + if (expr_ty == nullptr) { rust_error_at (expr.get_locus (), @@ -166,6 +165,8 @@ class TypeCheckExpr : public TypeCheckBase fn_return_tyty->append_reference (expr_ty->get_ref ()); for (auto &ref : infered->get_combined_refs ()) fn_return_tyty->append_reference (ref); + + infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ()); } void visit (HIR::CallExpr &expr) override @@ -622,17 +623,28 @@ class TypeCheckExpr : public TypeCheckBase auto else_blk_resolved = TypeCheckExpr::Resolve (expr.get_else_block (), inside_loop); - infered = if_blk_resolved->unify (else_blk_resolved); + if (if_blk_resolved->get_kind () == TyTy::NEVER) + infered = else_blk_resolved; + else if (else_blk_resolved->get_kind () == TyTy::NEVER) + infered = if_blk_resolved; + else + infered = if_blk_resolved->unify (else_blk_resolved); } void visit (HIR::IfExprConseqIf &expr) override { TypeCheckExpr::Resolve (expr.get_if_condition (), false); - auto if_blk = TypeCheckExpr::Resolve (expr.get_if_block (), inside_loop); - auto else_blk + auto if_blk_resolved + = TypeCheckExpr::Resolve (expr.get_if_block (), inside_loop); + auto else_blk_resolved = TypeCheckExpr::Resolve (expr.get_conseq_if_expr (), inside_loop); - infered = if_blk->unify (else_blk); + if (if_blk_resolved->get_kind () == TyTy::NEVER) + infered = else_blk_resolved; + else if (else_blk_resolved->get_kind () == TyTy::NEVER) + infered = if_blk_resolved; + else + infered = if_blk_resolved->unify (else_blk_resolved); } void visit (HIR::BlockExpr &expr) override; @@ -929,7 +941,7 @@ class TypeCheckExpr : public TypeCheckBase context->swap_head_loop_context (unified_ty); } - infered = new TyTy::TupleType (expr.get_mappings ().get_hirid ()); + infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ()); } void visit (HIR::ContinueExpr &expr) override @@ -941,7 +953,7 @@ class TypeCheckExpr : public TypeCheckBase return; } - infered = new TyTy::TupleType (expr.get_mappings ().get_hirid ()); + infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ()); } void visit (HIR::BorrowExpr &expr) override diff --git a/gcc/rust/typecheck/rust-hir-type-check-item.h b/gcc/rust/typecheck/rust-hir-type-check-item.h index 44fe94346a9c..1205dceceee4 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-item.h +++ b/gcc/rust/typecheck/rust-hir-type-check-item.h @@ -82,7 +82,8 @@ class TypeCheckItem : public TypeCheckBase context->pop_return_type (); - expected_ret_tyty->unify (block_expr_ty); + if (block_expr_ty->get_kind () != TyTy::NEVER) + expected_ret_tyty->unify (block_expr_ty); } private: diff --git a/gcc/rust/typecheck/rust-hir-type-check.cc b/gcc/rust/typecheck/rust-hir-type-check.cc index f1503bec99ec..aece1884bc7f 100644 --- a/gcc/rust/typecheck/rust-hir-type-check.cc +++ b/gcc/rust/typecheck/rust-hir-type-check.cc @@ -123,14 +123,7 @@ TypeResolution::Resolve (HIR::Crate &crate) void TypeCheckExpr::visit (HIR::BlockExpr &expr) { - TyTy::BaseType *block_tyty - = new TyTy::TupleType (expr.get_mappings ().get_hirid ()); - expr.iterate_stmts ([&] (HIR::Stmt *s) mutable -> bool { - bool is_final_stmt = expr.is_final_stmt (s); - bool has_final_expr = expr.has_expr () && expr.tail_expr_reachable (); - bool stmt_is_final_expr = is_final_stmt && !has_final_expr; - auto resolved = TypeCheckStmt::Resolve (s, inside_loop); if (resolved == nullptr) { @@ -138,12 +131,7 @@ TypeCheckExpr::visit (HIR::BlockExpr &expr) return false; } - if (stmt_is_final_expr) - { - delete block_tyty; - block_tyty = resolved; - } - else if (s->is_unit_check_needed () && !resolved->is_unit ()) + if (s->is_unit_check_needed () && !resolved->is_unit ()) { auto unit = new TyTy::TupleType (s->get_mappings ().get_hirid ()); resolved = unit->unify (resolved); @@ -153,14 +141,13 @@ TypeCheckExpr::visit (HIR::BlockExpr &expr) }); if (expr.has_expr ()) - { - delete block_tyty; - - block_tyty - = TypeCheckExpr::Resolve (expr.get_final_expr ().get (), inside_loop); - } - - infered = block_tyty->clone (); + infered + = TypeCheckExpr::Resolve (expr.get_final_expr ().get (), inside_loop) + ->clone (); + else if (expr.is_tail_reachable ()) + infered = new TyTy::TupleType (expr.get_mappings ().get_hirid ()); + else + infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ()); } // RUST_HIR_TYPE_CHECK_STRUCT_FIELD diff --git a/gcc/rust/typecheck/rust-substitution-mapper.h b/gcc/rust/typecheck/rust-substitution-mapper.h index bc20e4b3011c..4cd06bb0b18a 100644 --- a/gcc/rust/typecheck/rust-substitution-mapper.h +++ b/gcc/rust/typecheck/rust-substitution-mapper.h @@ -99,6 +99,7 @@ class SubstMapper : public TyTy::TyVisitor void visit (TyTy::ReferenceType &) override { gcc_unreachable (); } void visit (TyTy::ParamType &) override { gcc_unreachable (); } void visit (TyTy::StrType &) override { gcc_unreachable (); } + void visit (TyTy::NeverType &) override { gcc_unreachable (); } private: SubstMapper (HirId ref, HIR::GenericArgs *generics, Location locus) @@ -157,6 +158,7 @@ class SubstMapperInternal : public TyTy::TyVisitor void visit (TyTy::ReferenceType &) override { gcc_unreachable (); } void visit (TyTy::ParamType &) override { gcc_unreachable (); } void visit (TyTy::StrType &) override { gcc_unreachable (); } + void visit (TyTy::NeverType &) override { gcc_unreachable (); } private: SubstMapperInternal (HirId ref, TyTy::SubstitutionArgumentMappings &mappings) @@ -211,6 +213,7 @@ class SubstMapperFromExisting : public TyTy::TyVisitor void visit (TyTy::ReferenceType &) override { gcc_unreachable (); } void visit (TyTy::ParamType &) override { gcc_unreachable (); } void visit (TyTy::StrType &) override { gcc_unreachable (); } + void visit (TyTy::NeverType &) override { gcc_unreachable (); } private: SubstMapperFromExisting (TyTy::BaseType *concrete, TyTy::BaseType *receiver) @@ -258,6 +261,7 @@ class GetUsedSubstArgs : public TyTy::TyVisitor void visit (TyTy::ReferenceType &) override { gcc_unreachable (); } void visit (TyTy::ParamType &) override { gcc_unreachable (); } void visit (TyTy::StrType &) override { gcc_unreachable (); } + void visit (TyTy::NeverType &) override { gcc_unreachable (); } private: GetUsedSubstArgs () : args (TyTy::SubstitutionArgumentMappings::error ()) {} diff --git a/gcc/rust/typecheck/rust-tycheck-dump.h b/gcc/rust/typecheck/rust-tycheck-dump.h index 953770e3621a..2ab8abbdfcb1 100644 --- a/gcc/rust/typecheck/rust-tycheck-dump.h +++ b/gcc/rust/typecheck/rust-tycheck-dump.h @@ -107,7 +107,7 @@ class TypeResolverDump : public TypeCheckBase return true; }); - if (expr.has_expr () && expr.tail_expr_reachable ()) + if (expr.has_expr ()) { dump += indent (); expr.expr->accept_vis (*this); diff --git a/gcc/rust/typecheck/rust-tyty-call.h b/gcc/rust/typecheck/rust-tyty-call.h index 4171da5e94e0..2aba29803d94 100644 --- a/gcc/rust/typecheck/rust-tyty-call.h +++ b/gcc/rust/typecheck/rust-tyty-call.h @@ -53,6 +53,7 @@ class TypeCheckCallExpr : private TyVisitor void visit (ReferenceType &type) override { gcc_unreachable (); } void visit (ParamType &) override { gcc_unreachable (); } void visit (StrType &) override { gcc_unreachable (); } + void visit (NeverType &) override { gcc_unreachable (); } // tuple-structs void visit (ADTType &type) override; @@ -100,6 +101,7 @@ class TypeCheckMethodCallExpr : private TyVisitor void visit (ReferenceType &type) override { gcc_unreachable (); } void visit (ParamType &) override { gcc_unreachable (); } void visit (StrType &) override { gcc_unreachable (); } + void visit (NeverType &) override { gcc_unreachable (); } // FIXME void visit (FnPtr &type) override { gcc_unreachable (); } diff --git a/gcc/rust/typecheck/rust-tyty-cmp.h b/gcc/rust/typecheck/rust-tyty-cmp.h index b195e5ccbeda..1641264fe98a 100644 --- a/gcc/rust/typecheck/rust-tyty-cmp.h +++ b/gcc/rust/typecheck/rust-tyty-cmp.h @@ -84,6 +84,8 @@ class BaseCmp : public TyVisitor virtual void visit (StrType &) override { ok = false; } + virtual void visit (NeverType &) override { ok = false; } + protected: BaseCmp (BaseType *base) : mappings (Analysis::Mappings::get ()), @@ -815,6 +817,21 @@ class StrCmp : public BaseCmp StrType *base; }; +class NeverCmp : public BaseCmp +{ + using Rust::TyTy::BaseCmp::visit; + +public: + NeverCmp (NeverType *base) : BaseCmp (base), base (base) {} + + void visit (NeverType &type) override { ok = true; } + +private: + BaseType *get_base () override { return base; } + + NeverType *base; +}; + } // namespace TyTy } // namespace Rust diff --git a/gcc/rust/typecheck/rust-tyty-rules.h b/gcc/rust/typecheck/rust-tyty-rules.h index 03fe0d85abca..6f7e66340acf 100644 --- a/gcc/rust/typecheck/rust-tyty-rules.h +++ b/gcc/rust/typecheck/rust-tyty-rules.h @@ -293,6 +293,14 @@ class BaseRules : public TyVisitor type.as_string ().c_str ()); } + virtual void visit (NeverType &type) override + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + rust_error_at (ref_locus, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + protected: BaseRules (BaseType *base) : mappings (Analysis::Mappings::get ()), @@ -1138,6 +1146,21 @@ class StrRules : public BaseRules StrType *base; }; +class NeverRules : public BaseRules +{ + using Rust::TyTy::BaseRules::visit; + +public: + NeverRules (NeverType *base) : BaseRules (base), base (base) {} + + virtual void visit (NeverType &type) override { resolved = type.clone (); } + +private: + BaseType *get_base () override { return base; } + + NeverType *base; +}; + } // namespace TyTy } // namespace Rust diff --git a/gcc/rust/typecheck/rust-tyty-visitor.h b/gcc/rust/typecheck/rust-tyty-visitor.h index 453a3b6bc864..0ed7eef35a65 100644 --- a/gcc/rust/typecheck/rust-tyty-visitor.h +++ b/gcc/rust/typecheck/rust-tyty-visitor.h @@ -44,6 +44,7 @@ class TyVisitor virtual void visit (ReferenceType &type) = 0; virtual void visit (ParamType &type) = 0; virtual void visit (StrType &type) = 0; + virtual void visit (NeverType &type) = 0; }; } // namespace TyTy diff --git a/gcc/rust/typecheck/rust-tyty.cc b/gcc/rust/typecheck/rust-tyty.cc index 8378cf262f18..86b1da08a594 100644 --- a/gcc/rust/typecheck/rust-tyty.cc +++ b/gcc/rust/typecheck/rust-tyty.cc @@ -1307,6 +1307,38 @@ StrType::is_equal (const BaseType &other) const return get_kind () == other.get_kind (); } +void +NeverType::accept_vis (TyVisitor &vis) +{ + vis.visit (*this); +} + +std::string +NeverType::as_string () const +{ + return "!"; +} + +BaseType * +NeverType::unify (BaseType *other) +{ + NeverRules r (this); + return r.unify (other); +} + +bool +NeverType::can_eq (BaseType *other) +{ + NeverCmp r (this); + return r.can_eq (other); +} + +BaseType * +NeverType::clone () +{ + return new NeverType (get_ref (), get_ty_ref (), get_combined_refs ()); +} + // rust-tyty-call.h void diff --git a/gcc/rust/typecheck/rust-tyty.h b/gcc/rust/typecheck/rust-tyty.h index 2f343c12c7a5..2b9f304f6828 100644 --- a/gcc/rust/typecheck/rust-tyty.h +++ b/gcc/rust/typecheck/rust-tyty.h @@ -45,6 +45,7 @@ enum TypeKind FLOAT, USIZE, ISIZE, + NEVER, // there are more to add... ERROR }; @@ -1226,6 +1227,41 @@ class StrType : public BaseType BaseType *clone () final override; }; +// https://doc.rust-lang.org/std/primitive.never.html +// +// Since the `!` type is really complicated and it is even still unstable +// in rustc, only fairly limited support for this type is introduced here. +// Unification between `!` and ANY other type (including ``) is simply +// not allowed. If it is needed, it should be handled manually. For example, +// unifying `!` with other types is very necessary when resolving types of +// `if/else` expressions. +// +// See related discussion at https://github.com/Rust-GCC/gccrs/pull/364 +class NeverType : public BaseType +{ +public: + NeverType (HirId ref, std::set refs = std::set ()) + : BaseType (ref, ref, TypeKind::NEVER, refs) + {} + + NeverType (HirId ref, HirId ty_ref, std::set refs = std::set ()) + : BaseType (ref, ty_ref, TypeKind::NEVER, refs) + {} + + void accept_vis (TyVisitor &vis) override; + + std::string as_string () const override; + + BaseType *unify (BaseType *other) override; + bool can_eq (BaseType *other) override; + + BaseType *clone () final override; + + std::string get_name () const override final { return as_string (); } + + bool is_unit () const override { return true; } +}; + class TypeKindFormat { public: @@ -1281,6 +1317,9 @@ class TypeKindFormat case TypeKind::ISIZE: return "Isize"; + case TypeKind::NEVER: + return "Never"; + case TypeKind::ERROR: return "ERROR"; } diff --git a/gcc/testsuite/rust.test/compile/block_expr5.rs b/gcc/testsuite/rust.test/compile/block_expr5.rs new file mode 100644 index 000000000000..7e164a949bbf --- /dev/null +++ b/gcc/testsuite/rust.test/compile/block_expr5.rs @@ -0,0 +1,40 @@ +fn foo() -> i32 { + 0 +} + +fn bar() -> i32 { + foo(); + foo() +} + +fn baz() -> i32 { + { + bar(); + bar(); + } + { + bar(); + bar() + }; + { + bar(); + bar() + } +} + +fn test(ok: i32) -> i32 { + if ok >= 1 { + foo() + } else if ok <= -1 { + bar() + } else { + baz() + } +} + +fn main() { + let a = foo(); + let b = bar(); + let c = baz(); + test(a + b + c); +} diff --git a/gcc/testsuite/rust.test/compile/func1.rs b/gcc/testsuite/rust.test/compile/func1.rs new file mode 100644 index 000000000000..df1789e7bbe9 --- /dev/null +++ b/gcc/testsuite/rust.test/compile/func1.rs @@ -0,0 +1,7 @@ +fn not_void() -> i32 { + 8 +} + +fn main() { + not_void(); +} diff --git a/gcc/testsuite/rust.test/compile/func2.rs b/gcc/testsuite/rust.test/compile/func2.rs new file mode 100644 index 000000000000..f7dd556d955b --- /dev/null +++ b/gcc/testsuite/rust.test/compile/func2.rs @@ -0,0 +1,20 @@ +fn foo() { + 8; + 8; +} + +fn bar() -> i32 { + 8; + 8 +} + +fn baz() -> i32 { + 8; + return 8; +} + +fn main() { + let a = foo(); // { dg-warning "unused name" } + let b = bar(); // { dg-warning "unused name" } + let c = baz(); // { dg-warning "unused name" } +} diff --git a/gcc/testsuite/rust.test/compile/never_type1.rs b/gcc/testsuite/rust.test/compile/never_type1.rs new file mode 100644 index 000000000000..0f15029097da --- /dev/null +++ b/gcc/testsuite/rust.test/compile/never_type1.rs @@ -0,0 +1,22 @@ +fn foo() -> i32 { + let c; + let d; + + c = if false { + return 1; + } else { + 0.0 + }; + + d = if true { + 0.0 + } else { + return 1; + }; + + 0 +} + +fn main() { + foo(); +} diff --git a/gcc/testsuite/rust.test/compile/stmt_with_block1.rs b/gcc/testsuite/rust.test/compile/stmt_with_block1.rs new file mode 100644 index 000000000000..b6aa56cc2e67 --- /dev/null +++ b/gcc/testsuite/rust.test/compile/stmt_with_block1.rs @@ -0,0 +1,13 @@ +fn test(x: i32) -> i32 { + if x > 1 { 1 } else { 2 }; + if x > 1 { 1; } else { 2; } + + { 3; } + { 3 }; + + { 3 } +} + +fn main() { + let a = test(0); // { dg-warning "unused name" } +} diff --git a/gcc/testsuite/rust.test/xfail_compile/break1.rs b/gcc/testsuite/rust.test/xfail_compile/break1.rs index be3c9e8ee68b..33053cf5fbca 100644 --- a/gcc/testsuite/rust.test/xfail_compile/break1.rs +++ b/gcc/testsuite/rust.test/xfail_compile/break1.rs @@ -1,7 +1,6 @@ -// { dg-excess-errors "Noisy error and debug" } -fn main() { // { dg-error "expected .... got .." } +fn main() { let a; a = 1; - break a; // { dg-error "cannot `break` outside of a loop" + break a; // { dg-error "cannot `break` outside of a loop" } // { dg-error "failed to type resolve expression" "" { target { *-*-* } } .-1 } } diff --git a/gcc/testsuite/rust.test/xfail_compile/func1.rs b/gcc/testsuite/rust.test/xfail_compile/func1.rs index 7c1ce52fc86f..6758a3898e30 100644 --- a/gcc/testsuite/rust.test/xfail_compile/func1.rs +++ b/gcc/testsuite/rust.test/xfail_compile/func1.rs @@ -1,4 +1,4 @@ -fn test(x: i32) -> bool { // { dg-error "expected .bool. got ..." } +fn test(x: i32) -> bool { return x + 1; // { dg-error "expected .bool. got .i32." } } diff --git a/gcc/testsuite/rust.test/xfail_compile/func4.rs b/gcc/testsuite/rust.test/xfail_compile/func4.rs new file mode 100644 index 000000000000..3b2d2b0d773a --- /dev/null +++ b/gcc/testsuite/rust.test/xfail_compile/func4.rs @@ -0,0 +1,6 @@ +fn func() -> i32 { // { dg-error "expected .i32. got ...." } +} + +fn main() { + func(); +} diff --git a/gcc/testsuite/rust.test/xfail_compile/func5.rs b/gcc/testsuite/rust.test/xfail_compile/func5.rs new file mode 100644 index 000000000000..05624f524e94 --- /dev/null +++ b/gcc/testsuite/rust.test/xfail_compile/func5.rs @@ -0,0 +1,7 @@ +fn func() -> i32 { + return; // { dg-error "expected .i32. got ...." } +} + +fn main() { + func(); +} diff --git a/gcc/testsuite/rust.test/xfail_compile/implicit_returns_err2.rs b/gcc/testsuite/rust.test/xfail_compile/implicit_returns_err2.rs index 75f4db4db05b..fb90748871f5 100644 --- a/gcc/testsuite/rust.test/xfail_compile/implicit_returns_err2.rs +++ b/gcc/testsuite/rust.test/xfail_compile/implicit_returns_err2.rs @@ -1,6 +1,5 @@ -// { dg-error "expected .* got .*" "" { target { *-*-* } } 0 } - fn test(x: i32) -> i32 { + // { dg-error "expected .i32. got .bool." "" { target *-*-* } .-1 } return 1; // { dg-warning "unreachable expression" "" { target *-*-* } .+1 } true diff --git a/gcc/testsuite/rust.test/xfail_compile/implicit_returns_err4.rs b/gcc/testsuite/rust.test/xfail_compile/implicit_returns_err4.rs new file mode 100644 index 000000000000..59c6a020d4c9 --- /dev/null +++ b/gcc/testsuite/rust.test/xfail_compile/implicit_returns_err4.rs @@ -0,0 +1,10 @@ +fn test(x: bool) -> bool { + // { dg-error "expected .bool. got ...." "" { target *-*-*} .-1 } + return x; + // { dg-warning "unreachable expression" "" { target *-*-* } .+1 } + () +} + +fn main() { + let a = test(true); +} diff --git a/gcc/testsuite/rust.test/xfail_compile/never_type_err1.rs b/gcc/testsuite/rust.test/xfail_compile/never_type_err1.rs new file mode 100644 index 000000000000..52b1283fadfe --- /dev/null +++ b/gcc/testsuite/rust.test/xfail_compile/never_type_err1.rs @@ -0,0 +1,14 @@ +fn test() { + let a; + + // FIXME: Unimplemented features + a = if true { // { dg-error "expected .T.. got .!." } + return; + } else { + return; + }; +} + +fn main() { + test(); +} diff --git a/gcc/testsuite/rust.test/xfail_compile/never_type_err2.rs b/gcc/testsuite/rust.test/xfail_compile/never_type_err2.rs new file mode 100644 index 000000000000..c94cb828071f --- /dev/null +++ b/gcc/testsuite/rust.test/xfail_compile/never_type_err2.rs @@ -0,0 +1,4 @@ +// FIXME: Unimplemented features +fn foo() -> ! { // { dg-error "unresolved type" } + let a: !; // { dg-error "unresolved type" } +}