Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--Cargo.lock4
-rw-r--r--crates/hir_def/src/body/lower.rs11
-rw-r--r--crates/hir_def/src/body/scope.rs6
-rw-r--r--crates/hir_def/src/expr.rs12
-rw-r--r--crates/hir_ty/src/infer/expr.rs9
-rw-r--r--crates/hir_ty/src/tests/never_type.rs36
-rw-r--r--crates/parser/src/grammar/expressions.rs10
-rw-r--r--crates/parser/src/syntax_kind/generated.rs1
-rw-r--r--crates/syntax/Cargo.toml2
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs26
-rw-r--r--crates/syntax/src/tests/ast_src.rs1
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0194_let_else.rast51
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0194_let_else.rs1
13 files changed, 162 insertions, 8 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 17795ead23..5aba6387f0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1761,9 +1761,9 @@ dependencies = [
[[package]]
name = "ungrammar"
-version = "1.14.6"
+version = "1.14.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb1cd6666863b2ff36bab1ced85c4b5e651638705f306f3cfad0a367f85ea715"
+checksum = "403c1892ad46cacffb28c73550172999c6c75f70ca9c97bcafc8ce99d6421529"
[[package]]
name = "unicase"
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index 372cf9aba8..3136206761 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -639,7 +639,16 @@ impl ExprCollector<'_> {
let type_ref =
stmt.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
let initializer = stmt.initializer().map(|e| self.collect_expr(e));
- self.statements_in_scope.push(Statement::Let { pat, type_ref, initializer });
+ let else_branch = stmt
+ .let_else()
+ .and_then(|let_else| let_else.block_expr())
+ .map(|block| self.collect_block(block));
+ self.statements_in_scope.push(Statement::Let {
+ pat,
+ type_ref,
+ initializer,
+ else_branch,
+ });
}
ast::Stmt::ExprStmt(stmt) => {
if let Some(expr) = stmt.expr() {
diff --git a/crates/hir_def/src/body/scope.rs b/crates/hir_def/src/body/scope.rs
index 5d9dd73a12..8a48ad9199 100644
--- a/crates/hir_def/src/body/scope.rs
+++ b/crates/hir_def/src/body/scope.rs
@@ -149,11 +149,15 @@ fn compute_block_scopes(
) {
for stmt in statements {
match stmt {
- Statement::Let { pat, initializer, .. } => {
+ Statement::Let { pat, initializer, else_branch, .. } => {
if let Some(expr) = initializer {
scopes.set_scope(*expr, scope);
compute_expr_scopes(*expr, body, scopes, scope);
}
+ if let Some(expr) = else_branch {
+ scopes.set_scope(*expr, scope);
+ compute_expr_scopes(*expr, body, scopes, scope);
+ }
scope = scopes.new_scope(scope);
scopes.add_bindings(body, scope, *pat);
}
diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs
index b508d875e8..17af48b7bf 100644
--- a/crates/hir_def/src/expr.rs
+++ b/crates/hir_def/src/expr.rs
@@ -208,8 +208,16 @@ pub struct RecordLitField {
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Statement {
- Let { pat: PatId, type_ref: Option<Interned<TypeRef>>, initializer: Option<ExprId> },
- Expr { expr: ExprId, has_semi: bool },
+ Let {
+ pat: PatId,
+ type_ref: Option<Interned<TypeRef>>,
+ initializer: Option<ExprId>,
+ else_branch: Option<ExprId>,
+ },
+ Expr {
+ expr: ExprId,
+ has_semi: bool,
+ },
}
impl Expr {
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index f5bc898a85..ada5717f1a 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -914,7 +914,7 @@ impl<'a> InferenceContext<'a> {
) -> Ty {
for stmt in statements {
match stmt {
- Statement::Let { pat, type_ref, initializer } => {
+ Statement::Let { pat, type_ref, initializer, else_branch } => {
let decl_ty = type_ref
.as_ref()
.map(|tr| self.make_ty(tr))
@@ -931,6 +931,13 @@ impl<'a> InferenceContext<'a> {
}
}
+ if let Some(expr) = else_branch {
+ self.infer_expr_coerce(
+ *expr,
+ &Expectation::has_type(Ty::new(&Interner, TyKind::Never)),
+ );
+ }
+
self.infer_pat(*pat, &ty, BindingMode::default());
}
Statement::Expr { expr, .. } => {
diff --git a/crates/hir_ty/src/tests/never_type.rs b/crates/hir_ty/src/tests/never_type.rs
index 335c474df7..8be2b8ec10 100644
--- a/crates/hir_ty/src/tests/never_type.rs
+++ b/crates/hir_ty/src/tests/never_type.rs
@@ -407,3 +407,39 @@ fn diverging_expression_3_break() {
"]],
);
}
+
+#[test]
+fn let_else_must_diverge() {
+ check_infer_with_mismatches(
+ r#"
+ fn f() {
+ let 1 = 2 else {
+ return;
+ };
+ }
+ "#,
+ expect![[r#"
+ 7..54 '{ ... }; }': ()
+ 17..18 '1': i32
+ 17..18 '1': i32
+ 21..22 '2': i32
+ 28..51 '{ ... }': !
+ 38..44 'return': !
+ "#]],
+ );
+ check_infer_with_mismatches(
+ r#"
+ fn f() {
+ let 1 = 2 else {};
+ }
+ "#,
+ expect![[r#"
+ 7..33 '{ ... {}; }': ()
+ 17..18 '1': i32
+ 17..18 '1': i32
+ 21..22 '2': i32
+ 28..30 '{}': ()
+ 28..30: expected !, got ()
+ "#]],
+ );
+}
diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs
index aa171674ed..54eb96d84e 100644
--- a/crates/parser/src/grammar/expressions.rs
+++ b/crates/parser/src/grammar/expressions.rs
@@ -102,6 +102,16 @@ pub(super) fn stmt(p: &mut Parser, with_semi: StmtWithSemi, prefer_expr: bool) {
expressions::expr(p);
}
+ if p.at(T![else]) {
+ // test let_else
+ // fn f() { let Some(x) = opt else { return }; }
+
+ let m = p.start();
+ p.bump(T![else]);
+ block_expr(p);
+ m.complete(p, LET_ELSE);
+ }
+
match with_semi {
StmtWithSemi::No => (),
StmtWithSemi::Optional => {
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index 842d66755c..a9010c0c31 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -234,6 +234,7 @@ pub enum SyntaxKind {
NAME,
NAME_REF,
LET_STMT,
+ LET_ELSE,
EXPR_STMT,
GENERIC_PARAM_LIST,
GENERIC_PARAM,
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 3c5adb97fb..fba5db9a93 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -29,7 +29,7 @@ rayon = "1"
expect-test = "1.1"
proc-macro2 = "1.0.8"
quote = "1.0.2"
-ungrammar = "=1.14.6"
+ungrammar = "=1.14.8"
test_utils = { path = "../test_utils" }
sourcegen = { path = "../sourcegen" }
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index f69c873663..15b8371c3e 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -722,10 +722,20 @@ impl LetStmt {
pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
pub fn initializer(&self) -> Option<Expr> { support::child(&self.syntax) }
+ pub fn let_else(&self) -> Option<LetElse> { support::child(&self.syntax) }
pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct LetElse {
+ pub(crate) syntax: SyntaxNode,
+}
+impl LetElse {
+ pub fn else_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![else]) }
+ pub fn block_expr(&self) -> Option<BlockExpr> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ArrayExpr {
pub(crate) syntax: SyntaxNode,
}
@@ -2304,6 +2314,17 @@ impl AstNode for LetStmt {
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
+impl AstNode for LetElse {
+ fn can_cast(kind: SyntaxKind) -> bool { kind == LET_ELSE }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
impl AstNode for ArrayExpr {
fn can_cast(kind: SyntaxKind) -> bool { kind == ARRAY_EXPR }
fn cast(syntax: SyntaxNode) -> Option<Self> {
@@ -4320,6 +4341,11 @@ impl std::fmt::Display for LetStmt {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for LetElse {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for ArrayExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
diff --git a/crates/syntax/src/tests/ast_src.rs b/crates/syntax/src/tests/ast_src.rs
index efbb7ca0cc..2344fc59d1 100644
--- a/crates/syntax/src/tests/ast_src.rs
+++ b/crates/syntax/src/tests/ast_src.rs
@@ -198,6 +198,7 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc {
"NAME",
"NAME_REF",
"LET_STMT",
+ "LET_ELSE",
"EXPR_STMT",
"GENERIC_PARAM_LIST",
"GENERIC_PARAM",
diff --git a/crates/syntax/test_data/parser/inline/ok/0194_let_else.rast b/crates/syntax/test_data/parser/inline/ok/0194_let_else.rast
new file mode 100644
index 0000000000..ed0613a38f
--- /dev/null
+++ b/crates/syntax/test_data/parser/inline/ok/0194_let_else.rast
@@ -0,0 +1,51 @@
diff --git a/crates/syntax/test_data/parser/inline/ok/0194_let_else.rs b/crates/syntax/test_data/parser/inline/ok/0194_let_else.rs
new file mode 100644
index 0000000000..8303de06f1
--- /dev/null
+++ b/crates/syntax/test_data/parser/inline/ok/0194_let_else.rs
@@ -0,0 +1 @@
+fn f() { let Some(x) = opt else { return }; }