about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir-def/src/body/lower.rs67
-rw-r--r--crates/hir-def/src/body/pretty.rs70
-rw-r--r--crates/hir-def/src/expr.rs47
-rw-r--r--crates/hir-ty/src/diagnostics/unsafe_check.rs6
-rw-r--r--crates/hir-ty/src/infer/expr.rs217
-rw-r--r--crates/hir-ty/src/mir/lower.rs110
-rw-r--r--crates/hir-ty/src/tests.rs5
-rw-r--r--crates/hir-ty/src/tests/method_resolution.rs1
-rw-r--r--crates/hir-ty/src/tests/simple.rs9
9 files changed, 285 insertions, 247 deletions
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index 04b1c4f01e2..3164a5f4c29 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -37,7 +37,7 @@ use crate::{
     item_scope::BuiltinShadowMode,
     path::{GenericArgs, Path},
     type_ref::{Mutability, Rawness, TypeRef},
-    AdtId, BlockLoc, ModuleDefId, UnresolvedMacro,
+    AdtId, BlockId, BlockLoc, ModuleDefId, UnresolvedMacro,
 };
 
 pub struct LowerCtx<'a> {
@@ -238,33 +238,32 @@ impl ExprCollector<'_> {
             }
             ast::Expr::BlockExpr(e) => match e.modifier() {
                 Some(ast::BlockModifier::Try(_)) => {
-                    let body = self.collect_block(e);
-                    self.alloc_expr(Expr::TryBlock { body }, syntax_ptr)
+                    self.collect_block_(e, |id, statements, tail| Expr::TryBlock {
+                        id,
+                        statements,
+                        tail,
+                    })
                 }
                 Some(ast::BlockModifier::Unsafe(_)) => {
-                    let body = self.collect_block(e);
-                    self.alloc_expr(Expr::Unsafe { body }, syntax_ptr)
+                    self.collect_block_(e, |id, statements, tail| Expr::Unsafe {
+                        id,
+                        statements,
+                        tail,
+                    })
                 }
-                // FIXME: we need to record these effects somewhere...
                 Some(ast::BlockModifier::Label(label)) => {
                     let label = self.collect_label(label);
-                    let res = self.collect_block(e);
-                    match &mut self.body.exprs[res] {
-                        Expr::Block { label: block_label, .. } => {
-                            *block_label = Some(label);
-                        }
-                        _ => unreachable!(),
-                    }
-                    res
-                }
-                Some(ast::BlockModifier::Async(_)) => {
-                    let body = self.collect_block(e);
-                    self.alloc_expr(Expr::Async { body }, syntax_ptr)
-                }
-                Some(ast::BlockModifier::Const(_)) => {
-                    let body = self.collect_block(e);
-                    self.alloc_expr(Expr::Const { body }, syntax_ptr)
+                    self.collect_block_(e, |id, statements, tail| Expr::Block {
+                        id,
+                        statements,
+                        tail,
+                        label: Some(label),
+                    })
                 }
+                Some(ast::BlockModifier::Async(_)) => self
+                    .collect_block_(e, |id, statements, tail| Expr::Async { id, statements, tail }),
+                Some(ast::BlockModifier::Const(_)) => self
+                    .collect_block_(e, |id, statements, tail| Expr::Const { id, statements, tail }),
                 None => self.collect_block(e),
             },
             ast::Expr::LoopExpr(e) => {
@@ -737,6 +736,19 @@ impl ExprCollector<'_> {
     }
 
     fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId {
+        self.collect_block_(block, |id, statements, tail| Expr::Block {
+            id,
+            statements,
+            tail,
+            label: None,
+        })
+    }
+
+    fn collect_block_(
+        &mut self,
+        block: ast::BlockExpr,
+        mk_block: impl FnOnce(BlockId, Box<[Statement]>, Option<ExprId>) -> Expr,
+    ) -> ExprId {
         let file_local_id = self.ast_id_map.ast_id(&block);
         let ast_id = AstId::new(self.expander.current_file_id, file_local_id);
         let block_loc =
@@ -769,15 +781,8 @@ impl ExprCollector<'_> {
         });
 
         let syntax_node_ptr = AstPtr::new(&block.into());
