diff --git a/gcc/rust/ast/rust-ast-builder.cc b/gcc/rust/ast/rust-ast-builder.cc index 58c37a5aa5f7..2e3685f2b198 100644 --- a/gcc/rust/ast/rust-ast-builder.cc +++ b/gcc/rust/ast/rust-ast-builder.cc @@ -17,6 +17,7 @@ // . #include "rust-ast-builder.h" +#include "optional.h" #include "rust-ast-builder-type.h" #include "rust-ast.h" #include "rust-common.h" @@ -352,7 +353,7 @@ Builder::let (std::unique_ptr &&pattern, std::unique_ptr &&type, { return std::unique_ptr (new LetStmt (std::move (pattern), std::move (init), std::move (type), - {}, loc)); + tl::nullopt, {}, loc)); } std::unique_ptr diff --git a/gcc/rust/ast/rust-ast-collector.cc b/gcc/rust/ast/rust-ast-collector.cc index b4d565cadeb5..d0ecedb0c313 100644 --- a/gcc/rust/ast/rust-ast-collector.cc +++ b/gcc/rust/ast/rust-ast-collector.cc @@ -22,6 +22,7 @@ #include "rust-expr.h" #include "rust-item.h" #include "rust-keyword-values.h" +#include "rust-location.h" #include "rust-path.h" #include "rust-system.h" #include "rust-token.h" @@ -2587,6 +2588,13 @@ TokenCollector::visit (LetStmt &stmt) push (Rust::Token::make (EQUAL, UNDEF_LOCATION)); visit (stmt.get_init_expr ()); } + + if (stmt.has_else_expr ()) + { + push (Rust::Token::make (ELSE, UNDEF_LOCATION)); + visit (stmt.get_else_expr ()); + } + push (Rust::Token::make (SEMICOLON, UNDEF_LOCATION)); } diff --git a/gcc/rust/ast/rust-stmt.h b/gcc/rust/ast/rust-stmt.h index afe48f271c19..e64fbe4acd0a 100644 --- a/gcc/rust/ast/rust-stmt.h +++ b/gcc/rust/ast/rust-stmt.h @@ -19,6 +19,7 @@ #ifndef RUST_AST_STATEMENT_H #define RUST_AST_STATEMENT_H +#include "optional.h" #include "rust-ast.h" #include "rust-path.h" #include "rust-expr.h" @@ -72,6 +73,8 @@ class LetStmt : public Stmt // bool has_init_expr; std::unique_ptr init_expr; + tl::optional> else_expr; + location_t locus; public: @@ -85,15 +88,18 @@ class LetStmt : public Stmt // Returns whether let statement has an initialisation expression. bool has_init_expr () const { return init_expr != nullptr; } + bool has_else_expr () const { return else_expr.has_value (); } std::string as_string () const override; LetStmt (std::unique_ptr variables_pattern, std::unique_ptr init_expr, std::unique_ptr type, + tl::optional> else_expr, std::vector outer_attrs, location_t locus) : outer_attrs (std::move (outer_attrs)), variables_pattern (std::move (variables_pattern)), - type (std::move (type)), init_expr (std::move (init_expr)), locus (locus) + type (std::move (type)), init_expr (std::move (init_expr)), + else_expr (std::move (else_expr)), locus (locus) {} // Copy constructor with clone @@ -107,6 +113,9 @@ class LetStmt : public Stmt // guard to prevent null dereference (always required) if (other.init_expr != nullptr) init_expr = other.init_expr->clone_expr (); + if (other.else_expr.has_value ()) + else_expr = other.else_expr.value ()->clone_expr (); + if (other.type != nullptr) type = other.type->clone_type (); } @@ -128,6 +137,12 @@ class LetStmt : public Stmt init_expr = other.init_expr->clone_expr (); else init_expr = nullptr; + + if (other.else_expr != nullptr) + else_expr = other.else_expr.value ()->clone_expr (); + else + else_expr = tl::nullopt; + if (other.type != nullptr) type = other.type->clone_type (); else @@ -162,12 +177,24 @@ class LetStmt : public Stmt return *init_expr; } + Expr &get_else_expr () + { + rust_assert (has_else_expr ()); + return *else_expr.value (); + } + std::unique_ptr &get_init_expr_ptr () { rust_assert (has_init_expr ()); return init_expr; } + std::unique_ptr &get_else_expr_ptr () + { + rust_assert (has_else_expr ()); + return else_expr.value (); + } + Pattern &get_pattern () { rust_assert (variables_pattern != nullptr); diff --git a/gcc/rust/hir/rust-ast-lower-stmt.cc b/gcc/rust/hir/rust-ast-lower-stmt.cc index 761a638a80e6..0d3947cba2fa 100644 --- a/gcc/rust/hir/rust-ast-lower-stmt.cc +++ b/gcc/rust/hir/rust-ast-lower-stmt.cc @@ -76,20 +76,26 @@ ASTLoweringStmt::visit (AST::LetStmt &stmt) type = std::unique_ptr (ASTLoweringType::translate (stmt.get_type ())); - tl::optional> init_expression = tl::nullopt; + tl::optional> init_expr = tl::nullopt; + tl::optional> else_expr = tl::nullopt; if (stmt.has_init_expr ()) - init_expression = std::unique_ptr ( + init_expr = std::unique_ptr ( ASTLoweringExpr::translate (stmt.get_init_expr ())); + if (stmt.has_else_expr ()) + else_expr = std::unique_ptr ( + ASTLoweringExpr::translate (stmt.get_else_expr ())); + auto crate_num = mappings.get_current_crate (); Analysis::NodeMapping mapping (crate_num, stmt.get_node_id (), mappings.get_next_hir_id (crate_num), UNKNOWN_LOCAL_DEFID); translated = new HIR::LetStmt (mapping, std::unique_ptr (variables), - std::move (init_expression), std::move (type), - stmt.get_outer_attrs (), stmt.get_locus ()); + std::move (init_expr), std::move (else_expr), + std::move (type), stmt.get_outer_attrs (), + stmt.get_locus ()); } void diff --git a/gcc/rust/hir/tree/rust-hir-stmt.cc b/gcc/rust/hir/tree/rust-hir-stmt.cc index 025f67e2c9b1..fd58e29c7f8d 100644 --- a/gcc/rust/hir/tree/rust-hir-stmt.cc +++ b/gcc/rust/hir/tree/rust-hir-stmt.cc @@ -26,11 +26,13 @@ namespace HIR { LetStmt::LetStmt (Analysis::NodeMapping mappings, std::unique_ptr variables_pattern, tl::optional> init_expr, + tl::optional> else_expr, tl::optional> type, AST::AttrVec outer_attrs, location_t locus) : Stmt (std::move (mappings)), outer_attrs (std::move (outer_attrs)), variables_pattern (std::move (variables_pattern)), type (std::move (type)), - init_expr (std::move (init_expr)), locus (locus) + init_expr (std::move (init_expr)), else_expr (std::move (else_expr)), + locus (locus) {} LetStmt::LetStmt (LetStmt const &other) @@ -43,6 +45,8 @@ LetStmt::LetStmt (LetStmt const &other) // guard to prevent null dereference (always required) if (other.has_init_expr ()) init_expr = other.get_init_expr ().clone_expr (); + if (other.has_else_expr ()) + else_expr = other.get_else_expr ().clone_expr (); if (other.has_type ()) type = other.get_type ().clone_type (); @@ -67,6 +71,12 @@ LetStmt::operator= (LetStmt const &other) init_expr = other.get_init_expr ().clone_expr (); else init_expr = nullptr; + + if (other.has_else_expr ()) + else_expr = other.get_else_expr ().clone_expr (); + else + else_expr = tl::nullopt; + if (other.has_type ()) type = other.get_type ().clone_type (); else diff --git a/gcc/rust/hir/tree/rust-hir-stmt.h b/gcc/rust/hir/tree/rust-hir-stmt.h index 1e17f047d3e2..bfa8fe1e15bf 100644 --- a/gcc/rust/hir/tree/rust-hir-stmt.h +++ b/gcc/rust/hir/tree/rust-hir-stmt.h @@ -101,6 +101,7 @@ class LetStmt : public Stmt tl::optional> type; tl::optional> init_expr; + tl::optional> else_expr; location_t locus; @@ -113,12 +114,15 @@ class LetStmt : public Stmt // Returns whether let statement has an initialisation expression. bool has_init_expr () const { return init_expr.has_value (); } + // Returns whether let statement has a diverging else expression. + bool has_else_expr () const { return else_expr.has_value (); } std::string as_string () const override; LetStmt (Analysis::NodeMapping mappings, std::unique_ptr variables_pattern, tl::optional> init_expr, + tl::optional> else_expr, tl::optional> type, AST::AttrVec outer_attrs, location_t locus); @@ -167,6 +171,18 @@ class LetStmt : public Stmt return *init_expr.value (); } + HIR::Expr &get_else_expr () + { + rust_assert (*else_expr); + return *else_expr.value (); + } + + const HIR::Expr &get_else_expr () const + { + rust_assert (*else_expr); + return *else_expr.value (); + } + HIR::Pattern &get_pattern () { return *variables_pattern; } bool is_item () const override final { return false; } diff --git a/gcc/rust/parse/rust-parse-impl.h b/gcc/rust/parse/rust-parse-impl.h index a1b22c0a4168..bd5011329142 100644 --- a/gcc/rust/parse/rust-parse-impl.h +++ b/gcc/rust/parse/rust-parse-impl.h @@ -6163,6 +6163,10 @@ Parser::parse_let_stmt (AST::AttrVec outer_attrs, } } + tl::optional> else_expr = tl::nullopt; + if (maybe_skip_token (ELSE)) + else_expr = parse_block_expr (); + if (restrictions.consume_semi) { // `stmt` macro variables are parsed without a semicolon, but should be @@ -6177,7 +6181,7 @@ Parser::parse_let_stmt (AST::AttrVec outer_attrs, return std::unique_ptr ( new AST::LetStmt (std::move (pattern), std::move (expr), std::move (type), - std::move (outer_attrs), locus)); + std::move (else_expr), std::move (outer_attrs), locus)); } // Parses a type path. diff --git a/gcc/rust/resolve/rust-ast-resolve-stmt.h b/gcc/rust/resolve/rust-ast-resolve-stmt.h index 5d5891043627..a1261a070c1c 100644 --- a/gcc/rust/resolve/rust-ast-resolve-stmt.h +++ b/gcc/rust/resolve/rust-ast-resolve-stmt.h @@ -73,9 +73,10 @@ class ResolveStmt : public ResolverBase void visit (AST::LetStmt &stmt) override { if (stmt.has_init_expr ()) - { - ResolveExpr::go (stmt.get_init_expr (), prefix, canonical_prefix); - } + ResolveExpr::go (stmt.get_init_expr (), prefix, canonical_prefix); + + if (stmt.has_else_expr ()) + ResolveExpr::go (stmt.get_else_expr (), prefix, canonical_prefix); PatternDeclaration::go (stmt.get_pattern (), Rib::ItemType::Var); if (stmt.has_type ()) diff --git a/gcc/rust/resolve/rust-late-name-resolver-2.0.cc b/gcc/rust/resolve/rust-late-name-resolver-2.0.cc index d1c10e51a082..5fcf0ec27cde 100644 --- a/gcc/rust/resolve/rust-late-name-resolver-2.0.cc +++ b/gcc/rust/resolve/rust-late-name-resolver-2.0.cc @@ -140,6 +140,9 @@ Late::visit (AST::LetStmt &let) visit (let.get_init_expr ()); visit (let.get_pattern ()); + if (let.has_else_expr ()) + visit (let.get_init_expr ()); + // how do we deal with the fact that `let a = blipbloup` should look for a // label and cannot go through function ribs, but `let a = blipbloup()` can? diff --git a/gcc/rust/typecheck/rust-hir-type-check-stmt.cc b/gcc/rust/typecheck/rust-hir-type-check-stmt.cc index 87a7733848a1..aa50e257374c 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-stmt.cc +++ b/gcc/rust/typecheck/rust-hir-type-check-stmt.cc @@ -17,12 +17,14 @@ // . #include "rust-hir-type-check-stmt.h" +#include "rich-location.h" #include "rust-hir-type-check-type.h" #include "rust-hir-type-check-expr.h" #include "rust-hir-type-check-implitem.h" #include "rust-hir-type-check-item.h" #include "rust-hir-type-check-pattern.h" #include "rust-type-util.h" +#include "rust-tyty.h" namespace Rust { namespace Resolver { @@ -78,6 +80,7 @@ TypeCheckStmt::visit (HIR::LetStmt &stmt) auto &stmt_pattern = stmt.get_pattern (); TyTy::BaseType *init_expr_ty = nullptr; location_t init_expr_locus = UNKNOWN_LOCATION; + if (stmt.has_init_expr ()) { init_expr_locus = stmt.get_init_expr ().get_locus (); @@ -97,6 +100,22 @@ TypeCheckStmt::visit (HIR::LetStmt &stmt) specified_ty_locus = stmt.get_type ().get_locus (); } + if (stmt.has_else_expr ()) + { + auto &else_expr = stmt.get_else_expr (); + auto else_expr_ty = TypeCheckExpr::Resolve (else_expr); + + if (else_expr_ty->get_kind () != TyTy::TypeKind::NEVER) + { + rust_error_at (else_expr.get_locus (), + "% clause of let-else does not diverge"); + return; + } + + // FIXME: Is that enough? Do we need to do something like + // `append_reference` here as well? + } + // let x:i32 = 123; if (specified_ty != nullptr && init_expr_ty != nullptr) { diff --git a/gcc/testsuite/rust/compile/let-else-invalid-type.rs b/gcc/testsuite/rust/compile/let-else-invalid-type.rs new file mode 100644 index 000000000000..6679e5763949 --- /dev/null +++ b/gcc/testsuite/rust/compile/let-else-invalid-type.rs @@ -0,0 +1,8 @@ +enum FakeOption { + Some(i32), + None, +} + +fn main() { + let FakeOption::Some(a) = FakeOption::None else { FakeOption::Some(15) }; // { dg-error "does not diverge" } +}