Skip to content

Commit 545b068

Browse files
Merge #10474
10474: feat: Support `let...else` r=jonas-schievink a=jonas-schievink bors r+ closes #10469 Co-authored-by: Jonas Schievink <[email protected]>
2 parents 4675410 + f8acae7 commit 545b068

File tree

13 files changed

+162
-8
lines changed

13 files changed

+162
-8
lines changed

Cargo.lock

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/hir_def/src/body/lower.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,16 @@ impl ExprCollector<'_> {
639639
let type_ref =
640640
stmt.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
641641
let initializer = stmt.initializer().map(|e| self.collect_expr(e));
642-
self.statements_in_scope.push(Statement::Let { pat, type_ref, initializer });
642+
let else_branch = stmt
643+
.let_else()
644+
.and_then(|let_else| let_else.block_expr())
645+
.map(|block| self.collect_block(block));
646+
self.statements_in_scope.push(Statement::Let {
647+
pat,
648+
type_ref,
649+
initializer,
650+
else_branch,
651+
});
643652
}
644653
ast::Stmt::ExprStmt(stmt) => {
645654
if let Some(expr) = stmt.expr() {

crates/hir_def/src/body/scope.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -149,11 +149,15 @@ fn compute_block_scopes(
149149
) {
150150
for stmt in statements {
151151
match stmt {
152-
Statement::Let { pat, initializer, .. } => {
152+
Statement::Let { pat, initializer, else_branch, .. } => {
153153
if let Some(expr) = initializer {
154154
scopes.set_scope(*expr, scope);
155155
compute_expr_scopes(*expr, body, scopes, scope);
156156
}
157+
if let Some(expr) = else_branch {
158+
scopes.set_scope(*expr, scope);
159+
compute_expr_scopes(*expr, body, scopes, scope);
160+
}
157161
scope = scopes.new_scope(scope);
158162
scopes.add_bindings(body, scope, *pat);
159163
}

crates/hir_def/src/expr.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,16 @@ pub struct RecordLitField {
208208

209209
#[derive(Debug, Clone, Eq, PartialEq)]
210210
pub enum Statement {
211-
Let { pat: PatId, type_ref: Option<Interned<TypeRef>>, initializer: Option<ExprId> },
212-
Expr { expr: ExprId, has_semi: bool },
211+
Let {
212+
pat: PatId,
213+
type_ref: Option<Interned<TypeRef>>,
214+
initializer: Option<ExprId>,
215+
else_branch: Option<ExprId>,
216+
},
217+
Expr {
218+
expr: ExprId,
219+
has_semi: bool,
220+
},
213221
}
214222

215223
impl Expr {

crates/hir_ty/src/infer/expr.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -914,7 +914,7 @@ impl<'a> InferenceContext<'a> {
914914
) -> Ty {
915915
for stmt in statements {
916916
match stmt {
917-
Statement::Let { pat, type_ref, initializer } => {
917+
Statement::Let { pat, type_ref, initializer, else_branch } => {
918918
let decl_ty = type_ref
919919
.as_ref()
920920
.map(|tr| self.make_ty(tr))
@@ -931,6 +931,13 @@ impl<'a> InferenceContext<'a> {
931931
}
932932
}
933933

934+
if let Some(expr) = else_branch {
935+
self.infer_expr_coerce(
936+
*expr,
937+
&Expectation::has_type(Ty::new(&Interner, TyKind::Never)),
938+
);
939+
}
940+
934941
self.infer_pat(*pat, &ty, BindingMode::default());
935942
}
936943
Statement::Expr { expr, .. } => {

crates/hir_ty/src/tests/never_type.rs

+36
Original file line numberDiff line numberDiff line change
@@ -407,3 +407,39 @@ fn diverging_expression_3_break() {
407407
"]],
408408
);
409409
}
410+
411+
#[test]
412+
fn let_else_must_diverge() {
413+
check_infer_with_mismatches(
414+
r#"
415+
fn f() {
416+
let 1 = 2 else {
417+
return;
418+
};
419+
}
420+
"#,
421+
expect![[r#"
422+
7..54 '{ ... }; }': ()
423+
17..18 '1': i32
424+
17..18 '1': i32
425+
21..22 '2': i32
426+
28..51 '{ ... }': !
427+
38..44 'return': !
428+
"#]],
429+
);
430+
check_infer_with_mismatches(
431+
r#"
432+
fn f() {
433+
let 1 = 2 else {};
434+
}
435+
"#,
436+
expect![[r#"
437+
7..33 '{ ... {}; }': ()
438+
17..18 '1': i32
439+
17..18 '1': i32
440+
21..22 '2': i32
441+
28..30 '{}': ()
442+
28..30: expected !, got ()
443+
"#]],
444+
);
445+
}

crates/parser/src/grammar/expressions.rs

+10
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,16 @@ pub(super) fn stmt(p: &mut Parser, with_semi: StmtWithSemi, prefer_expr: bool) {
102102
expressions::expr(p);
103103
}
104104

105+
if p.at(T![else]) {
106+
// test let_else
107+
// fn f() { let Some(x) = opt else { return }; }
108+
109+
let m = p.start();
110+
p.bump(T![else]);
111+
block_expr(p);
112+
m.complete(p, LET_ELSE);
113+
}
114+
105115
match with_semi {
106116
StmtWithSemi::No => (),
107117
StmtWithSemi::Optional => {

crates/parser/src/syntax_kind/generated.rs

+1
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ pub enum SyntaxKind {
234234
NAME,
235235
NAME_REF,
236236
LET_STMT,
237+
LET_ELSE,
237238
EXPR_STMT,
238239
GENERIC_PARAM_LIST,
239240
GENERIC_PARAM,

crates/syntax/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ rayon = "1"
2929
expect-test = "1.1"
3030
proc-macro2 = "1.0.8"
3131
quote = "1.0.2"
32-
ungrammar = "=1.14.6"
32+
ungrammar = "=1.14.8"
3333

3434
test_utils = { path = "../test_utils" }
3535
sourcegen = { path = "../sourcegen" }

crates/syntax/src/ast/generated/nodes.rs

+26
Original file line numberDiff line numberDiff line change
@@ -722,9 +722,19 @@ impl LetStmt {
722722
pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
723723
pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
724724
pub fn initializer(&self) -> Option<Expr> { support::child(&self.syntax) }
725+
pub fn let_else(&self) -> Option<LetElse> { support::child(&self.syntax) }
725726
pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) }
726727
}
727728

729+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
730+
pub struct LetElse {
731+
pub(crate) syntax: SyntaxNode,
732+
}
733+
impl LetElse {
734+
pub fn else_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![else]) }
735+
pub fn block_expr(&self) -> Option<BlockExpr> { support::child(&self.syntax) }
736+
}
737+
728738
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
729739
pub struct ArrayExpr {
730740
pub(crate) syntax: SyntaxNode,
@@ -2304,6 +2314,17 @@ impl AstNode for LetStmt {
23042314
}
23052315
fn syntax(&self) -> &SyntaxNode { &self.syntax }
23062316
}
2317+
impl AstNode for LetElse {
2318+
fn can_cast(kind: SyntaxKind) -> bool { kind == LET_ELSE }
2319+
fn cast(syntax: SyntaxNode) -> Option<Self> {
2320+
if Self::can_cast(syntax.kind()) {
2321+
Some(Self { syntax })
2322+
} else {
2323+
None
2324+
}
2325+
}
2326+
fn syntax(&self) -> &SyntaxNode { &self.syntax }
2327+
}
23072328
impl AstNode for ArrayExpr {
23082329
fn can_cast(kind: SyntaxKind) -> bool { kind == ARRAY_EXPR }
23092330
fn cast(syntax: SyntaxNode) -> Option<Self> {
@@ -4320,6 +4341,11 @@ impl std::fmt::Display for LetStmt {
43204341
std::fmt::Display::fmt(self.syntax(), f)
43214342
}
43224343
}
4344+
impl std::fmt::Display for LetElse {
4345+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4346+
std::fmt::Display::fmt(self.syntax(), f)
4347+
}
4348+
}
43234349
impl std::fmt::Display for ArrayExpr {
43244350
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43254351
std::fmt::Display::fmt(self.syntax(), f)

crates/syntax/src/tests/ast_src.rs

+1
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc {
198198
"NAME",
199199
"NAME_REF",
200200
"LET_STMT",
201+
"LET_ELSE",
201202
"EXPR_STMT",
202203
"GENERIC_PARAM_LIST",
203204
"GENERIC_PARAM",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fn f() { let Some(x) = opt else { return }; }

0 commit comments

Comments
 (0)