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.rs261
1 files changed, 154 insertions, 107 deletions
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index 66b1d840bd..a37e5e8168 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -14,7 +14,8 @@ use hir_def::{
lang_item::{LangItem, LangItemTarget},
path::Path,
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
- AdtId, DefWithBodyId, EnumVariantId, HasModule, ItemContainerId, LocalFieldId, TraitId,
+ AdtId, DefWithBodyId, EnumVariantId, GeneralConstId, HasModule, ItemContainerId, LocalFieldId,
+ TraitId,
};
use hir_expand::name::Name;
use la_arena::ArenaMap;
@@ -30,7 +31,6 @@ use crate::{
inhabitedness::is_ty_uninhabited_from,
layout::{layout_of_ty, LayoutError},
mapping::ToChalk,
- method_resolution::lookup_impl_const,
static_lifetime,
utils::{generics, ClosureSubst},
Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt,
@@ -51,17 +51,22 @@ struct LoopBlocks {
place: Place,
}
+#[derive(Debug, Clone, Default)]
+struct DropScope {
+ /// locals, in order of definition (so we should run drop glues in reverse order)
+ locals: Vec<LocalId>,
+}
+
struct MirLowerCtx<'a> {
result: MirBody,
owner: DefWithBodyId,
current_loop_blocks: Option<LoopBlocks>,
- // FIXME: we should resolve labels in HIR lowering and always work with label id here, not
- // with raw names.
labeled_loop_blocks: FxHashMap<LabelId, LoopBlocks>,
discr_temp: Option<Place>,
db: &'a dyn HirDatabase,
body: &'a Body,
infer: &'a InferenceResult,
+ drop_scopes: Vec<DropScope>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -181,7 +186,6 @@ impl<'ctx> MirLowerCtx<'ctx> {
binding_locals,
param_locals: vec![],
owner,
- arg_count: body.params.len(),
closures: vec![],
};
let ctx = MirLowerCtx {
@@ -193,15 +197,18 @@ impl<'ctx> MirLowerCtx<'ctx> {
current_loop_blocks: None,
labeled_loop_blocks: Default::default(),
discr_temp: None,
+ drop_scopes: vec![DropScope::default()],
};
ctx
}
- fn temp(&mut self, ty: Ty) -> Result<LocalId> {
+ fn temp(&mut self, ty: Ty, current: BasicBlockId, span: MirSpan) -> Result<LocalId> {
if matches!(ty.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) {
implementation_error!("unsized temporaries");
}
- Ok(self.result.locals.alloc(Local { ty }))
+ let l = self.result.locals.alloc(Local { ty });
+ self.push_storage_live_for_local(l, current, span)?;
+ Ok(l)
}
fn lower_expr_to_some_operand(
@@ -234,7 +241,8 @@ impl<'ctx> MirLowerCtx<'ctx> {
match adjustments.split_last() {
Some((last, rest)) => match &last.kind {
Adjust::NeverToAny => {
- let temp = self.temp(TyKind::Never.intern(Interner))?;
+ let temp =
+ self.temp(TyKind::Never.intern(Interner), current, MirSpan::Unknown)?;
self.lower_expr_to_place_with_adjust(expr_id, temp.into(), current, rest)
}
Adjust::Deref(_) => {
@@ -303,45 +311,39 @@ impl<'ctx> MirLowerCtx<'ctx> {
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)
- .ok_or_else(unresolved_name)?;
- let pr = match pr {
- ResolveValueResult::ValueNs(v) => v,
- ResolveValueResult::Partial(..) => {
- if let Some((assoc, subst)) = self
- .infer
- .assoc_resolutions_for_expr(expr_id)
- {
- match assoc {
- hir_def::AssocItemId::ConstId(c) => {
- self.lower_const(c, current, place, subst, expr_id.into(), self.expr_ty_without_adjust(expr_id))?;
- return Ok(Some(current))
- },
- hir_def::AssocItemId::FunctionId(_) => {
- // FnDefs are zero sized, no action is needed.
- return Ok(Some(current))
- }
- hir_def::AssocItemId::TypeAliasId(_) => {
- // FIXME: If it is unreachable, use proper error instead of `not_supported`.
- 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());
+ let pr = if let Some((assoc, subst)) = self
+ .infer
+ .assoc_resolutions_for_expr(expr_id)
+ {
+ match assoc {
+ hir_def::AssocItemId::ConstId(c) => {
+ self.lower_const(c.into(), current, place, subst, expr_id.into(), self.expr_ty_without_adjust(expr_id))?;
+ return Ok(Some(current))
+ },
+ hir_def::AssocItemId::FunctionId(_) => {
+ // FnDefs are zero sized, no action is needed.
+ return Ok(Some(current))
}
+ hir_def::AssocItemId::TypeAliasId(_) => {
+ // FIXME: If it is unreachable, use proper error instead of `not_supported`.
+ 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 {
+ let unresolved_name = || MirLowerError::unresolved_path(self.db, p);
+ let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
+ resolver
+ .resolve_path_in_value_ns_fully(self.db.upcast(), p)
+ .ok_or_else(unresolved_name)?
};
match pr {
ValueNs::LocalBinding(_) | ValueNs::StaticId(_) => {
@@ -357,7 +359,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
Ok(Some(current))
}
ValueNs::ConstId(const_id) => {
- self.lower_const(const_id, current, place, Substitution::empty(Interner), expr_id.into(), self.expr_ty_without_adjust(expr_id))?;
+ self.lower_const(const_id.into(), current, place, Substitution::empty(Interner), expr_id.into(), self.expr_ty_without_adjust(expr_id))?;
Ok(Some(current))
}
ValueNs::EnumVariantId(variant_id) => {
@@ -470,9 +472,10 @@ impl<'ctx> MirLowerCtx<'ctx> {
Expr::Block { id: _, statements, tail, label } => {
if let Some(label) = label {
self.lower_loop(current, place.clone(), Some(*label), expr_id.into(), |this, begin| {
- if let Some(block) = this.lower_block_to_place(statements, begin, *tail, place, expr_id.into())? {
+ if let Some(current) = this.lower_block_to_place(statements, begin, *tail, place, expr_id.into())? {
let end = this.current_loop_end()?;
- this.set_goto(block, end, expr_id.into());
+ let current = this.pop_drop_scope(current);
+ this.set_goto(current, end, expr_id.into());
}
Ok(())
})
@@ -481,8 +484,9 @@ impl<'ctx> MirLowerCtx<'ctx> {
}
}
Expr::Loop { body, label } => self.lower_loop(current, place, *label, expr_id.into(), |this, begin| {
- if let Some((_, block)) = this.lower_expr_as_place(begin, *body, true)? {
- this.set_goto(block, begin, expr_id.into());
+ if let Some((_, current)) = this.lower_expr_as_place(begin, *body, true)? {
+ let current = this.pop_drop_scope(current);
+ this.set_goto(current, begin, expr_id.into());
}
Ok(())
}),
@@ -502,6 +506,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
expr_id.into(),
);
if let Some((_, block)) = this.lower_expr_as_place(after_cond, *body, true)? {
+ let block = this.pop_drop_scope(block);
this.set_goto(block, begin, expr_id.into());
}
Ok(())
@@ -531,9 +536,9 @@ impl<'ctx> MirLowerCtx<'ctx> {
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 iterator_place: Place = self.temp(iterator_ty.clone(), current, expr_id.into())?.into();
+ let option_item_place: Place = self.temp(option_item_ty.clone(), current, expr_id.into())?.into();
+ let ref_mut_iterator_place: Place = self.temp(ref_mut_iterator_ty, current, expr_id.into())?.into();
let Some(current) = self.lower_call_and_args(into_iter_fn_op, Some(iterable).into_iter(), iterator_place.clone(), current, false, expr_id.into())?
else {
return Ok(None);
@@ -556,6 +561,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
AdtPatternShape::Tuple { args: &[pat], ellipsis: None },
)?;
if let Some((_, block)) = this.lower_expr_as_place(current, body, true)? {
+ let block = this.pop_drop_scope(block);
this.set_goto(block, begin, expr_id.into());
}
Ok(())
@@ -686,6 +692,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
return Ok(None);
}
}
+ current = self.drop_until_scope(0, current);
self.set_terminator(current, TerminatorKind::Return, expr_id.into());
Ok(None)
}
@@ -770,7 +777,11 @@ impl<'ctx> MirLowerCtx<'ctx> {
Expr::Await { .. } => not_supported!("await"),
Expr::Yeet { .. } => not_supported!("yeet"),
Expr::Async { .. } => not_supported!("async block"),
- Expr::Const { .. } => not_supported!("anonymous const block"),
+ &Expr::Const(id) => {
+ let subst = self.placeholder_subst();
+ self.lower_const(id.into(), current, place, subst, expr_id.into(), self.expr_ty_without_adjust(expr_id))?;
+ Ok(Some(current))
+ },
Expr::Cast { expr, type_ref: _ } => {
let Some((x, current)) = self.lower_expr_to_some_operand(*expr, current)? else {
return Ok(None);
@@ -830,11 +841,16 @@ impl<'ctx> MirLowerCtx<'ctx> {
},
Expr::BinaryOp { lhs, rhs, op } => {
let op = op.ok_or(MirLowerError::IncompleteExpr)?;
- let is_builtin = {
+ let is_builtin = 'b: {
// Without adjust here is a hack. We assume that we know every possible adjustment
// for binary operator, and use without adjust to simplify our conditions.
let lhs_ty = self.expr_ty_without_adjust(*lhs);
let rhs_ty = self.expr_ty_without_adjust(*rhs);
+ if matches!(op ,BinaryOp::CmpOp(syntax::ast::CmpOp::Eq { .. })) {
+ if lhs_ty.as_raw_ptr().is_some() && rhs_ty.as_raw_ptr().is_some() {
+ break 'b true;
+ }
+ }
let builtin_inequal_impls = matches!(
op,
BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr) | BinaryOp::Assignment { op: Some(ArithOp::Shl | ArithOp::Shr) }
@@ -973,8 +989,8 @@ impl<'ctx> MirLowerCtx<'ctx> {
ProjectionElem::Deref => ProjectionElem::Deref,
ProjectionElem::Field(x) => ProjectionElem::Field(x),
ProjectionElem::TupleOrClosureField(x) => ProjectionElem::TupleOrClosureField(x),
- ProjectionElem::ConstantIndex { offset, min_length, from_end } => ProjectionElem::ConstantIndex { offset, min_length, from_end },
- ProjectionElem::Subslice { from, to, from_end } => ProjectionElem::Subslice { from, to, from_end },
+ ProjectionElem::ConstantIndex { offset, from_end } => ProjectionElem::ConstantIndex { offset, from_end },
+ ProjectionElem::Subslice { from, to } => ProjectionElem::Subslice { from, to },
ProjectionElem::OpaqueCast(x) => ProjectionElem::OpaqueCast(x),
ProjectionElem::Index(x) => match x { },
}
@@ -982,12 +998,9 @@ impl<'ctx> MirLowerCtx<'ctx> {
};
match &capture.kind {
CaptureKind::ByRef(bk) => {
- let placeholder_subst = match self.owner.as_generic_def_id() {
- Some(x) => TyBuilder::placeholder_subst(self.db, x),
- None => Substitution::empty(Interner),
- };
+ let placeholder_subst = self.placeholder_subst();
let tmp_ty = capture.ty.clone().substitute(Interner, &placeholder_subst);
- let tmp: Place = self.temp(tmp_ty)?.into();
+ let tmp: Place = self.temp(tmp_ty, current, capture.span)?.into();
self.push_assignment(
current,
tmp.clone(),
@@ -1085,6 +1098,14 @@ impl<'ctx> MirLowerCtx<'ctx> {
}
}
+ fn placeholder_subst(&mut self) -> Substitution {
+ let placeholder_subst = match self.owner.as_generic_def_id() {
+ Some(x) => TyBuilder::placeholder_subst(self.db, x),
+ None => Substitution::empty(Interner),
+ };
+ placeholder_subst
+ }
+
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) {
@@ -1146,7 +1167,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
fn lower_const(
&mut self,
- const_id: hir_def::ConstId,
+ const_id: GeneralConstId,
prev_block: BasicBlockId,
place: Place,
subst: Substitution,
@@ -1157,20 +1178,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
// We can't evaluate constant with substitution now, as generics are not monomorphized in lowering.
intern_const_scalar(ConstScalar::UnevaluatedConst(const_id, subst), ty)
} else {
- let (const_id, subst) = lookup_impl_const(
- self.db,
- self.db.trait_environment_for_body(self.owner),
- const_id,
- subst,
- );
- let name = self
- .db
- .const_data(const_id)
- .name
- .as_ref()
- .and_then(|x| x.as_str())
- .unwrap_or("_")
- .to_owned();
+ let name = const_id.name(self.db.upcast());
self.db
.const_eval(const_id.into(), subst)
.map_err(|e| MirLowerError::ConstEvalError(name, Box::new(e)))?
@@ -1313,12 +1321,14 @@ impl<'ctx> MirLowerCtx<'ctx> {
self.push_statement(block, StatementKind::Assign(place, rvalue).with_span(span));
}
- fn discr_temp_place(&mut self) -> Place {
+ fn discr_temp_place(&mut self, current: BasicBlockId) -> Place {
match &self.discr_temp {
Some(x) => x.clone(),
None => {
- let tmp: Place =
- self.temp(TyBuilder::discr_ty()).expect("discr_ty is never unsized").into();
+ let tmp: Place = self
+ .temp(TyBuilder::discr_ty(), current, MirSpan::Unknown)
+ .expect("discr_ty is never unsized")
+ .into();
self.discr_temp = Some(tmp.clone());
tmp
}
@@ -1349,6 +1359,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
None
};
self.set_goto(prev_block, begin, span);
+ self.push_drop_scope();
f(self, begin)?;
let my = mem::replace(&mut self.current_loop_blocks, prev).ok_or(
MirLowerError::ImplementationError("current_loop_blocks is corrupt".to_string()),
@@ -1409,27 +1420,9 @@ impl<'ctx> MirLowerCtx<'ctx> {
is_ty_uninhabited_from(&self.infer[expr_id], self.owner.module(self.db.upcast()), self.db)
}
- /// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` in
- /// the appropriated places.
+ /// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` and
+ /// `Drop` in the appropriated places.
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 unneeded 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()
@@ -1437,6 +1430,18 @@ impl<'ctx> MirLowerCtx<'ctx> {
.map(MirSpan::PatId)
.unwrap_or(MirSpan::Unknown);
let l = self.binding_local(b)?;
+ self.push_storage_live_for_local(l, current, span)
+ }
+
+ fn push_storage_live_for_local(
+ &mut self,
+ l: LocalId,
+ current: BasicBlockId,
+ span: MirSpan,
+ ) -> Result<()> {
+ self.drop_scopes.last_mut().unwrap().locals.push(l);
+ // FIXME: this storage dead is not neccessary, but since drop scope handling is broken, we need
+ // it to avoid falso positives in mutability errors
self.push_statement(current, StatementKind::StorageDead(l).with_span(span));
self.push_statement(current, StatementKind::StorageLive(l).with_span(span));
Ok(())
@@ -1500,10 +1505,11 @@ impl<'ctx> MirLowerCtx<'ctx> {
}
}
hir_def::hir::Statement::Expr { expr, has_semi: _ } => {
+ self.push_drop_scope();
let Some((_, c)) = self.lower_expr_as_place(current, *expr, true)? else {
return Ok(None);
};
- current = c;
+ current = self.pop_drop_scope(c);
}
}
}
@@ -1521,6 +1527,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
let base_param_count = self.result.param_locals.len();
self.result.param_locals.extend(params.clone().map(|(x, ty)| {
let local_id = self.result.locals.alloc(Local { ty });
+ self.drop_scopes.last_mut().unwrap().locals.push(local_id);
if let Pat::Bind { id, subpat: None } = self.body[x] {
if matches!(
self.body.bindings[id].mode,
@@ -1590,6 +1597,44 @@ impl<'ctx> MirLowerCtx<'ctx> {
}
}
}
+
+ fn drop_until_scope(&mut self, scope_index: usize, mut current: BasicBlockId) -> BasicBlockId {
+ for scope in self.drop_scopes[scope_index..].to_vec().iter().rev() {
+ self.emit_drop_and_storage_dead_for_scope(scope, &mut current);
+ }
+ current
+ }
+
+ fn push_drop_scope(&mut self) {
+ self.drop_scopes.push(DropScope::default());
+ }
+
+ fn pop_drop_scope(&mut self, mut current: BasicBlockId) -> BasicBlockId {
+ let scope = self.drop_scopes.pop().unwrap();
+ self.emit_drop_and_storage_dead_for_scope(&scope, &mut current);
+ current
+ }
+
+ fn emit_drop_and_storage_dead_for_scope(
+ &mut self,
+ scope: &DropScope,
+ current: &mut Idx<BasicBlock>,
+ ) {
+ for &l in scope.locals.iter().rev() {
+ if !self.result.locals[l].ty.clone().is_copy(self.db, self.owner) {
+ let prev = std::mem::replace(current, self.new_basic_block());
+ self.set_terminator(
+ prev,
+ TerminatorKind::Drop { place: l.into(), target: *current, unwind: None },
+ MirSpan::Unknown,
+ );
+ }
+ self.push_statement(
+ *current,
+ StatementKind::StorageDead(l).with_span(MirSpan::Unknown),
+ );
+ }
+ }
}
fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
@@ -1628,10 +1673,10 @@ pub fn mir_body_for_closure_query(
};
let (captures, _) = infer.closure_info(&closure);
let mut ctx = MirLowerCtx::new(db, owner, &body, &infer);
- ctx.result.arg_count = args.len() + 1;
// 0 is return local
ctx.result.locals.alloc(Local { ty: infer[*root].clone() });
- ctx.result.locals.alloc(Local { ty: infer[expr].clone() });
+ let closure_local = ctx.result.locals.alloc(Local { ty: infer[expr].clone() });
+ ctx.result.param_locals.push(closure_local);
let Some(sig) = ClosureSubst(substs).sig_ty().callable_sig(db) else {
implementation_error!("closure has not callable sig");
};
@@ -1639,8 +1684,9 @@ pub fn mir_body_for_closure_query(
args.iter().zip(sig.params().iter()).map(|(x, y)| (*x, y.clone())),
|_| true,
)?;
- if let Some(b) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? {
- ctx.set_terminator(b, TerminatorKind::Return, (*root).into());
+ if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? {
+ let current = ctx.pop_drop_scope(current);
+ ctx.set_terminator(current, TerminatorKind::Return, (*root).into());
}
let mut upvar_map: FxHashMap<LocalId, Vec<(&CapturedItem, usize)>> = FxHashMap::default();
for (i, capture) in captures.iter().enumerate() {
@@ -1761,8 +1807,9 @@ pub fn lower_to_mir(
}
ctx.lower_params_and_bindings([].into_iter(), binding_picker)?
};
- if let Some(b) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? {
- ctx.set_terminator(b, TerminatorKind::Return, root_expr.into());
+ if let Some(current) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? {
+ let current = ctx.pop_drop_scope(current);
+ ctx.set_terminator(current, TerminatorKind::Return, root_expr.into());
}
Ok(ctx.result)
}