Skip to content

Commit

Permalink
Merge #10474
Browse files Browse the repository at this point in the history
10474: feat: Support `let...else` r=jonas-schievink a=jonas-schievink

bors r+

closes #10469

Co-authored-by: Jonas Schievink <[email protected]>
  • Loading branch information
bors[bot] and jonas-schievink authored Oct 7, 2021
2 parents 4675410 + f8acae7 commit 545b068
Show file tree
Hide file tree
Showing 13 changed files with 162 additions and 8 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 10 additions & 1 deletion crates/hir_def/src/body/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
6 changes: 5 additions & 1 deletion crates/hir_def/src/body/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
12 changes: 10 additions & 2 deletions crates/hir_def/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
9 changes: 8 additions & 1 deletion crates/hir_ty/src/infer/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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, .. } => {
Expand Down
36 changes: 36 additions & 0 deletions crates/hir_ty/src/tests/never_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 ()
"#]],
);
}
10 changes: 10 additions & 0 deletions crates/parser/src/grammar/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down
1 change: 1 addition & 0 deletions crates/parser/src/syntax_kind/generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ pub enum SyntaxKind {
NAME,
NAME_REF,
LET_STMT,
LET_ELSE,
EXPR_STMT,
GENERIC_PARAM_LIST,
GENERIC_PARAM,
Expand Down
2 changes: 1 addition & 1 deletion crates/syntax/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
26 changes: 26 additions & 0 deletions crates/syntax/src/ast/generated/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -722,9 +722,19 @@ 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,
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions crates/syntax/src/tests/ast_src.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions crates/syntax/test_data/parser/inline/ok/0194_let_else.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fn f() { let Some(x) = opt else { return }; }

0 comments on commit 545b068

Please sign in to comment.