Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/mir/lower.rs')
| -rw-r--r-- | crates/hir-ty/src/mir/lower.rs | 467 |
1 files changed, 194 insertions, 273 deletions
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 8e7fb091c0..4cb5dece76 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -11,19 +11,23 @@ use hir_def::{ }, lang_item::{LangItem, LangItemTarget}, layout::LayoutError, + path::Path, resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, DefWithBodyId, EnumVariantId, HasModule, }; +use hir_expand::name; use la_arena::ArenaMap; use crate::{ consteval::ConstEvalError, db::HirDatabase, display::HirDisplay, infer::TypeMismatch, inhabitedness::is_ty_uninhabited_from, layout::layout_of_ty, mapping::ToChalk, static_lifetime, - utils::generics, Adjust, AutoBorrow, CallableDefId, TyBuilder, TyExt, + utils::generics, Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt, }; use super::*; +mod as_place; + #[derive(Debug, Clone, Copy)] struct LoopBlocks { begin: BasicBlockId, @@ -61,6 +65,7 @@ pub enum MirLowerError { /// Something that should never happen and is definitely a bug, but we don't want to panic if it happened ImplementationError(&'static str), LangItemNotFound(LangItem), + MutatingRvalue, } macro_rules! not_supported { @@ -69,6 +74,13 @@ macro_rules! not_supported { }; } +macro_rules! implementation_error { + ($x: expr) => {{ + ::stdx::never!("MIR lower implementation bug: {}", $x); + return Err(MirLowerError::ImplementationError($x)); + }}; +} + impl From<ConstEvalError> for MirLowerError { fn from(value: ConstEvalError) -> Self { match value { @@ -84,117 +96,89 @@ impl From<LayoutError> for MirLowerError { } } +impl MirLowerError { + fn unresolved_path(db: &dyn HirDatabase, p: &Path) -> Self { + Self::UnresolvedName(p.display(db).to_string()) + } +} + type Result<T> = std::result::Result<T, MirLowerError>; impl MirLowerCtx<'_> { fn temp(&mut self, ty: Ty) -> Result<LocalId> { if matches!(ty.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) { - not_supported!("unsized temporaries"); + implementation_error!("unsized temporaries"); } Ok(self.result.locals.alloc(Local { ty })) } - fn lower_expr_as_place(&self, expr_id: ExprId) -> Option<Place> { - let adjustments = self.infer.expr_adjustments.get(&expr_id); - let mut r = self.lower_expr_as_place_without_adjust(expr_id)?; - for adjustment in adjustments.iter().flat_map(|x| x.iter()) { - match adjustment.kind { - Adjust::NeverToAny => return Some(r), - Adjust::Deref(None) => { - r.projection.push(ProjectionElem::Deref); - } - Adjust::Deref(Some(_)) => return None, - Adjust::Borrow(_) => return None, - Adjust::Pointer(_) => return None, - } - } - Some(r) - } - - fn lower_expr_as_place_without_adjust(&self, expr_id: ExprId) -> Option<Place> { - match &self.body.exprs[expr_id] { - Expr::Path(p) => { - let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id); - let pr = resolver.resolve_path_in_value_ns(self.db.upcast(), p.mod_path())?; - let pr = match pr { - ResolveValueResult::ValueNs(v) => v, - ResolveValueResult::Partial(..) => return None, - }; - match pr { - ValueNs::LocalBinding(pat_id) => { - Some(self.result.binding_locals[pat_id].into()) - } - _ => None, - } - } - Expr::UnaryOp { expr, op } => match op { - hir_def::expr::UnaryOp::Deref => { - if !matches!( - self.expr_ty(*expr).kind(Interner), - TyKind::Ref(..) | TyKind::Raw(..) - ) { - return None; - } - let mut r = self.lower_expr_as_place(*expr)?; - r.projection.push(ProjectionElem::Deref); - Some(r) - } - _ => None, - }, - Expr::Field { expr, .. } => { - let mut r = self.lower_expr_as_place(*expr)?; - self.push_field_projection(&mut r, expr_id).ok()?; - Some(r) - } - _ => None, - } - } - fn lower_expr_to_some_operand( &mut self, expr_id: ExprId, current: BasicBlockId, - ) -> Result<(Operand, Option<BasicBlockId>)> { + ) -> Result<Option<(Operand, BasicBlockId)>> { if !self.has_adjustments(expr_id) { match &self.body.exprs[expr_id] { Expr::Literal(l) => { let ty = self.expr_ty(expr_id); - return Ok((self.lower_literal_to_operand(ty, l)?, Some(current))); + return Ok(Some((self.lower_literal_to_operand(ty, l)?, current))); } _ => (), } } - let (p, current) = self.lower_expr_to_some_place(expr_id, current)?; - Ok((Operand::Copy(p), current)) - } - - fn lower_expr_to_some_place( - &mut self, - expr_id: ExprId, - prev_block: BasicBlockId, - ) -> Result<(Place, Option<BasicBlockId>)> { - if let Some(p) = self.lower_expr_as_place(expr_id) { - return Ok((p, Some(prev_block))); - } - let ty = self.expr_ty_after_adjustments(expr_id); - let place = self.temp(ty)?; - Ok((place.into(), self.lower_expr_to_place(expr_id, place.into(), prev_block)?)) + let Some((p, current)) = self.lower_expr_as_place(current, expr_id, true)? else { + return Ok(None); + }; + Ok(Some((Operand::Copy(p), current))) } - fn lower_expr_to_some_place_without_adjust( + fn lower_expr_to_place_with_adjust( &mut self, expr_id: ExprId, - prev_block: BasicBlockId, - ) -> Result<(Place, Option<BasicBlockId>)> { - if let Some(p) = self.lower_expr_as_place_without_adjust(expr_id) { - return Ok((p, Some(prev_block))); + place: Place, + current: BasicBlockId, + adjustments: &[Adjustment], + ) -> Result<Option<BasicBlockId>> { + match adjustments.split_last() { + Some((last, rest)) => match &last.kind { + Adjust::NeverToAny => { + let temp = self.temp(TyKind::Never.intern(Interner))?; + self.lower_expr_to_place_with_adjust(expr_id, temp.into(), current, rest) + } + Adjust::Deref(_) => { + let Some((p, current)) = self.lower_expr_as_place_with_adjust(current, expr_id, true, adjustments)? else { + return Ok(None); + }; + self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into()); + Ok(Some(current)) + } + Adjust::Borrow(AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m)) => { + let Some((p, current)) = self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)? else { + return Ok(None); + }; + let bk = BorrowKind::from_chalk(*m); + self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into()); + Ok(Some(current)) + } + Adjust::Pointer(cast) => { + let Some((p, current)) = self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)? else { + return Ok(None); + }; + self.push_assignment( + current, + place, + Rvalue::Cast( + CastKind::Pointer(cast.clone()), + Operand::Copy(p).into(), + last.target.clone(), + ), + expr_id.into(), + ); + Ok(Some(current)) + } + }, + None => self.lower_expr_to_place_without_adjust(expr_id, place, current), } - let ty = self.expr_ty(expr_id); - let place = self.temp(ty)?; - Ok(( - place.into(), - self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)?, - )) } fn lower_expr_to_place( @@ -203,50 +187,8 @@ impl MirLowerCtx<'_> { place: Place, prev_block: BasicBlockId, ) -> Result<Option<BasicBlockId>> { - if let Some(x) = self.infer.expr_adjustments.get(&expr_id) { - if x.len() > 0 { - let (mut r, Some(current)) = - self.lower_expr_to_some_place_without_adjust(expr_id, prev_block)? - else { - return Ok(None); - }; - for adjustment in x { - match &adjustment.kind { - Adjust::NeverToAny => (), - Adjust::Deref(None) => { - r.projection.push(ProjectionElem::Deref); - } - Adjust::Deref(Some(_)) => not_supported!("implicit overloaded dereference"), - Adjust::Borrow(AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m)) => { - let tmp = self.temp(adjustment.target.clone())?; - self.push_assignment( - current, - tmp.into(), - Rvalue::Ref(BorrowKind::from_chalk(*m), r), - expr_id.into(), - ); - r = tmp.into(); - } - Adjust::Pointer(cast) => { - let target = &adjustment.target; - let tmp = self.temp(target.clone())?; - self.push_assignment( - current, - tmp.into(), - Rvalue::Cast( - CastKind::Pointer(cast.clone()), - Operand::Copy(r).into(), - target.clone(), - ), - expr_id.into(), - ); - r = tmp.into(); - } - } - } - self.push_assignment(current, place, Operand::Copy(r).into(), expr_id.into()); - return Ok(Some(current)); - } + if let Some(adjustments) = self.infer.expr_adjustments.get(&expr_id) { + return self.lower_expr_to_place_with_adjust(expr_id, place, prev_block, adjustments); } self.lower_expr_to_place_without_adjust(expr_id, place, prev_block) } @@ -260,7 +202,7 @@ impl MirLowerCtx<'_> { match &self.body.exprs[expr_id] { Expr::Missing => Err(MirLowerError::IncompleteExpr), Expr::Path(p) => { - let unresolved_name = || MirLowerError::UnresolvedName(p.display(self.db).to_string()); + let unresolved_name = || MirLowerError::unresolved_path(self.db, p); let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id); let pr = resolver .resolve_path_in_value_ns(self.db.upcast(), p.mod_path()) @@ -286,7 +228,7 @@ impl MirLowerCtx<'_> { match variant { VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e), VariantId::StructId(s) => ValueNs::StructId(s), - VariantId::UnionId(_) => return Err(MirLowerError::ImplementationError("Union variant as path")), + VariantId::UnionId(_) => implementation_error!("Union variant as path"), } } else { return Err(unresolved_name()); @@ -355,7 +297,7 @@ impl MirLowerCtx<'_> { } } Expr::If { condition, then_branch, else_branch } => { - let (discr, Some(current)) = self.lower_expr_to_some_operand(*condition, current)? else { + let Some((discr, current)) = self.lower_expr_to_some_operand(*condition, current)? else { return Ok(None); }; let start_of_then = self.new_basic_block(); @@ -377,8 +319,7 @@ impl MirLowerCtx<'_> { Ok(self.merge_blocks(end_of_then, end_of_else)) } Expr::Let { pat, expr } => { - self.push_storage_live(*pat, current)?; - let (cond_place, Some(current)) = self.lower_expr_to_some_place(*expr, current)? else { + let Some((cond_place, current)) = self.lower_expr_as_place(current, *expr, true)? else { return Ok(None); }; let (then_target, else_target) = self.pattern_match( @@ -411,70 +352,17 @@ impl MirLowerCtx<'_> { 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: _, - } => { - self.push_storage_live(*pat, current)?; - if let Some(expr_id) = initializer { - let else_block; - let (init_place, Some(c)) = - self.lower_expr_to_some_place(*expr_id, current)? - else { - return Ok(None); - }; - current = c; - (current, else_block) = self.pattern_match( - current, - None, - init_place, - self.expr_ty_after_adjustments(*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)?; - if let Some(b) = b { - self.set_terminator(b, Terminator::Unreachable); - } - } - } - } }, - hir_def::expr::Statement::Expr { expr, has_semi: _ } => { - let (_, Some(c)) = self.lower_expr_to_some_place(*expr, current)? else { - return Ok(None); - }; - current = c; - } - } - } - match tail { - Some(tail) => self.lower_expr_to_place(*tail, place, current), - None => Ok(Some(current)), - } + self.lower_block_to_place(*label, statements, current, *tail, place) } Expr::Loop { body, label } => self.lower_loop(current, *label, |this, begin| { - if let (_, Some(block)) = this.lower_expr_to_some_place(*body, begin)? { + if let Some((_, block)) = this.lower_expr_as_place(begin, *body, true)? { this.set_goto(block, begin); } Ok(()) }), Expr::While { condition, body, label } => { self.lower_loop(current, *label, |this, begin| { - let (discr, Some(to_switch)) = this.lower_expr_to_some_operand(*condition, begin)? else { + let Some((discr, to_switch)) = this.lower_expr_to_some_operand(*condition, begin)? else { return Ok(()); }; let end = this.current_loop_end()?; @@ -486,7 +374,7 @@ impl MirLowerCtx<'_> { targets: SwitchTargets::static_if(1, after_cond, end), }, ); - if let (_, Some(block)) = this.lower_expr_to_some_place(*body, after_cond)? { + if let Some((_, block)) = this.lower_expr_as_place(after_cond, *body, true)? { this.set_goto(block, begin); } Ok(()) @@ -510,7 +398,9 @@ impl MirLowerCtx<'_> { self.db.intern_callable_def(CallableDefId::FunctionId(iter_next_fn)).into(), Substitution::from1(Interner, self.expr_ty(iterable)) ).intern(Interner)); - let iterator_ty = &self.infer.type_of_for_iterator[expr_id]; + let &Some(iterator_ty) = &self.infer.type_of_for_iterator.get(&expr_id) else { + return Err(MirLowerError::TypeError("unknown for loop iterator type")); + }; let ref_mut_iterator_ty = TyKind::Ref(Mutability::Mut, static_lifetime(), iterator_ty.clone()).intern(Interner); let item_ty = &self.infer.type_of_pat[pat]; let option_item_ty = TyKind::Adt(chalk_ir::AdtId(option.into()), Substitution::from1(Interner, item_ty.clone())).intern(Interner); @@ -523,7 +413,6 @@ impl MirLowerCtx<'_> { }; self.push_assignment(current, ref_mut_iterator_place.clone(), Rvalue::Ref(BorrowKind::Mut { allow_two_phase_borrow: false }, iterator_place), expr_id.into()); self.lower_loop(current, label, |this, begin| { - this.push_storage_live(pat, begin)?; let Some(current) = this.lower_call(iter_next_fn_op, vec![Operand::Copy(ref_mut_iterator_place)], option_item_place.clone(), begin, false)? else { return Ok(()); @@ -538,7 +427,7 @@ impl MirLowerCtx<'_> { pat.into(), Some(end), &[pat], &None)?; - if let (_, Some(block)) = this.lower_expr_to_some_place(body, current)? { + if let Some((_, block)) = this.lower_expr_as_place(current, body, true)? { this.set_goto(block, begin); } Ok(()) @@ -595,7 +484,7 @@ impl MirLowerCtx<'_> { ) } Expr::Match { expr, arms } => { - let (cond_place, Some(mut current)) = self.lower_expr_to_some_place(*expr, current)? + let Some((cond_place, mut current)) = self.lower_expr_as_place(current, *expr, true)? else { return Ok(None); }; @@ -605,7 +494,6 @@ impl MirLowerCtx<'_> { if guard.is_some() { not_supported!("pattern matching with guard"); } - self.push_storage_live(*pat, current)?; let (then, otherwise) = self.pattern_match( current, None, @@ -686,7 +574,7 @@ impl MirLowerCtx<'_> { for RecordLitField { name, expr } in fields.iter() { let field_id = variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?; - let (op, Some(c)) = self.lower_expr_to_some_operand(*expr, current)? else { + let Some((op, c)) = self.lower_expr_to_some_operand(*expr, current)? else { return Ok(None); }; current = c; @@ -719,19 +607,6 @@ impl MirLowerCtx<'_> { } } } - Expr::Field { expr, .. } => { - let (mut current_place, Some(current)) = self.lower_expr_to_some_place(*expr, current)? else { - return Ok(None); - }; - self.push_field_projection(&mut current_place, expr_id)?; - self.push_assignment( - current, - place, - Operand::Copy(current_place).into(), - expr_id.into(), - ); - Ok(Some(current)) - } Expr::Await { .. } => not_supported!("await"), Expr::Try { .. } => not_supported!("? operator"), Expr::Yeet { .. } => not_supported!("yeet"), @@ -739,7 +614,7 @@ impl MirLowerCtx<'_> { Expr::Async { .. } => not_supported!("async block"), Expr::Const { .. } => not_supported!("anonymous const block"), Expr::Cast { expr, type_ref: _ } => { - let (x, Some(current)) = self.lower_expr_to_some_operand(*expr, current)? else { + let Some((x, current)) = self.lower_expr_to_some_operand(*expr, current)? else { return Ok(None); }; let source_ty = self.infer[*expr].clone(); @@ -753,7 +628,7 @@ impl MirLowerCtx<'_> { Ok(Some(current)) } Expr::Ref { expr, rawness: _, mutability } => { - let (p, Some(current)) = self.lower_expr_to_some_place(*expr, current)? else { + let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else { return Ok(None); }; let bk = BorrowKind::from_hir(*mutability); @@ -761,35 +636,29 @@ impl MirLowerCtx<'_> { Ok(Some(current)) } Expr::Box { .. } => not_supported!("box expression"), - Expr::UnaryOp { expr, op } => match op { - hir_def::expr::UnaryOp::Deref => { - if !matches!(self.expr_ty(*expr).kind(Interner), TyKind::Ref(..) | TyKind::Raw(..)) { - not_supported!("explicit overloaded deref"); - } - let (mut tmp, Some(current)) = self.lower_expr_to_some_place(*expr, current)? else { - return Ok(None); - }; - tmp.projection.push(ProjectionElem::Deref); - self.push_assignment(current, place, Operand::Copy(tmp).into(), expr_id.into()); - Ok(Some(current)) - } - hir_def::expr::UnaryOp::Not | hir_def::expr::UnaryOp::Neg => { - let (operand, Some(current)) = self.lower_expr_to_some_operand(*expr, current)? else { - return Ok(None); - }; - let operation = match op { - hir_def::expr::UnaryOp::Not => UnOp::Not, - hir_def::expr::UnaryOp::Neg => UnOp::Neg, - _ => unreachable!(), - }; - self.push_assignment( - current, - place, - Rvalue::UnaryOp(operation, operand), - expr_id.into(), - ); - Ok(Some(current)) - } + Expr::Field { .. } | Expr::Index { .. } | Expr::UnaryOp { op: hir_def::expr::UnaryOp::Deref, .. } => { + let Some((p, current)) = self.lower_expr_as_place(current, expr_id, true)? else { + return Ok(None); + }; + self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into()); + Ok(Some(current)) + } + Expr::UnaryOp { expr, op: op @ (hir_def::expr::UnaryOp::Not | hir_def::expr::UnaryOp::Neg) } => { + let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? else { + return Ok(None); + }; + let operation = match op { + hir_def::expr::UnaryOp::Not => UnOp::Not, + hir_def::expr::UnaryOp::Neg => UnOp::Neg, + _ => unreachable!(), + }; + self.push_assignment( + current, + place, + Rvalue::UnaryOp(operation, operand), + expr_id.into(), + ); + Ok(Some(current)) }, Expr::BinaryOp { lhs, rhs, op } => { let op = op.ok_or(MirLowerError::IncompleteExpr)?; @@ -797,19 +666,21 @@ impl MirLowerCtx<'_> { if op.is_some() { not_supported!("assignment with arith op (like +=)"); } - let Some(lhs_place) = self.lower_expr_as_place(*lhs) else { - not_supported!("assignment to complex place"); + let Some((lhs_place, current)) = + self.lower_expr_as_place(current, *lhs, false)? + else { + return Ok(None); }; - let (rhs_op, Some(current)) = self.lower_expr_to_some_operand(*rhs, current)? else { + let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else { return Ok(None); }; self.push_assignment(current, lhs_place, rhs_op.into(), expr_id.into()); return Ok(Some(current)); } - let (lhs_op, Some(current)) = self.lower_expr_to_some_operand(*lhs, current)? else { + let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)? else { return Ok(None); }; - let (rhs_op, Some(current)) = self.lower_expr_to_some_operand(*rhs, current)? else { + let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else { return Ok(None); }; self.push_assignment( @@ -833,24 +704,12 @@ impl MirLowerCtx<'_> { Ok(Some(current)) } Expr::Range { .. } => not_supported!("range"), - Expr::Index { base, index } => { - let (mut p_base, Some(current)) = self.lower_expr_to_some_place(*base, current)? else { - return Ok(None); - }; - let l_index = self.temp(self.expr_ty_after_adjustments(*index))?; - let Some(current) = self.lower_expr_to_place(*index, l_index.into(), current)? else { - return Ok(None); - }; - p_base.projection.push(ProjectionElem::Index(l_index)); - self.push_assignment(current, place, Operand::Copy(p_base).into(), expr_id.into()); - Ok(Some(current)) - } Expr::Closure { .. } => not_supported!("closure"), Expr::Tuple { exprs, is_assignee_expr: _ } => { let Some(values) = exprs .iter() .map(|x| { - let (o, Some(c)) = self.lower_expr_to_some_operand(*x, current)? else { + let Some((o, c)) = self.lower_expr_to_some_operand(*x, current)? else { return Ok(None); }; current = c; @@ -880,7 +739,7 @@ impl MirLowerCtx<'_> { let Some(values) = elements .iter() .map(|x| { - let (o, Some(c)) = self.lower_expr_to_some_operand(*x, current)? else { + let Some((o, c)) = self.lower_expr_to_some_operand(*x, current)? else { return Ok(None); }; current = c; @@ -1034,7 +893,7 @@ impl MirLowerCtx<'_> { ) -> Result<Option<BasicBlockId>> { let Some(args) = args .map(|arg| { - if let (temp, Some(c)) = self.lower_expr_to_some_operand(arg, current)? { + if let Some((temp, c)) = self.lower_expr_to_some_operand(arg, current)? { current = c; Ok(Some(temp)) } else { @@ -1250,6 +1109,7 @@ impl MirLowerCtx<'_> { if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) { binding_mode = mode; } + self.push_storage_live(*id, current)?; self.push_assignment( current, target_place.into(), @@ -1464,7 +1324,7 @@ impl MirLowerCtx<'_> { } /// This function push `StorageLive` statements for each binding in the pattern. - fn push_storage_live(&mut self, pat: PatId, current: BasicBlockId) -> Result<()> { + fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) -> Result<()> { // Current implementation is wrong. It adds no `StorageDead` at the end of scope, and before each break // and continue. It just add a `StorageDead` before the `StorageLive`, which is not wrong, but unneeeded in // the proper implementation. Due this limitation, implementing a borrow checker on top of this mir will falsely @@ -1483,12 +1343,15 @@ impl MirLowerCtx<'_> { // ``` // But I think this approach work for mutability analysis, as user can't write code which mutates a binding // after StorageDead, except loops, which are handled by this hack. - let span = pat.into(); - self.body.walk_child_bindings(pat, &mut |b| { - let l = self.result.binding_locals[b]; - self.push_statement(current, StatementKind::StorageDead(l).with_span(span)); - self.push_statement(current, StatementKind::StorageLive(l).with_span(span)); - }); + let span = self.body.bindings[b] + .definitions + .first() + .copied() + .map(MirSpan::PatId) + .unwrap_or(MirSpan::Unknown); + let l = self.result.binding_locals[b]; + self.push_statement(current, StatementKind::StorageDead(l).with_span(span)); + self.push_statement(current, StatementKind::StorageLive(l).with_span(span)); Ok(()) } @@ -1496,6 +1359,65 @@ impl MirLowerCtx<'_> { let crate_id = self.owner.module(self.db.upcast()).krate(); self.db.lang_item(crate_id, item).ok_or(MirLowerError::LangItemNotFound(item)) } + + fn lower_block_to_place( + &mut self, + label: Option<LabelId>, + statements: &[hir_def::expr::Statement], + mut current: BasicBlockId, + tail: Option<ExprId>, + place: Place, + ) -> Result<Option<Idx<BasicBlock>>> { + 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: _ } => { + if let Some(expr_id) = initializer { + let else_block; + let Some((init_place, c)) = + self.lower_expr_as_place(current, *expr_id, true)? + else { + return Ok(None); + }; + current = c; + (current, else_block) = self.pattern_match( + current, + None, + init_place, + self.expr_ty_after_adjustments(*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)) => { + if let Some((_, b)) = + self.lower_expr_as_place(else_block, *else_branch, true)? + { + self.set_terminator(b, Terminator::Unreachable); + } + } + } + } + } + hir_def::expr::Statement::Expr { expr, has_semi: _ } => { + let Some((_, c)) = self.lower_expr_as_place(current, *expr, true)? else { + return Ok(None); + }; + current = c; + } + } + } + match tail { + Some(tail) => self.lower_expr_to_place(tail, place, current), + None => Ok(Some(current)), + } + } } fn pattern_matching_dereference( @@ -1533,7 +1455,8 @@ fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> { pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result<Arc<MirBody>> { let body = db.body(def); let infer = db.infer(def); - Ok(Arc::new(lower_to_mir(db, def, &body, &infer, body.body_expr)?)) + let result = lower_to_mir(db, def, &body, &infer, body.body_expr)?; + Ok(Arc::new(result)) } pub fn mir_body_recover( @@ -1553,9 +1476,7 @@ pub fn lower_to_mir( // need to take this input explicitly. root_expr: ExprId, ) -> Result<MirBody> { - if let (Some((_, x)), _) | (_, Some((_, x))) = - (infer.expr_type_mismatches().next(), infer.pat_type_mismatches().next()) - { + if let Some((_, x)) = infer.type_mismatches().next() { return Err(MirLowerError::TypeMismatch(x.clone())); } let mut basic_blocks = Arena::new(); |