mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-03 07:04:49 +00:00
Auto merge of #14787 - HKalbasi:mir2, r=HKalbasi
MIR episode 5 This PR inits drop support (it is very broken at this stage, some things are dropped multiple time, drop scopes are wrong, ...) and adds stdout support (`println!` doesn't work since its expansion is dummy, but `stdout().write(b"hello world\n")` works if you use `RA_SYSROOT_HACK`) for interpreting. There is no useful unit test that it can interpret yet, but it is a good sign that it didn't hit a major road block yet. In MIR lowering, it adds support for slice pattern and anonymous const blocks, and some fixes so that we can evaluate `SmolStr::new_inline` in const eval. With these changes, 57 failed mir body remains.
This commit is contained in:
commit
034d7c8537
36 changed files with 1529 additions and 357 deletions
|
@ -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)]
|
||||
|
@ -183,7 +188,6 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
binding_locals,
|
||||
param_locals: vec![],
|
||||
owner,
|
||||
arg_count: body.params.len(),
|
||||
closures: vec![],
|
||||
};
|
||||
let ctx = MirLowerCtx {
|
||||
|
@ -195,15 +199,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(_)) {
|
||||
return Err(MirLowerError::UnsizedTemporary(ty));
|
||||
}
|
||||
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(
|
||||
|
@ -236,7 +243,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(_) => {
|
||||
|
@ -305,45 +313,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(_) => {
|
||||
|
@ -359,7 +361,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) => {
|
||||
|
@ -472,9 +474,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(())
|
||||
})
|
||||
|
@ -483,8 +486,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(())
|
||||
}),
|
||||
|
@ -504,6 +508,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(())
|
||||
|
@ -533,9 +538,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);
|
||||
|
@ -558,6 +563,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(())
|
||||
|
@ -688,6 +694,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)
|
||||
}
|
||||
|
@ -772,7 +779,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);
|
||||
|
@ -832,11 +843,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) }
|
||||
|
@ -975,8 +991,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 { },
|
||||
}
|
||||
|
@ -984,12 +1000,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(),
|
||||
|
@ -1087,6 +1100,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) {
|
||||
|
@ -1148,7 +1169,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,
|
||||
|
@ -1159,20 +1180,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)))?
|
||||
|
@ -1315,12 +1323,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
|
||||
}
|
||||
|
@ -1351,6 +1361,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()),
|
||||
|
@ -1411,27 +1422,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()
|
||||
|
@ -1439,6 +1432,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(())
|
||||
|
@ -1502,10 +1507,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1523,6 +1529,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,
|
||||
|
@ -1592,6 +1599,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> {
|
||||
|
@ -1630,10 +1675,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");
|
||||
};
|
||||
|
@ -1641,8 +1686,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() {
|
||||
|
@ -1763,8 +1809,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)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue