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 | 1206 |
1 files changed, 780 insertions, 426 deletions
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 936b56a021..afa5275ac6 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -6,31 +6,38 @@ use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind}; use hir_def::{ body::Body, expr::{ - Array, BindingAnnotation, ExprId, LabelId, Literal, MatchArm, Pat, PatId, RecordLitField, + Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId, + RecordLitField, }, + lang_item::{LangItem, LangItemTarget}, layout::LayoutError, + path::Path, resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, DefWithBodyId, EnumVariantId, HasModule, }; +use hir_expand::name::Name; use la_arena::ArenaMap; use crate::{ - consteval::ConstEvalError, db::HirDatabase, layout::layout_of_ty, mapping::ToChalk, - utils::generics, Adjust, AutoBorrow, CallableDefId, TyBuilder, TyExt, + consteval::ConstEvalError, db::HirDatabase, display::HirDisplay, infer::TypeMismatch, + inhabitedness::is_ty_uninhabited_from, layout::layout_of_ty, mapping::ToChalk, static_lifetime, + utils::generics, Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt, }; use super::*; +mod as_place; + #[derive(Debug, Clone, Copy)] struct LoopBlocks { begin: BasicBlockId, - end: BasicBlockId, + /// `None` for loops that are not terminating + end: Option<BasicBlockId>, } struct MirLowerCtx<'a> { result: MirBody, owner: DefWithBodyId, - binding_locals: ArenaMap<PatId, LocalId>, current_loop_blocks: Option<LoopBlocks>, discr_temp: Option<Place>, db: &'a dyn HirDatabase, @@ -43,13 +50,22 @@ pub enum MirLowerError { ConstEvalError(Box<ConstEvalError>), LayoutError(LayoutError), IncompleteExpr, - UnresolvedName, + UnresolvedName(String), + RecordLiteralWithoutPath, + UnresolvedMethod, + UnresolvedField, MissingFunctionDefinition, + TypeMismatch(TypeMismatch), + /// This should be never happen. Type mismatch should catch everything. TypeError(&'static str), NotSupported(String), ContinueWithoutLoop, BreakWithoutLoop, Loop, + /// 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 { @@ -58,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 { @@ -73,93 +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"); - } - Ok(self.result.locals.alloc(Local { mutability: Mutability::Not, 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.binding_locals[pat_id].into()), - _ => None, - } - } - Expr::UnaryOp { expr, op } => match op { - hir_def::expr::UnaryOp::Deref => { - let mut r = self.lower_expr_as_place(*expr)?; - r.projection.push(ProjectionElem::Deref); - Some(r) - } - _ => None, - }, - _ => None, + implementation_error!("unsized temporaries"); } + Ok(self.result.locals.alloc(Local { ty })) } fn lower_expr_to_some_operand( &mut self, expr_id: ExprId, current: BasicBlockId, - ) -> Result<(Operand, 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)?, 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)) + 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( + fn lower_expr_to_place_with_adjust( &mut self, expr_id: ExprId, - prev_block: BasicBlockId, - ) -> Result<(Place, BasicBlockId)> { - if let Some(p) = self.lower_expr_as_place(expr_id) { - return Ok((p, prev_block)); - } - let mut ty = self.expr_ty(expr_id); - if let Some(x) = self.infer.expr_adjustments.get(&expr_id) { - if let Some(x) = x.last() { - ty = x.target.clone(); - } + 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 place = self.temp(ty)?; - Ok((place.into(), self.lower_expr_to_place(expr_id, place.into(), prev_block)?)) } fn lower_expr_to_place( @@ -167,48 +186,9 @@ impl MirLowerCtx<'_> { expr_id: ExprId, place: Place, prev_block: BasicBlockId, - ) -> Result<BasicBlockId> { - if let Some(x) = self.infer.expr_adjustments.get(&expr_id) { - if x.len() > 0 { - let tmp = self.temp(self.expr_ty(expr_id))?; - let current = - self.lower_expr_to_place_without_adjust(expr_id, tmp.into(), prev_block)?; - let mut r = Place::from(tmp); - for adjustment in x { - match &adjustment.kind { - Adjust::NeverToAny => (), - Adjust::Deref(None) => { - r.projection.push(ProjectionElem::Deref); - } - Adjust::Deref(Some(_)) => not_supported!("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), - ); - 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(), - ), - ); - r = tmp.into(); - } - } - } - self.push_assignment(current, place, Operand::Copy(r).into()); - return Ok(current); - } + ) -> Result<Option<BasicBlockId>> { + 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) } @@ -218,27 +198,41 @@ impl MirLowerCtx<'_> { expr_id: ExprId, place: Place, mut current: BasicBlockId, - ) -> Result<BasicBlockId> { + ) -> Result<Option<BasicBlockId>> { match &self.body.exprs[expr_id] { Expr::Missing => Err(MirLowerError::IncompleteExpr), Expr::Path(p) => { + 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()) - .ok_or(MirLowerError::UnresolvedName)?; + .ok_or_else(unresolved_name)?; let pr = match pr { ResolveValueResult::ValueNs(v) => v, ResolveValueResult::Partial(..) => { - return match self + if let Some(assoc) = self .infer .assoc_resolutions_for_expr(expr_id) - .ok_or(MirLowerError::UnresolvedName)? - .0 - //.ok_or(ConstEvalError::SemanticError("unresolved assoc item"))? { - hir_def::AssocItemId::ConstId(c) => self.lower_const(c, current, place), - _ => return Err(MirLowerError::UnresolvedName), - }; + match assoc.0 { + hir_def::AssocItemId::ConstId(c) => { + self.lower_const(c, current, place, expr_id.into())?; + return Ok(Some(current)) + }, + _ => not_supported!("associated functions and types"), + } + } else if let Some(variant) = self + .infer + .variant_resolution_for_expr(expr_id) + { + match variant { + VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e), + VariantId::StructId(s) => ValueNs::StructId(s), + VariantId::UnionId(_) => implementation_error!("Union variant as path"), + } + } else { + return Err(unresolved_name()); + } } }; match pr { @@ -246,14 +240,26 @@ impl MirLowerCtx<'_> { self.push_assignment( current, place, - Operand::Copy(self.binding_locals[pat_id].into()).into(), + Operand::Copy(self.result.binding_locals[pat_id].into()).into(), + expr_id.into(), ); - Ok(current) + Ok(Some(current)) + } + ValueNs::ConstId(const_id) => { + self.lower_const(const_id, current, place, expr_id.into())?; + Ok(Some(current)) } - ValueNs::ConstId(const_id) => self.lower_const(const_id, current, place), ValueNs::EnumVariantId(variant_id) => { let ty = self.infer.type_of_expr[expr_id].clone(); - self.lower_enum_variant(variant_id, current, place, ty, vec![]) + let current = self.lower_enum_variant( + variant_id, + current, + place, + ty, + vec![], + expr_id.into(), + )?; + Ok(Some(current)) } ValueNs::GenericParam(p) => { let Some(def) = self.owner.as_generic_def_id() else { @@ -277,12 +283,13 @@ impl MirLowerCtx<'_> { .intern(Interner), ) .into(), + expr_id.into(), ); - Ok(current) + Ok(Some(current)) } ValueNs::StructId(_) => { // It's probably a unit struct or a zero sized function, so no action is needed. - Ok(current) + Ok(Some(current)) } x => { not_supported!("unknown name {x:?} in value name space"); @@ -290,19 +297,18 @@ impl MirLowerCtx<'_> { } } Expr::If { condition, then_branch, else_branch } => { - let (discr, current) = self.lower_expr_to_some_operand(*condition, current)?; + let Some((discr, current)) = self.lower_expr_to_some_operand(*condition, current)? else { + return Ok(None); + }; let start_of_then = self.new_basic_block(); - let end = self.new_basic_block(); let end_of_then = self.lower_expr_to_place(*then_branch, place.clone(), start_of_then)?; - self.set_goto(end_of_then, end); - let mut start_of_else = end; - if let Some(else_branch) = else_branch { - start_of_else = self.new_basic_block(); - let end_of_else = - self.lower_expr_to_place(*else_branch, place, start_of_else)?; - self.set_goto(end_of_else, end); - } + let start_of_else = self.new_basic_block(); + let end_of_else = if let Some(else_branch) = else_branch { + self.lower_expr_to_place(*else_branch, place, start_of_else)? + } else { + Some(start_of_else) + }; self.set_terminator( current, Terminator::SwitchInt { @@ -310,26 +316,37 @@ impl MirLowerCtx<'_> { targets: SwitchTargets::static_if(1, start_of_then, start_of_else), }, ); - Ok(end) + Ok(self.merge_blocks(end_of_then, end_of_else)) } Expr::Let { pat, expr } => { - let (cond_place, current) = self.lower_expr_to_some_place(*expr, current)?; - let result = self.new_basic_block(); + 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( current, None, cond_place, - self.expr_ty(*expr), + self.expr_ty_after_adjustments(*expr), *pat, BindingAnnotation::Unannotated, )?; - self.write_bytes_to_place(then_target, place.clone(), vec![1], TyBuilder::bool())?; - self.set_goto(then_target, result); + self.write_bytes_to_place( + then_target, + place.clone(), + vec![1], + TyBuilder::bool(), + MirSpan::Unknown, + )?; if let Some(else_target) = else_target { - self.write_bytes_to_place(else_target, place, vec![0], TyBuilder::bool())?; - self.set_goto(else_target, result); + self.write_bytes_to_place( + else_target, + place, + vec![0], + TyBuilder::bool(), + MirSpan::Unknown, + )?; } - Ok(result) + Ok(self.merge_blocks(Some(then_target), else_target)) } Expr::Unsafe { id: _, statements, tail } => { self.lower_block_to_place(None, statements, current, *tail, place) @@ -337,14 +354,18 @@ impl MirLowerCtx<'_> { Expr::Block { id: _, statements, tail, label } => { 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)?; - this.set_goto(block, begin); + Expr::Loop { body, label } => self.lower_loop(current, *label, |this, 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, end| { - let (discr, to_switch) = this.lower_expr_to_some_operand(*condition, begin)?; + self.lower_loop(current, *label, |this, begin| { + let Some((discr, to_switch)) = this.lower_expr_to_some_operand(*condition, begin)? else { + return Ok(()); + }; + let end = this.current_loop_end()?; let after_cond = this.new_basic_block(); this.set_terminator( to_switch, @@ -353,18 +374,71 @@ impl MirLowerCtx<'_> { targets: SwitchTargets::static_if(1, after_cond, end), }, ); - let (_, block) = this.lower_expr_to_some_place(*body, after_cond)?; - this.set_goto(block, begin); + if let Some((_, block)) = this.lower_expr_as_place(after_cond, *body, true)? { + this.set_goto(block, begin); + } Ok(()) }) } - Expr::For { .. } => not_supported!("for loop"), + &Expr::For { iterable, pat, body, label } => { + let into_iter_fn = self.resolve_lang_item(LangItem::IntoIterIntoIter)? + .as_function().ok_or(MirLowerError::LangItemNotFound(LangItem::IntoIterIntoIter))?; + let iter_next_fn = self.resolve_lang_item(LangItem::IteratorNext)? + .as_function().ok_or(MirLowerError::LangItemNotFound(LangItem::IteratorNext))?; + let option_some = self.resolve_lang_item(LangItem::OptionSome)? + .as_enum_variant().ok_or(MirLowerError::LangItemNotFound(LangItem::OptionSome))?; + let option = option_some.parent; + let into_iter_fn_op = Operand::const_zst( + TyKind::FnDef( + self.db.intern_callable_def(CallableDefId::FunctionId(into_iter_fn)).into(), + Substitution::from1(Interner, self.expr_ty(iterable)) + ).intern(Interner)); + let iter_next_fn_op = Operand::const_zst( + TyKind::FnDef( + self.db.intern_callable_def(CallableDefId::FunctionId(iter_next_fn)).into(), + Substitution::from1(Interner, self.expr_ty(iterable)) + ).intern(Interner)); + 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); + let iterator_place: Place = self.temp(iterator_ty.clone())?.into(); + let option_item_place: Place = self.temp(option_item_ty.clone())?.into(); + let ref_mut_iterator_place: Place = self.temp(ref_mut_iterator_ty)?.into(); + let Some(current) = self.lower_call_and_args(into_iter_fn_op, Some(iterable).into_iter(), iterator_place.clone(), current, false)? + else { + return Ok(None); + }; + 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| { + 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(()); + }; + let end = this.current_loop_end()?; + let (current, _) = this.pattern_matching_variant( + option_item_ty.clone(), + BindingAnnotation::Unannotated, + option_item_place.into(), + option_some.into(), + current, + pat.into(), + Some(end), + &[pat], &None)?; + if let Some((_, block)) = this.lower_expr_as_place(current, body, true)? { + this.set_goto(block, begin); + } + Ok(()) + }) + }, Expr::Call { callee, args, .. } => { - let callee_ty = self.expr_ty(*callee); + let callee_ty = self.expr_ty_after_adjustments(*callee); match &callee_ty.data(Interner).kind { chalk_ir::TyKind::FnDef(..) => { let func = Operand::from_bytes(vec![], callee_ty.clone()); - self.lower_call(func, args.iter().copied(), place, current) + self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id)) } TyKind::Scalar(_) | TyKind::Tuple(_, _) @@ -394,24 +468,28 @@ impl MirLowerCtx<'_> { } Expr::MethodCall { receiver, args, .. } => { let (func_id, generic_args) = - self.infer.method_resolution(expr_id).ok_or(MirLowerError::UnresolvedName)?; + self.infer.method_resolution(expr_id).ok_or(MirLowerError::UnresolvedMethod)?; let ty = chalk_ir::TyKind::FnDef( CallableDefId::FunctionId(func_id).to_chalk(self.db), generic_args, ) .intern(Interner); let func = Operand::from_bytes(vec![], ty); - self.lower_call( + self.lower_call_and_args( func, iter::once(*receiver).chain(args.iter().copied()), place, current, + self.is_uninhabited(expr_id), ) } Expr::Match { expr, arms } => { - let (cond_place, mut current) = self.lower_expr_to_some_place(*expr, current)?; - let cond_ty = self.expr_ty(*expr); - let end = self.new_basic_block(); + let Some((cond_place, mut current)) = self.lower_expr_as_place(current, *expr, true)? + else { + return Ok(None); + }; + let cond_ty = self.expr_ty_after_adjustments(*expr); + let mut end = None; for MatchArm { pat, guard, expr } in arms.iter() { if guard.is_some() { not_supported!("pattern matching with guard"); @@ -424,8 +502,10 @@ impl MirLowerCtx<'_> { *pat, BindingAnnotation::Unannotated, )?; - let block = self.lower_expr_to_place(*expr, place.clone(), then)?; - self.set_goto(block, end); + if let Some(block) = self.lower_expr_to_place(*expr, place.clone(), then)? { + let r = end.get_or_insert_with(|| self.new_basic_block()); + self.set_goto(block, *r); + } match otherwise { Some(o) => current = o, None => { @@ -446,8 +526,7 @@ impl MirLowerCtx<'_> { let loop_data = self.current_loop_blocks.ok_or(MirLowerError::ContinueWithoutLoop)?; self.set_goto(current, loop_data.begin); - let otherwise = self.new_basic_block(); - Ok(otherwise) + Ok(None) } }, Expr::Break { expr, label } => { @@ -457,26 +536,33 @@ impl MirLowerCtx<'_> { match label { Some(_) => not_supported!("break with label"), None => { - let loop_data = - self.current_loop_blocks.ok_or(MirLowerError::BreakWithoutLoop)?; - self.set_goto(current, loop_data.end); - Ok(self.new_basic_block()) + let end = + self.current_loop_end()?; + self.set_goto(current, end); + Ok(None) } } } Expr::Return { expr } => { if let Some(expr) = expr { - current = self.lower_expr_to_place(*expr, return_slot().into(), current)?; + if let Some(c) = self.lower_expr_to_place(*expr, return_slot().into(), current)? { + current = c; + } else { + return Ok(None); + } } self.set_terminator(current, Terminator::Return); - Ok(self.new_basic_block()) + Ok(None) } Expr::Yield { .. } => not_supported!("yield"), - Expr::RecordLit { fields, .. } => { + Expr::RecordLit { fields, path, .. } => { let variant_id = self .infer .variant_resolution_for_expr(expr_id) - .ok_or(MirLowerError::UnresolvedName)?; + .ok_or_else(|| match path { + Some(p) => MirLowerError::UnresolvedName(p.display(self.db).to_string()), + None => MirLowerError::RecordLiteralWithoutPath, + })?; let subst = match self.expr_ty(expr_id).kind(Interner) { TyKind::Adt(_, s) => s.clone(), _ => not_supported!("Non ADT record literal"), @@ -487,9 +573,11 @@ impl MirLowerCtx<'_> { let mut operands = vec![None; variant_data.fields().len()]; for RecordLitField { name, expr } in fields.iter() { let field_id = - variant_data.field(name).ok_or(MirLowerError::UnresolvedName)?; - let op; - (op, current) = self.lower_expr_to_some_operand(*expr, current)?; + variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?; + let Some((op, c)) = self.lower_expr_to_some_operand(*expr, current)? else { + return Ok(None); + }; + current = c; operands[u32::from(field_id.into_raw()) as usize] = Some(op); } self.push_assignment( @@ -501,15 +589,16 @@ impl MirLowerCtx<'_> { MirLowerError::TypeError("missing field in record literal"), )?, ), + expr_id.into(), ); - Ok(current) + Ok(Some(current)) } VariantId::UnionId(union_id) => { let [RecordLitField { name, expr }] = fields.as_ref() else { not_supported!("Union record literal with more than one field"); }; let local_id = - variant_data.field(name).ok_or(MirLowerError::UnresolvedName)?; + variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?; let mut place = place; place .projection @@ -518,23 +607,6 @@ impl MirLowerCtx<'_> { } } } - Expr::Field { expr, name } => { - let (mut current_place, current) = self.lower_expr_to_some_place(*expr, current)?; - if let TyKind::Tuple(..) = self.expr_ty(*expr).kind(Interner) { - let index = name - .as_tuple_index() - .ok_or(MirLowerError::TypeError("named field on tuple"))?; - current_place.projection.push(ProjectionElem::TupleField(index)) - } else { - let field = self - .infer - .field_resolution(expr_id) - .ok_or(MirLowerError::UnresolvedName)?; - current_place.projection.push(ProjectionElem::Field(field)); - } - self.push_assignment(current, place, Operand::Copy(current_place).into()); - Ok(current) - } Expr::Await { .. } => not_supported!("await"), Expr::Try { .. } => not_supported!("? operator"), Expr::Yeet { .. } => not_supported!("yeet"), @@ -542,41 +614,51 @@ impl MirLowerCtx<'_> { Expr::Async { .. } => not_supported!("async block"), Expr::Const { .. } => not_supported!("anonymous const block"), Expr::Cast { expr, type_ref: _ } => { - let (x, current) = self.lower_expr_to_some_operand(*expr, current)?; + let Some((x, current)) = self.lower_expr_to_some_operand(*expr, current)? else { + return Ok(None); + }; let source_ty = self.infer[*expr].clone(); let target_ty = self.infer[expr_id].clone(); self.push_assignment( current, place, Rvalue::Cast(cast_kind(&source_ty, &target_ty)?, x, target_ty), + expr_id.into(), ); - Ok(current) + Ok(Some(current)) } Expr::Ref { expr, rawness: _, mutability } => { - let p; - (p, current) = self.lower_expr_to_some_place(*expr, current)?; + let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else { + return Ok(None); + }; let bk = BorrowKind::from_hir(*mutability); - self.push_assignment(current, place, Rvalue::Ref(bk, p)); - Ok(current) + self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into()); + Ok(Some(current)) } Expr::Box { .. } => not_supported!("box expression"), - Expr::UnaryOp { expr, op } => match op { - hir_def::expr::UnaryOp::Deref => { - let (mut tmp, current) = self.lower_expr_to_some_place(*expr, current)?; - tmp.projection.push(ProjectionElem::Deref); - self.push_assignment(current, place, Operand::Copy(tmp).into()); - Ok(current) - } - hir_def::expr::UnaryOp::Not => { - let (op, current) = self.lower_expr_to_some_operand(*expr, current)?; - self.push_assignment(current, place, Rvalue::UnaryOp(UnOp::Not, op)); - Ok(current) - } - hir_def::expr::UnaryOp::Neg => { - let (op, current) = self.lower_expr_to_some_operand(*expr, current)?; - self.push_assignment(current, place, Rvalue::UnaryOp(UnOp::Neg, op)); - Ok(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)?; @@ -584,18 +666,23 @@ 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; - (rhs_op, current) = self.lower_expr_to_some_operand(*rhs, current)?; - self.push_assignment(current, lhs_place, rhs_op.into()); - return Ok(current); + 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; - (lhs_op, current) = self.lower_expr_to_some_operand(*lhs, current)?; - let rhs_op; - (rhs_op, current) = self.lower_expr_to_some_operand(*rhs, current)?; + let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)? else { + return Ok(None); + }; + let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else { + return Ok(None); + }; self.push_assignment( current, place, @@ -612,34 +699,32 @@ impl MirLowerCtx<'_> { lhs_op, rhs_op, ), + expr_id.into(), ); - Ok(current) + Ok(Some(current)) } Expr::Range { .. } => not_supported!("range"), - Expr::Index { base, index } => { - let mut p_base; - (p_base, current) = self.lower_expr_to_some_place(*base, current)?; - let l_index = self.temp(self.expr_ty(*index))?; - current = self.lower_expr_to_place(*index, l_index.into(), current)?; - p_base.projection.push(ProjectionElem::Index(l_index)); - self.push_assignment(current, place, Operand::Copy(p_base).into()); - Ok(current) - } Expr::Closure { .. } => not_supported!("closure"), Expr::Tuple { exprs, is_assignee_expr: _ } => { - let r = Rvalue::Aggregate( - AggregateKind::Tuple(self.expr_ty(expr_id)), - exprs + let Some(values) = exprs .iter() .map(|x| { - let o; - (o, current) = self.lower_expr_to_some_operand(*x, current)?; - Ok(o) + let Some((o, c)) = self.lower_expr_to_some_operand(*x, current)? else { + return Ok(None); + }; + current = c; + Ok(Some(o)) }) - .collect::<Result<_>>()?, + .collect::<Result<Option<_>>>()? + else { + return Ok(None); + }; + let r = Rvalue::Aggregate( + AggregateKind::Tuple(self.expr_ty(expr_id)), + values, ); - self.push_assignment(current, place, r); - Ok(current) + self.push_assignment(current, place, r, expr_id.into()); + Ok(Some(current)) } Expr::Array(l) => match l { Array::ElementList { elements, .. } => { @@ -651,86 +736,54 @@ impl MirLowerCtx<'_> { )) } }; - let r = Rvalue::Aggregate( - AggregateKind::Array(elem_ty), - elements + let Some(values) = elements .iter() .map(|x| { - let o; - (o, current) = self.lower_expr_to_some_operand(*x, current)?; - Ok(o) + let Some((o, c)) = self.lower_expr_to_some_operand(*x, current)? else { + return Ok(None); + }; + current = c; + Ok(Some(o)) }) - .collect::<Result<_>>()?, + .collect::<Result<Option<_>>>()? + else { + return Ok(None); + }; + let r = Rvalue::Aggregate( + AggregateKind::Array(elem_ty), + values, ); - self.push_assignment(current, place, r); - Ok(current) + self.push_assignment(current, place, r, expr_id.into()); + Ok(Some(current)) } Array::Repeat { .. } => not_supported!("array repeat"), }, Expr::Literal(l) => { let ty = self.expr_ty(expr_id); let op = self.lower_literal_to_operand(ty, l)?; - self.push_assignment(current, place, op.into()); - Ok(current) + self.push_assignment(current, place, op.into(), expr_id.into()); + Ok(Some(current)) } Expr::Underscore => not_supported!("underscore"), } } - 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)?; - } + fn push_field_projection(&self, place: &mut Place, expr_id: ExprId) -> Result<()> { + if let Expr::Field { expr, name } = &self.body[expr_id] { + if let TyKind::Tuple(..) = self.expr_ty_after_adjustments(*expr).kind(Interner) { + let index = name + .as_tuple_index() + .ok_or(MirLowerError::TypeError("named field on tuple"))?; + place.projection.push(ProjectionElem::TupleField(index)) + } else { + let field = + self.infer.field_resolution(expr_id).ok_or(MirLowerError::UnresolvedField)?; + place.projection.push(ProjectionElem::Field(field)); } + } else { + not_supported!("") } - match tail { - Some(tail) => self.lower_expr_to_place(tail, place, current), - None => Ok(current), - } + Ok(()) } fn lower_literal_to_operand(&mut self, ty: Ty, l: &Literal) -> Result<Operand> { @@ -779,9 +832,10 @@ impl MirLowerCtx<'_> { const_id: hir_def::ConstId, prev_block: BasicBlockId, place: Place, - ) -> Result<BasicBlockId> { + span: MirSpan, + ) -> Result<()> { let c = self.db.const_eval(const_id)?; - self.write_const_to_place(c, prev_block, place) + self.write_const_to_place(c, prev_block, place, span) } fn write_const_to_place( @@ -789,9 +843,10 @@ impl MirLowerCtx<'_> { c: Const, prev_block: BasicBlockId, place: Place, - ) -> Result<BasicBlockId> { - self.push_assignment(prev_block, place, Operand::Constant(c).into()); - Ok(prev_block) + span: MirSpan, + ) -> Result<()> { + self.push_assignment(prev_block, place, Operand::Constant(c).into(), span); + Ok(()) } fn write_bytes_to_place( @@ -800,9 +855,10 @@ impl MirLowerCtx<'_> { place: Place, cv: Vec<u8>, ty: Ty, - ) -> Result<BasicBlockId> { - self.push_assignment(prev_block, place, Operand::from_bytes(cv, ty).into()); - Ok(prev_block) + span: MirSpan, + ) -> Result<()> { + self.push_assignment(prev_block, place, Operand::from_bytes(cv, ty).into(), span); + Ok(()) } fn lower_enum_variant( @@ -812,6 +868,7 @@ impl MirLowerCtx<'_> { place: Place, ty: Ty, fields: Vec<Operand>, + span: MirSpan, ) -> Result<BasicBlockId> { let subst = match ty.kind(Interner) { TyKind::Adt(_, subst) => subst.clone(), @@ -821,36 +878,51 @@ impl MirLowerCtx<'_> { prev_block, place, Rvalue::Aggregate(AggregateKind::Adt(variant_id.into(), subst), fields), + span, ); Ok(prev_block) } - fn lower_call( + fn lower_call_and_args( &mut self, func: Operand, args: impl Iterator<Item = ExprId>, place: Place, mut current: BasicBlockId, - ) -> Result<BasicBlockId> { - let args = args + is_uninhabited: bool, + ) -> Result<Option<BasicBlockId>> { + let Some(args) = args .map(|arg| { - let temp; - (temp, current) = self.lower_expr_to_some_operand(arg, current)?; - Ok(temp) + if let Some((temp, c)) = self.lower_expr_to_some_operand(arg, current)? { + current = c; + Ok(Some(temp)) + } else { + Ok(None) + } }) - .collect::<Result<Vec<_>>>()?; - let b = self.result.basic_blocks.alloc(BasicBlock { - statements: vec![], - terminator: None, - is_cleanup: false, - }); + .collect::<Result<Option<Vec<_>>>>()? + else { + return Ok(None); + }; + self.lower_call(func, args, place, current, is_uninhabited) + } + + fn lower_call( + &mut self, + func: Operand, + args: Vec<Operand>, + place: Place, + current: BasicBlockId, + is_uninhabited: bool, + ) -> Result<Option<BasicBlockId>> { + let b = if is_uninhabited { None } else { Some(self.new_basic_block()) }; self.set_terminator( current, Terminator::Call { func, args, destination: place, - target: Some(b), + target: b, cleanup: None, from_hir_call: true, }, @@ -874,8 +946,28 @@ impl MirLowerCtx<'_> { self.infer[e].clone() } - fn push_assignment(&mut self, block: BasicBlockId, place: Place, rvalue: Rvalue) { - self.result.basic_blocks[block].statements.push(Statement::Assign(place, rvalue)); + fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty { + let mut ty = None; + if let Some(x) = self.infer.expr_adjustments.get(&e) { + if let Some(x) = x.last() { + ty = Some(x.target.clone()); + } + } + ty.unwrap_or_else(|| self.expr_ty(e)) + } + + fn push_statement(&mut self, block: BasicBlockId, statement: Statement) { + self.result.basic_blocks[block].statements.push(statement); + } + + fn push_assignment( + &mut self, + block: BasicBlockId, + place: Place, + rvalue: Rvalue, + span: MirSpan, + ) { + self.push_statement(block, StatementKind::Assign(place, rvalue).with_span(span)); } /// It gets a `current` unterminated block, appends some statements and possibly a terminator to it to check if @@ -924,11 +1016,50 @@ impl MirLowerCtx<'_> { binding_mode, )? } - Pat::Or(_) => not_supported!("or pattern"), + Pat::Or(pats) => { + let then_target = self.new_basic_block(); + let mut finished = false; + for pat in &**pats { + let (next, next_else) = self.pattern_match( + current, + None, + cond_place.clone(), + cond_ty.clone(), + *pat, + binding_mode, + )?; + self.set_goto(next, then_target); + match next_else { + Some(t) => { + current = t; + } + None => { + finished = true; + break; + } + } + } + (then_target, (!finished).then_some(current)) + } Pat::Record { .. } => not_supported!("record pattern"), Pat::Range { .. } => not_supported!("range pattern"), Pat::Slice { .. } => not_supported!("slice pattern"), - Pat::Path(_) => not_supported!("path pattern"), + Pat::Path(_) => { + let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else { + not_supported!("unresolved variant"); + }; + self.pattern_matching_variant( + cond_ty, + binding_mode, + cond_place, + variant, + current, + pattern.into(), + current_else, + &[], + &None, + )? + } Pat::Lit(l) => { let then_target = self.new_basic_block(); let else_target = current_else.unwrap_or_else(|| self.new_basic_block()); @@ -962,8 +1093,9 @@ impl MirLowerCtx<'_> { } (then_target, Some(else_target)) } - Pat::Bind { mode, name: _, subpat } => { - let target_place = self.binding_locals[pattern]; + Pat::Bind { id, subpat } => { + let target_place = self.result.binding_locals[*id]; + let mode = self.body.bindings[*id].mode; if let Some(subpat) = subpat { (current, current_else) = self.pattern_match( current, @@ -975,8 +1107,9 @@ impl MirLowerCtx<'_> { )? } if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) { - binding_mode = *mode; + binding_mode = mode; } + self.push_storage_live(*id, current)?; self.push_assignment( current, target_place.into(), @@ -990,6 +1123,7 @@ impl MirLowerCtx<'_> { cond_place, ), }, + pattern.into(), ); (current, current_else) } @@ -997,74 +1131,17 @@ impl MirLowerCtx<'_> { let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else { not_supported!("unresolved variant"); }; - pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place); - let subst = match cond_ty.kind(Interner) { - TyKind::Adt(_, s) => s, - _ => { - return Err(MirLowerError::TypeError( - "non adt type matched with tuple struct", - )) - } - }; - let fields_type = self.db.field_types(variant); - match variant { - VariantId::EnumVariantId(v) => { - let e = self.db.const_eval_discriminant(v)? as u128; - let next = self.new_basic_block(); - let tmp = self.discr_temp_place(); - self.push_assignment( - current, - tmp.clone(), - Rvalue::Discriminant(cond_place.clone()), - ); - let else_target = current_else.unwrap_or_else(|| self.new_basic_block()); - self.set_terminator( - current, - Terminator::SwitchInt { - discr: Operand::Copy(tmp), - targets: SwitchTargets::static_if(e, next, else_target), - }, - ); - let enum_data = self.db.enum_data(v.parent); - let fields = - enum_data.variants[v.local_id].variant_data.fields().iter().map( - |(x, _)| { - ( - PlaceElem::Field(FieldId { parent: v.into(), local_id: x }), - fields_type[x].clone().substitute(Interner, subst), - ) - }, - ); - self.pattern_match_tuple_like( - next, - Some(else_target), - args.iter().zip(fields).map(|(x, y)| (y.0, *x, y.1)), - *ellipsis, - &cond_place, - binding_mode, - )? - } - VariantId::StructId(s) => { - let struct_data = self.db.struct_data(s); - let fields = struct_data.variant_data.fields().iter().map(|(x, _)| { - ( - PlaceElem::Field(FieldId { parent: s.into(), local_id: x }), - fields_type[x].clone().substitute(Interner, subst), - ) - }); - self.pattern_match_tuple_like( - current, - current_else, - args.iter().zip(fields).map(|(x, y)| (y.0, *x, y.1)), - *ellipsis, - &cond_place, - binding_mode, - )? - } - VariantId::UnionId(_) => { - return Err(MirLowerError::TypeError("pattern matching on union")) - } - } + self.pattern_matching_variant( + cond_ty, + binding_mode, + cond_place, + variant, + current, + pattern.into(), + current_else, + args, + ellipsis, + )? } Pat::Ref { .. } => not_supported!("& pattern"), Pat::Box { .. } => not_supported!("box pattern"), @@ -1072,6 +1149,83 @@ impl MirLowerCtx<'_> { }) } + fn pattern_matching_variant( + &mut self, + mut cond_ty: Ty, + mut binding_mode: BindingAnnotation, + mut cond_place: Place, + variant: VariantId, + current: BasicBlockId, + span: MirSpan, + current_else: Option<BasicBlockId>, + args: &[PatId], + ellipsis: &Option<usize>, + ) -> Result<(BasicBlockId, Option<BasicBlockId>)> { + pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place); + let subst = match cond_ty.kind(Interner) { + TyKind::Adt(_, s) => s, + _ => return Err(MirLowerError::TypeError("non adt type matched with tuple struct")), + }; + let fields_type = self.db.field_types(variant); + Ok(match variant { + VariantId::EnumVariantId(v) => { + let e = self.db.const_eval_discriminant(v)? as u128; + let next = self.new_basic_block(); + let tmp = self.discr_temp_place(); + self.push_assignment( + current, + tmp.clone(), + Rvalue::Discriminant(cond_place.clone()), + span, + ); + let else_target = current_else.unwrap_or_else(|| self.new_basic_block()); + self.set_terminator( + current, + Terminator::SwitchInt { + discr: Operand::Copy(tmp), + targets: SwitchTargets::static_if(e, next, else_target), + }, + ); + let enum_data = self.db.enum_data(v.parent); + let fields = + enum_data.variants[v.local_id].variant_data.fields().iter().map(|(x, _)| { + ( + PlaceElem::Field(FieldId { parent: v.into(), local_id: x }), + fields_type[x].clone().substitute(Interner, subst), + ) + }); + self.pattern_match_tuple_like( + next, + Some(else_target), + args.iter().zip(fields).map(|(x, y)| (y.0, *x, y.1)), + *ellipsis, + &cond_place, + binding_mode, + )? + } + VariantId::StructId(s) => { + let struct_data = self.db.struct_data(s); + let fields = struct_data.variant_data.fields().iter().map(|(x, _)| { + ( + PlaceElem::Field(FieldId { parent: s.into(), local_id: x }), + fields_type[x].clone().substitute(Interner, subst), + ) + }); + self.pattern_match_tuple_like( + current, + current_else, + args.iter().zip(fields).map(|(x, y)| (y.0, *x, y.1)), + *ellipsis, + &cond_place, + binding_mode, + )? + } + VariantId::UnionId(_) => { + return Err(MirLowerError::TypeError("pattern matching on union")) + } + }) + } + fn pattern_match_tuple_like( &mut self, mut current: BasicBlockId, @@ -1109,23 +1263,161 @@ impl MirLowerCtx<'_> { &mut self, prev_block: BasicBlockId, label: Option<LabelId>, - f: impl FnOnce(&mut MirLowerCtx<'_>, BasicBlockId, BasicBlockId) -> Result<()>, - ) -> Result<BasicBlockId> { + f: impl FnOnce(&mut MirLowerCtx<'_>, BasicBlockId) -> Result<()>, + ) -> Result<Option<BasicBlockId>> { if label.is_some() { not_supported!("loop with label"); } let begin = self.new_basic_block(); - let end = self.new_basic_block(); - let prev = mem::replace(&mut self.current_loop_blocks, Some(LoopBlocks { begin, end })); + let prev = + mem::replace(&mut self.current_loop_blocks, Some(LoopBlocks { begin, end: None })); self.set_goto(prev_block, begin); - f(self, begin, end)?; - self.current_loop_blocks = prev; - Ok(end) + f(self, begin)?; + let my = mem::replace(&mut self.current_loop_blocks, prev) + .ok_or(MirLowerError::ImplementationError("current_loop_blocks is corrupt"))?; + Ok(my.end) } fn has_adjustments(&self, expr_id: ExprId) -> bool { !self.infer.expr_adjustments.get(&expr_id).map(|x| x.is_empty()).unwrap_or(true) } + + fn merge_blocks( + &mut self, + b1: Option<BasicBlockId>, + b2: Option<BasicBlockId>, + ) -> Option<BasicBlockId> { + match (b1, b2) { + (None, None) => None, + (None, Some(b)) | (Some(b), None) => Some(b), + (Some(b1), Some(b2)) => { + let bm = self.new_basic_block(); + self.set_goto(b1, bm); + self.set_goto(b2, bm); + Some(bm) + } + } + } + + fn current_loop_end(&mut self) -> Result<BasicBlockId> { + let r = match self + .current_loop_blocks + .as_mut() + .ok_or(MirLowerError::ImplementationError("Current loop access out of loop"))? + .end + { + Some(x) => x, + None => { + let s = self.new_basic_block(); + self.current_loop_blocks + .as_mut() + .ok_or(MirLowerError::ImplementationError("Current loop access out of loop"))? + .end = Some(s); + s + } + }; + Ok(r) + } + + fn is_uninhabited(&self, expr_id: ExprId) -> bool { + is_ty_uninhabited_from(&self.infer[expr_id], self.owner.module(self.db.upcast()), self.db) + } + + /// This function push `StorageLive` statements for each binding in the pattern. + 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 + // allow this: + // + // ``` + // let x; + // loop { + // let y = 2; + // x = &y; + // if some_condition { + // break; // we need to add a StorageDead(y) above this to kill the x borrow + // } + // } + // use(x) + // ``` + // 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 = 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(()) + } + + fn resolve_lang_item(&self, item: LangItem) -> Result<LangItemTarget> { + 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( @@ -1161,9 +1453,20 @@ 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 _p = profile::span("mir_body_query").detail(|| match def { + DefWithBodyId::FunctionId(it) => db.function_data(it).name.to_string(), + DefWithBodyId::StaticId(it) => db.static_data(it).name.clone().to_string(), + DefWithBodyId::ConstId(it) => { + db.const_data(it).name.clone().unwrap_or_else(Name::missing).to_string() + } + DefWithBodyId::VariantId(it) => { + db.enum_data(it.parent).variants[it.local_id].name.to_string() + } + }); 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( @@ -1183,37 +1486,88 @@ pub fn lower_to_mir( // need to take this input explicitly. root_expr: ExprId, ) -> Result<MirBody> { + if let Some((_, x)) = infer.type_mismatches().next() { + return Err(MirLowerError::TypeMismatch(x.clone())); + } let mut basic_blocks = Arena::new(); let start_block = basic_blocks.alloc(BasicBlock { statements: vec![], terminator: None, is_cleanup: false }); let mut locals = Arena::new(); // 0 is return local - locals.alloc(Local { mutability: Mutability::Mut, ty: infer[root_expr].clone() }); - let mut create_local_of_path = |p: PatId| { - // FIXME: mutablity is broken - locals.alloc(Local { mutability: Mutability::Not, ty: infer[p].clone() }) - }; + locals.alloc(Local { ty: infer[root_expr].clone() }); + let mut binding_locals: ArenaMap<BindingId, LocalId> = ArenaMap::new(); // 1 to param_len is for params - let mut binding_locals: ArenaMap<PatId, LocalId> = - body.params.iter().map(|&x| (x, create_local_of_path(x))).collect(); + let param_locals: Vec<LocalId> = if let DefWithBodyId::FunctionId(fid) = owner { + let substs = TyBuilder::placeholder_subst(db, fid); + let callable_sig = db.callable_item_signature(fid.into()).substitute(Interner, &substs); + body.params + .iter() + .zip(callable_sig.params().iter()) + .map(|(&x, ty)| { + let local_id = locals.alloc(Local { ty: ty.clone() }); + if let Pat::Bind { id, subpat: None } = body[x] { + if matches!( + body.bindings[id].mode, + BindingAnnotation::Unannotated | BindingAnnotation::Mutable + ) { + binding_locals.insert(id, local_id); + } + } + local_id + }) + .collect() + } else { + if !body.params.is_empty() { + return Err(MirLowerError::TypeError("Unexpected parameter for non function body")); + } + vec![] + }; // and then rest of bindings - for (pat_id, _) in body.pats.iter() { - if !binding_locals.contains_idx(pat_id) { - binding_locals.insert(pat_id, create_local_of_path(pat_id)); + for (id, _) in body.bindings.iter() { + if !binding_locals.contains_idx(id) { + binding_locals.insert(id, locals.alloc(Local { ty: infer[id].clone() })); } } - let mir = MirBody { basic_blocks, locals, start_block, owner, arg_count: body.params.len() }; + let mir = MirBody { + basic_blocks, + locals, + start_block, + binding_locals, + param_locals, + owner, + arg_count: body.params.len(), + }; let mut ctx = MirLowerCtx { result: mir, db, infer, body, - binding_locals, owner, current_loop_blocks: None, discr_temp: None, }; - let b = ctx.lower_expr_to_place(root_expr, return_slot().into(), start_block)?; - ctx.result.basic_blocks[b].terminator = Some(Terminator::Return); + let mut current = start_block; + for (¶m, local) in body.params.iter().zip(ctx.result.param_locals.clone().into_iter()) { + if let Pat::Bind { id, .. } = body[param] { + if local == ctx.result.binding_locals[id] { + continue; + } + } + let r = ctx.pattern_match( + current, + None, + local.into(), + ctx.result.locals[local].ty.clone(), + param, + BindingAnnotation::Unannotated, + )?; + if let Some(b) = r.1 { + ctx.set_terminator(b, Terminator::Unreachable); + } + current = r.0; + } + if let Some(b) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? { + ctx.result.basic_blocks[b].terminator = Some(Terminator::Return); + } Ok(ctx.result) } |