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.rs1206
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 (&param, 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)
}