-        let expr_id = self.alloc_expr(
-            Expr::Block {
-                id: block_id,
-                statements: statements.into_boxed_slice(),
-                tail,
-                label: None,
-            },
-            syntax_node_ptr,
-        );
+        let expr_id = self
+            .alloc_expr(mk_block(block_id, statements.into_boxed_slice(), tail), syntax_node_ptr);
 
         self.expander.def_map = prev_def_map;
         self.expander.module = prev_local_module;
diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs
index 4b4664a1cf4..622756ee8a9 100644
--- a/crates/hir-def/src/body/pretty.rs
+++ b/crates/hir-def/src/body/pretty.rs
@@ -292,18 +292,6 @@ impl<'a> Printer<'a> {
                 self.print_expr(*expr);
                 w!(self, "?");
             }
-            Expr::TryBlock { body } => {
-                w!(self, "try ");
-                self.print_expr(*body);
-            }
-            Expr::Async { body } => {
-                w!(self, "async ");
-                self.print_expr(*body);
-            }
-            Expr::Const { body } => {
-                w!(self, "const ");
-                self.print_expr(*body);
-            }
             Expr::Cast { expr, type_ref } => {
                 self.print_expr(*expr);
                 w!(self, " as ");
@@ -402,10 +390,6 @@ impl<'a> Printer<'a> {
                 }
                 w!(self, ")");
             }
-            Expr::Unsafe { body } => {
-                w!(self, "unsafe ");
-                self.print_expr(*body);
-            }
             Expr::Array(arr) => {
                 w!(self, "[");
                 if !matches!(arr, Array::ElementList { elements, .. } if elements.is_empty()) {
@@ -428,25 +412,47 @@ impl<'a> Printer<'a> {
             }
             Expr::Literal(lit) => self.print_literal(lit),
             Expr::Block { id: _, statements, tail, label } => {
-                self.whitespace();
-                if let Some(lbl) = label {
-                    w!(self, "{}: ", self.body[*lbl].name);
+                let label = label.map(|lbl| format!("{}: ", self.body[lbl].name));
+                self.print_block(label.as_deref(), statements, tail);
+            }
+            Expr::Unsafe { id: _, statements, tail } => {
+                self.print_block(Some("unsafe "), statements, tail);
+            }
+            Expr::TryBlock { id: _, statements, tail } => {
+                self.print_block(Some("try "), statements, tail);
+            }
+            Expr::Async { id: _, statements, tail } => {
+                self.print_block(Some("async "), statements, tail);
+            }
+            Expr::Const { id: _, statements, tail } => {
+                self.print_block(Some("const "), statements, tail);
+            }
+        }
+    }
+
+    fn print_block(
+        &mut self,
+        label: Option<&str>,
+        statements: &Box<[Statement]>,
+        tail: &Option<la_arena::Idx<Expr>>,
+    ) {
+        self.whitespace();
+        if let Some(lbl) = label {
+            w!(self, "{}", lbl);
+        }
+        w!(self, "{{");
+        if !statements.is_empty() || tail.is_some() {
+            self.indented(|p| {
+                for stmt in &**statements {
+                    p.print_stmt(stmt);
                 }
-                w!(self, "{{");
-                if !statements.is_empty() || tail.is_some() {
-                    self.indented(|p| {
-                        for stmt in &**statements {
-                            p.print_stmt(stmt);
-                        }
-                        if let Some(tail) = tail {
-                            p.print_expr(*tail);
-                        }
-                        p.newline();
-                    });
+                if let Some(tail) = tail {
+                    p.print_expr(*tail);
                 }
-                w!(self, "}}");
-            }
+                p.newline();
+            });
         }
+        w!(self, "}}");
     }
 
     fn print_pat(&mut self, pat: PatId) {
diff --git a/crates/hir-def/src/expr.rs b/crates/hir-def/src/expr.rs
index 8d6f0be2648..78a2f861233 100644
--- a/crates/hir-def/src/expr.rs
+++ b/crates/hir-def/src/expr.rs
@@ -109,6 +109,26 @@ pub enum Expr {
         tail: Option<ExprId>,
         label: Option<LabelId>,
     },
+    TryBlock {
+        id: BlockId,
+        statements: Box<[Statement]>,
+        tail: Option<ExprId>,
+    },
+    Async {
+        id: BlockId,
+        statements: Box<[Statement]>,
+        tail: Option<ExprId>,
+    },
+    Const {
+        id: BlockId,
+        statements: Box<[Statement]>,
+        tail: Option<ExprId>,
+    },
+    Unsafe {
+        id: BlockId,
+        statements: Box<[Statement]>,
+        tail: Option<ExprId>,
+    },
     Loop {
         body: ExprId,
         label: Option<LabelId>,
@@ -172,15 +192,6 @@ pub enum Expr {
     Try {
         expr: ExprId,
     },
-    TryBlock {
-        body: ExprId,
-    },
-    Async {
-        body: ExprId,
-    },
-    Const {
-        body: ExprId,
-    },
     Cast {
         expr: ExprId,
         type_ref: Interned<TypeRef>,
@@ -222,9 +233,6 @@ pub enum Expr {
         exprs: Box<[ExprId]>,
         is_assignee_expr: bool,
     },
-    Unsafe {
-        body: ExprId,
-    },
     Array(Array),
     Literal(Literal),
     Underscore,
@@ -290,13 +298,20 @@ impl Expr {
             Expr::Let { expr, .. } => {
                 f(*expr);
             }
-            Expr::Block { statements, tail, .. } => {
+            Expr::Block { statements, tail, .. }
+            | Expr::TryBlock { statements, tail, .. }
+            | Expr::Unsafe { statements, tail, .. }
+            | Expr::Async { statements, tail, .. }
+            | Expr::Const { statements, tail, .. } => {
                 for stmt in statements.iter() {
                     match stmt {
-                        Statement::Let { initializer, .. } => {
+                        Statement::Let { initializer, else_branch, .. } => {
                             if let &Some(expr) = initializer {
                                 f(expr);
                             }
+                            if let &Some(expr) = else_branch {
+                                f(expr);
+                            }
                         }
                         Statement::Expr { expr: expression, .. } => f(*expression),
                     }
@@ -305,10 +320,6 @@ impl Expr {
                     f(expr);
                 }
             }
-            Expr::TryBlock { body }
-            | Expr::Unsafe { body }
-            | Expr::Async { body }
-            | Expr::Const { body } => f(*body),
             Expr::Loop { body, .. } => f(*body),
             Expr::While { condition, body, .. } => {
                 f(*condition);
diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs
index 431ab949b46..9a32a9e92ce 100644
--- a/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -94,8 +94,10 @@ fn walk_unsafe(
                 unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
             }
         }
-        Expr::Unsafe { body: child } => {
-            return walk_unsafe(db, infer, def, body, *child, true, unsafe_expr_cb);
+        Expr::Unsafe { .. } => {
+            expr.walk_child_exprs(|child| {
+                walk_unsafe(db, infer, def, body, child, true, unsafe_expr_cb);
+            });
         }
         _ => {}
     }
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 81e97a9b0bf..023e19d25ed 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -124,41 +124,18 @@ impl<'a> InferenceContext<'a> {
                 self.result.standard_types.bool_.clone()
             }
             Expr::Block { statements, tail, label, id: _ } => {
-                let old_resolver = mem::replace(
-                    &mut self.resolver,
-                    resolver_for_expr(self.db.upcast(), self.owner, tgt_expr),
-                );
-                let ty = match label {
-                    Some(_) => {
-                        let break_ty = expected.coercion_target_type(&mut self.table);
-                        let (breaks, ty) = self.with_breakable_ctx(
-                            BreakableKind::Block,
-                            Some(break_ty.clone()),
-                            *label,
-                            |this| {
-                                this.infer_block(
-                                    tgt_expr,
-                                    statements,
-                                    *tail,
-                                    &Expectation::has_type(break_ty),
-                                )
-                            },
-                        );
-                        breaks.unwrap_or(ty)
-                    }
-                    None => self.infer_block(tgt_expr, statements, *tail, expected),
-                };
-                self.resolver = old_resolver;
-                ty
+                self.infer_block(tgt_expr, statements, *tail, *label, expected)
             }
-            Expr::Unsafe { body } => self.infer_expr(*body, expected),
-            Expr::Const { body } => {
+            Expr::Unsafe { id: _, statements, tail } => {
+                self.infer_block(tgt_expr, statements, *tail, None, expected)
+            }
+            Expr::Const { id: _, statements, tail } => {
                 self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
-                    this.infer_expr(*body, expected)
+                    this.infer_block(tgt_expr, statements, *tail, None, expected)
                 })
                 .1
             }
-            Expr::TryBlock { body } => {
+            Expr::TryBlock { id: _, statements, tail } => {
                 // The type that is returned from the try block
                 let try_ty = self.table.new_type_var();
                 if let Some(ty) = expected.only_has_type(&mut self.table) {
@@ -169,13 +146,16 @@ impl<'a> InferenceContext<'a> {
                 let ok_ty =
                     self.resolve_associated_type(try_ty.clone(), self.resolve_ops_try_output());
 
-                self.with_breakable_ctx(BreakableKind::Block, Some(ok_ty.clone()), None, |this| {
-                    this.infer_expr(*body, &Expectation::has_type(ok_ty));
-                });
-
+                self.infer_block(
+                    tgt_expr,
+                    statements,
+                    *tail,
+                    None,
+                    &Expectation::has_type(ok_ty.clone()),
+                );
                 try_ty
             }
-            Expr::Async { body } => {
+            Expr::Async { id: _, statements, tail } => {
                 let ret_ty = self.table.new_type_var();
                 let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
                 let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
@@ -184,7 +164,13 @@ impl<'a> InferenceContext<'a> {
 
                 let (_, inner_ty) =
                     self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
-                        this.infer_expr_coerce(*body, &Expectation::has_type(ret_ty))
+                        this.infer_block(
+                            tgt_expr,
+                            statements,
+                            *tail,
+                            None,
+                            &Expectation::has_type(ret_ty),
+                        )
                     });
 
                 self.diverges = prev_diverges;
@@ -193,7 +179,8 @@ impl<'a> InferenceContext<'a> {
 
                 // Use the first type parameter as the output type of future.
                 // existential type AsyncBlockImplTrait<InnerType>: Future<Output = InnerType>
-                let impl_trait_id = crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, *body);
+                let impl_trait_id =
+                    crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, tgt_expr);
                 let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into();
                 TyKind::OpaqueType(opaque_ty_id, Substitution::from1(Interner, inner_ty))
                     .intern(Interner)
@@ -1153,80 +1140,102 @@ impl<'a> InferenceContext<'a> {
         expr: ExprId,
         statements: &[Statement],
         tail: Option<ExprId>,
+        label: Option<LabelId>,
         expected: &Expectation,
     ) -> Ty {
-        for stmt in statements {
-            match stmt {
-                Statement::Let { pat, type_ref, initializer, else_branch } => {
-                    let decl_ty = type_ref
-                        .as_ref()
-                        .map(|tr| self.make_ty(tr))
-                        .unwrap_or_else(|| self.table.new_type_var());
-
-                    let ty = if let Some(expr) = initializer {
-                        let ty = if contains_explicit_ref_binding(&self.body, *pat) {
-                            self.infer_expr(*expr, &Expectation::has_type(decl_ty.clone()))
-                        } else {
-                            self.infer_expr_coerce(*expr, &Expectation::has_type(decl_ty.clone()))
-                        };
-                        if type_ref.is_some() {
-                            decl_ty
-                        } else {
-                            ty
+        let coerce_ty = expected.coercion_target_type(&mut self.table);
+        let old_resolver =
+            mem::replace(&mut self.resolver, resolver_for_expr(self.db.upcast(), self.owner, expr));
+
+        let (break_ty, ty) =
+            self.with_breakable_ctx(BreakableKind::Block, Some(coerce_ty.clone()), label, |this| {
+                for stmt in statements {
+                    match stmt {
+                        Statement::Let { pat, type_ref, initializer, else_branch } => {
+                            let decl_ty = type_ref
+                                .as_ref()
+                                .map(|tr| this.make_ty(tr))
+                                .unwrap_or_else(|| this.table.new_type_var());
+
+                            let ty = if let Some(expr) = initializer {
+                                let ty = if contains_explicit_ref_binding(&this.body, *pat) {
+                                    this.infer_expr(*expr, &Expectation::has_type(decl_ty.clone()))
+                                } else {
+                                    this.infer_expr_coerce(
+                                        *expr,
+                                        &Expectation::has_type(decl_ty.clone()),
+                                    )
+                                };
+                                if type_ref.is_some() {
+                                    decl_ty
+                                } else {
+                                    ty
+                                }
+                            } else {
+                                decl_ty
+                            };
+
+                            this.infer_top_pat(*pat, &ty);
+
+                            if let Some(expr) = else_branch {
+                                let previous_diverges =
+                                    mem::replace(&mut this.diverges, Diverges::Maybe);
+                                this.infer_expr_coerce(
+                                    *expr,
+                                    &Expectation::HasType(this.result.standard_types.never.clone()),
+                                );
+                                this.diverges = previous_diverges;
+                            }
+                        }
+                        &Statement::Expr { expr, has_semi } => {
+                            this.infer_expr(
+                                expr,
+                                &if has_semi {
+                                    Expectation::none()
+                                } else {
+                                    Expectation::HasType(this.result.standard_types.unit.clone())
+                                },
+                            );
                         }
-                    } else {
-                        decl_ty
-                    };
-
-                    self.infer_top_pat(*pat, &ty);
-
-                    if let Some(expr) = else_branch {
-                        let previous_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
-                        self.infer_expr_coerce(
-                            *expr,
-                            &Expectation::HasType(self.result.standard_types.never.clone()),
-                        );
-                        self.diverges = previous_diverges;
                     }
                 }
-                &Statement::Expr { expr, has_semi } => {
-                    self.infer_expr(
-                        expr,
-                        &if has_semi {
-                            Expectation::none()
-                        } else {
-                            Expectation::HasType(self.result.standard_types.unit.clone())
-                        },
-                    );
-                }
-            }
-        }
 
-        if let Some(expr) = tail {
-            self.infer_expr_coerce(expr, expected)
-        } else {
-            // Citing rustc: if there is no explicit tail expression,
-            // that is typically equivalent to a tail expression
-            // of `()` -- except if the block diverges. In that
-            // case, there is no value supplied from the tail
-            // expression (assuming there are no other breaks,
-            // this implies that the type of the block will be
-            // `!`).
-            if self.diverges.is_always() {
-                // we don't even make an attempt at coercion
-                self.table.new_maybe_never_var()
-            } else if let Some(t) = expected.only_has_type(&mut self.table) {
-                if self.coerce(Some(expr), &TyBuilder::unit(), &t).is_err() {
-                    self.result.type_mismatches.insert(
-                        expr.into(),
-                        TypeMismatch { expected: t.clone(), actual: TyBuilder::unit() },
-                    );
+                // FIXME: This should make use of the breakable CoerceMany
+                if let Some(expr) = tail {
+                    this.infer_expr_coerce(expr, expected)
+                } else {
+                    // Citing rustc: if there is no explicit tail expression,
+                    // that is typically equivalent to a tail expression
+                    // of `()` -- except if the block diverges. In that
+                    // case, there is no value supplied from the tail
+                    // expression (assuming there are no other breaks,
+                    // this implies that the type of the block will be
+                    // `!`).
+                    if this.diverges.is_always() {
+                        // we don't even make an attempt at coercion
+                        this.table.new_maybe_never_var()
+                    } else if let Some(t) = expected.only_has_type(&mut this.table) {
+                        if this
+                            .coerce(Some(expr), &this.result.standard_types.unit.clone(), &t)
+                            .is_err()
+                        {
+                            this.result.type_mismatches.insert(
+                                expr.into(),
+                                TypeMismatch {
+                                    expected: t.clone(),
+                                    actual: this.result.standard_types.unit.clone(),
+                                },
+                            );
+                        }
+                        t
+                    } else {
+                        this.result.standard_types.unit.clone()
+                    }
                 }
-                t
-            } else {
-                TyBuilder::unit()
-            }
-        }
+            });
+        self.resolver = old_resolver;
+
+        break_ty.unwrap_or(ty)
     }
 
     fn lookup_field(
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index 1fa21e230c4..936b56a0217 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -331,56 +331,11 @@ impl MirLowerCtx<'_> {
                 }
                 Ok(result)
             }
+            Expr::Unsafe { id: _, statements, tail } => {
+                self.lower_block_to_place(None, statements, current, *tail, place)
+            }
             Expr::Block { id: _, statements, tail, label } => {
-                if label.is_some() {
-                    not_supported!("block with label");
-                }
-                for statement in statements.iter() {
-                    match statement {
-                        hir_def::expr::Statement::Let {
-                            pat,
-                            initializer,
-                            else_branch,
-                            type_ref: _,
-                        } => match initializer {
-                            Some(expr_id) => {
-                                let else_block;
-                                let init_place;
-                                (init_place, current) =
-                                    self.lower_expr_to_some_place(*expr_id, current)?;
-                                (current, else_block) = self.pattern_match(
-                                    current,
-                                    None,
-                                    init_place,
-                                    self.expr_ty(*expr_id),
-                                    *pat,
-                                    BindingAnnotation::Unannotated,
-                                )?;
-                                match (else_block, else_branch) {
-                                    (None, _) => (),
-                                    (Some(else_block), None) => {
-                                        self.set_terminator(else_block, Terminator::Unreachable);
-                                    }
-                                    (Some(else_block), Some(else_branch)) => {
-                                        let (_, b) = self
-                                            .lower_expr_to_some_place(*else_branch, else_block)?;
-                                        self.set_terminator(b, Terminator::Unreachable);
-                                    }
-                                }
-                            }
-                            None => continue,
-                        },
-                        hir_def::expr::Statement::Expr { expr, has_semi: _ } => {
-                            let ty = self.expr_ty(*expr);
-                            let temp = self.temp(ty)?;
-                            current = self.lower_expr_to_place(*expr, temp.into(), current)?;
-                        }
-                    }
-                }
-                match tail {
-                    Some(tail) => self.lower_expr_to_place(*tail, place, current),
-                    None => Ok(current),
-                }
+                self.lower_block_to_place(*label, statements, current, *tail, place)
             }
             Expr::Loop { body, label } => self.lower_loop(current, *label, |this, begin, _| {
                 let (_, block) = this.lower_expr_to_some_place(*body, begin)?;
@@ -686,7 +641,6 @@ impl MirLowerCtx<'_> {
                 self.push_assignment(current, place, r);
                 Ok(current)
             }
-            Expr::Unsafe { body } => self.lower_expr_to_place(*body, place, current),
             Expr::Array(l) => match l {
                 Array::ElementList { elements, .. } => {
                     let elem_ty = match &self.expr_ty(expr_id).data(Interner).kind {
@@ -723,6 +677,62 @@ impl MirLowerCtx<'_> {
         }
     }
 
+    fn lower_block_to_place(
+        &mut self,
+        label: Option<LabelId>,
+        statements: &[hir_def::expr::Statement],
+        mut current: BasicBlockId,
+        tail: Option<ExprId>,
+        place: Place,
+    ) -> Result<BasicBlockId> {
+        if label.is_some() {
+            not_supported!("block with label");
+        }
+        for statement in statements.iter() {
+            match statement {
+                hir_def::expr::Statement::Let { pat, initializer, else_branch, type_ref: _ } => {
+                    match initializer {
+                        Some(expr_id) => {
+                            let else_block;
+                            let init_place;
+                            (init_place, current) =
+                                self.lower_expr_to_some_place(*expr_id, current)?;
+                            (current, else_block) = self.pattern_match(
+                                current,
+                                None,
+                                init_place,
+                                self.expr_ty(*expr_id),
+                                *pat,
+                                BindingAnnotation::Unannotated,
+                            )?;
+                            match (else_block, else_branch) {
+                                (None, _) => (),
+                                (Some(else_block), None) => {
+                                    self.set_terminator(else_block, Terminator::Unreachable);
+                                }
+                                (Some(else_block), Some(else_branch)) => {
+                                    let (_, b) =
+                                        self.lower_expr_to_some_place(*else_branch, else_block)?;
+                                    self.set_terminator(b, Terminator::Unreachable);
+                                }
+                            }
+                        }
+                        None => continue,
+                    }
+                }
+                hir_def::expr::Statement::Expr { expr, has_semi: _ } => {
+                    let ty = self.expr_ty(*expr);
+                    let temp = self.temp(ty)?;
+                    current = self.lower_expr_to_place(*expr, temp.into(), current)?;
+                }
+            }
+        }
+        match tail {
+            Some(tail) => self.lower_expr_to_place(tail, place, current),
+            None => Ok(current),
+        }
+    }
+
     fn lower_literal_to_operand(&mut self, ty: Ty, l: &Literal) -> Result<Operand> {
         let size = layout_of_ty(self.db, &ty, self.owner.module(self.db.upcast()).krate())?
             .size
diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs
index ab848a18eb3..759878b10bb 100644
--- a/crates/hir-ty/src/tests.rs
+++ b/crates/hir-ty/src/tests.rs
@@ -61,22 +61,27 @@ fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> {
     Some(tracing::subscriber::set_default(subscriber))
 }
 
+#[track_caller]
 fn check_types(ra_fixture: &str) {
     check_impl(ra_fixture, false, true, false)
 }
 
+#[track_caller]
 fn check_types_source_code(ra_fixture: &str) {
     check_impl(ra_fixture, false, true, true)
 }
 
+#[track_caller]
 fn check_no_mismatches(ra_fixture: &str) {
     check_impl(ra_fixture, true, false, false)
 }
 
+#[track_caller]
 fn check(ra_fixture: &str) {
     check_impl(ra_fixture, false, false, false)
 }
 
+#[track_caller]
 fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_source: bool) {
     let _tracing = setup_tracing();
     let (db, files) = TestDB::with_many_files(ra_fixture);
diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs
index 41c53701df6..4b671449e15 100644
--- a/crates/hir-ty/src/tests/method_resolution.rs
+++ b/crates/hir-ty/src/tests/method_resolution.rs
@@ -1167,7 +1167,6 @@ fn test() {
             123..167 '{     ...o(); }': ()
             133..134 's': &S
             137..151 'unsafe { f() }': &S
-            137..151 'unsafe { f() }': &S
             146..147 'f': fn f() -> &S
             146..149 'f()': &S
             157..158 's': &S
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index 2e5787b701c..1a07a2c51d8 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/crates/hir-ty/src/tests/simple.rs
@@ -352,7 +352,6 @@ unsafe fn baz(u: MyUnion) {
             71..89 'MyUnio...o: 0 }': MyUnion
             86..87 '0': u32
             95..113 'unsafe...(u); }': ()
-            95..113 'unsafe...(u); }': ()
             104..107 'baz': fn baz(MyUnion)
             104..110 'baz(u)': ()
             108..109 'u': MyUnion
@@ -360,7 +359,6 @@ unsafe fn baz(u: MyUnion) {
             126..146 'MyUnio... 0.0 }': MyUnion
             141..144 '0.0': f32
             152..170 'unsafe...(u); }': ()
-            152..170 'unsafe...(u); }': ()
             161..164 'baz': fn baz(MyUnion)
             161..167 'baz(u)': ()
             165..166 'u': MyUnion
@@ -2077,22 +2075,17 @@ async fn main() {
             16..193 '{     ...2 }; }': ()
             26..27 'x': i32
             30..43 'unsafe { 92 }': i32
-            30..43 'unsafe { 92 }': i32
             39..41 '92': i32
             53..54 'y': impl Future<Output = ()>
-            57..85 'async ...wait }': ()
             57..85 'async ...wait }': impl Future<Output = ()>
-            65..77 'async { () }': ()
             65..77 'async { () }': impl Future<Output = ()>
             65..83 'async ....await': ()
             73..75 '()': ()
             95..96 'z': ControlFlow<(), ()>
-            130..140 'try { () }': ()
             130..140 'try { () }': ControlFlow<(), ()>
             136..138 '()': ()
             150..151 'w': i32
             154..166 'const { 92 }': i32
-            154..166 'const { 92 }': i32
             162..164 '92': i32
             176..177 't': i32
             180..190 ''a: { 92 }': i32
@@ -2122,7 +2115,6 @@ fn main() {
             83..84 'f': F
             89..91 '{}': ()
             103..231 '{     ... }); }': ()
-            109..161 'async ...     }': Result<(), ()>
             109..161 'async ...     }': impl Future<Output = Result<(), ()>>
             125..139 'return Err(())': !
             132..135 'Err': Err<(), ()>(()) -> Result<(), ()>
@@ -2134,7 +2126,6 @@ fn main() {
             167..171 'test': fn test<(), (), || -> impl Future<Output = Result<(), ()>>, impl Future<Output = Result<(), ()>>>(|| -> impl Future<Output = Result<(), ()>>)
             167..228 'test(|...    })': ()
             172..227 '|| asy...     }': || -> impl Future<Output = Result<(), ()>>
-            175..227 'async ...     }': Result<(), ()>
             175..227 'async ...     }': impl Future<Output = Result<(), ()>>
             191..205 'return Err(())': !
             198..201 'Err': Err<(), ()>(()) -> Result<(), ()>