Revert "Avoid leaking block expression values"
This reverts commit 4fef39113a
.
This commit is contained in:
parent
dab3a80f23
commit
a71a819480
20 changed files with 10709 additions and 10906 deletions
|
@ -3,7 +3,6 @@ use crate::build::ForGuard::OutsideGuard;
|
|||
use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
|
||||
use crate::thir::*;
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::middle::region;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_session::lint::builtin::UNSAFE_OP_IN_UNSAFE_FN;
|
||||
use rustc_session::lint::Level;
|
||||
|
@ -13,7 +12,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
crate fn ast_block(
|
||||
&mut self,
|
||||
destination: Place<'tcx>,
|
||||
scope: Option<region::Scope>,
|
||||
block: BasicBlock,
|
||||
ast_block: &'tcx hir::Block<'tcx>,
|
||||
source_info: SourceInfo,
|
||||
|
@ -30,10 +28,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
self.in_opt_scope(opt_destruction_scope.map(|de| (de, source_info)), move |this| {
|
||||
this.in_scope((region_scope, source_info), LintLevel::Inherited, move |this| {
|
||||
if targeted_by_break {
|
||||
this.in_breakable_scope(None, destination, scope, span, |this| {
|
||||
this.in_breakable_scope(None, destination, span, |this| {
|
||||
Some(this.ast_block_stmts(
|
||||
destination,
|
||||
scope,
|
||||
block,
|
||||
span,
|
||||
stmts,
|
||||
|
@ -42,7 +39,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
))
|
||||
})
|
||||
} else {
|
||||
this.ast_block_stmts(destination, scope, block, span, stmts, expr, safety_mode)
|
||||
this.ast_block_stmts(destination, block, span, stmts, expr, safety_mode)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -51,7 +48,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
fn ast_block_stmts(
|
||||
&mut self,
|
||||
destination: Place<'tcx>,
|
||||
scope: Option<region::Scope>,
|
||||
mut block: BasicBlock,
|
||||
span: Span,
|
||||
stmts: Vec<StmtRef<'tcx>>,
|
||||
|
@ -186,7 +182,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
};
|
||||
this.block_context.push(BlockFrame::TailExpr { tail_result_is_ignored, span });
|
||||
|
||||
unpack!(block = this.into(destination, scope, block, expr));
|
||||
unpack!(block = this.into(destination, block, expr));
|
||||
let popped = this.block_context.pop();
|
||||
|
||||
assert!(popped.map_or(false, |bf| bf.is_tail_expr()));
|
||||
|
|
|
@ -115,15 +115,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let box_ = Rvalue::NullaryOp(NullOp::Box, value.ty);
|
||||
this.cfg.push_assign(block, source_info, Place::from(result), box_);
|
||||
|
||||
// Initialize the box contents. No scope is needed since the
|
||||
// `Box` is already scheduled to be dropped.
|
||||
// initialize the box contents:
|
||||
unpack!(
|
||||
block = this.into(
|
||||
this.hir.tcx().mk_place_deref(Place::from(result)),
|
||||
None,
|
||||
block,
|
||||
value,
|
||||
)
|
||||
block =
|
||||
this.into(this.hir.tcx().mk_place_deref(Place::from(result)), block, value)
|
||||
);
|
||||
let result_operand = Operand::Move(Place::from(result));
|
||||
this.record_operands_moved(slice::from_ref(&result_operand));
|
||||
|
|
|
@ -114,7 +114,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
unpack!(block = this.into(temp_place, temp_lifetime, block, expr));
|
||||
unpack!(block = this.into(temp_place, block, expr));
|
||||
|
||||
if let Some(temp_lifetime) = temp_lifetime {
|
||||
this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Value);
|
||||
}
|
||||
|
||||
block.and(temp)
|
||||
}
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
//! See docs in build/expr/mod.rs
|
||||
|
||||
use crate::build::expr::category::{Category, RvalueFunc};
|
||||
use crate::build::scope::DropKind;
|
||||
use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
|
||||
use crate::thir::*;
|
||||
use rustc_ast::InlineAsmOptions;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::middle::region;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::CanonicalUserTypeAnnotation;
|
||||
|
||||
|
@ -17,19 +15,13 @@ use std::slice;
|
|||
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
/// Compile `expr`, storing the result into `destination`, which
|
||||
/// is assumed to be uninitialized.
|
||||
/// If a `drop_scope` is provided, `destination` is scheduled to be dropped
|
||||
/// in `scope` once it has been initialized.
|
||||
crate fn into_expr(
|
||||
&mut self,
|
||||
destination: Place<'tcx>,
|
||||
scope: Option<region::Scope>,
|
||||
mut block: BasicBlock,
|
||||
expr: Expr<'tcx>,
|
||||
) -> BlockAnd<()> {
|
||||
debug!(
|
||||
"into_expr(destination={:?}, scope={:?}, block={:?}, expr={:?})",
|
||||
destination, scope, block, expr
|
||||
);
|
||||
debug!("into_expr(destination={:?}, block={:?}, expr={:?})", destination, block, expr);
|
||||
|
||||
// since we frequently have to reference `self` from within a
|
||||
// closure, where `self` would be shadowed, it's easier to
|
||||
|
@ -41,14 +33,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let expr_is_block_or_scope =
|
||||
matches!(expr.kind, ExprKind::Block { .. } | ExprKind::Scope { .. });
|
||||
|
||||
let schedule_drop = move |this: &mut Self| {
|
||||
if let Some(drop_scope) = scope {
|
||||
let local =
|
||||
destination.as_local().expect("cannot schedule drop of non-Local place");
|
||||
this.schedule_drop(expr_span, drop_scope, local, DropKind::Value);
|
||||
}
|
||||
};
|
||||
|
||||
if !expr_is_block_or_scope {
|
||||
this.block_context.push(BlockFrame::SubExpr);
|
||||
}
|
||||
|
@ -58,15 +42,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let region_scope = (region_scope, source_info);
|
||||
ensure_sufficient_stack(|| {
|
||||
this.in_scope(region_scope, lint_level, |this| {
|
||||
this.into(destination, scope, block, value)
|
||||
this.into(destination, block, value)
|
||||
})
|
||||
})
|
||||
}
|
||||
ExprKind::Block { body: ast_block } => {
|
||||
this.ast_block(destination, scope, block, ast_block, source_info)
|
||||
this.ast_block(destination, block, ast_block, source_info)
|
||||
}
|
||||
ExprKind::Match { scrutinee, arms } => {
|
||||
this.match_expr(destination, scope, expr_span, block, scrutinee, arms)
|
||||
this.match_expr(destination, expr_span, block, scrutinee, arms)
|
||||
}
|
||||
ExprKind::If { cond, then, else_opt } => {
|
||||
let place = unpack!(
|
||||
|
@ -79,9 +63,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let term = TerminatorKind::if_(this.hir.tcx(), operand, then_block, else_block);
|
||||
this.cfg.terminate(block, source_info, term);
|
||||
|
||||
unpack!(then_block = this.into(destination, scope, then_block, then));
|
||||
unpack!(then_block = this.into(destination, then_block, then));
|
||||
else_block = if let Some(else_opt) = else_opt {
|
||||
unpack!(this.into(destination, None, else_block, else_opt))
|
||||
unpack!(this.into(destination, else_block, else_opt))
|
||||
} else {
|
||||
// Body of the `if` expression without an `else` clause must return `()`, thus
|
||||
// we implicitly generate a `else {}` if it is not specified.
|
||||
|
@ -117,7 +101,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
|
||||
// This is an optimization. If the expression was a call then we already have an
|
||||
// unreachable block. Don't bother to terminate it and create a new one.
|
||||
schedule_drop(this);
|
||||
if is_call {
|
||||
block.unit()
|
||||
} else {
|
||||
|
@ -193,35 +176,26 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// Start the loop.
|
||||
this.cfg.goto(block, source_info, loop_block);
|
||||
|
||||
this.in_breakable_scope(
|
||||
Some(loop_block),
|
||||
destination,
|
||||
scope,
|
||||
expr_span,
|
||||
move |this| {
|
||||
// conduct the test, if necessary
|
||||
let body_block = this.cfg.start_new_block();
|
||||
this.cfg.terminate(
|
||||
loop_block,
|
||||
source_info,
|
||||
TerminatorKind::FalseUnwind { real_target: body_block, unwind: None },
|
||||
);
|
||||
this.diverge_from(loop_block);
|
||||
this.in_breakable_scope(Some(loop_block), destination, expr_span, move |this| {
|
||||
// conduct the test, if necessary
|
||||
let body_block = this.cfg.start_new_block();
|
||||
this.cfg.terminate(
|
||||
loop_block,
|
||||
source_info,
|
||||
TerminatorKind::FalseUnwind { real_target: body_block, unwind: None },
|
||||
);
|
||||
this.diverge_from(loop_block);
|
||||
|
||||
// The “return” value of the loop body must always be an unit. We therefore
|
||||
// introduce a unit temporary as the destination for the loop body.
|
||||
let tmp = this.get_unit_temp();
|
||||
// Execute the body, branching back to the test.
|
||||
// We don't need to provide a drop scope because `tmp`
|
||||
// has type `()`.
|
||||
let body_block_end = unpack!(this.into(tmp, None, body_block, body));
|
||||
this.cfg.goto(body_block_end, source_info, loop_block);
|
||||
schedule_drop(this);
|
||||
// The “return” value of the loop body must always be an unit. We therefore
|
||||
// introduce a unit temporary as the destination for the loop body.
|
||||
let tmp = this.get_unit_temp();
|
||||
// Execute the body, branching back to the test.
|
||||
let body_block_end = unpack!(this.into(tmp, body_block, body));
|
||||
this.cfg.goto(body_block_end, source_info, loop_block);
|
||||
|
||||
// Loops are only exited by `break` expressions.
|
||||
None
|
||||
},
|
||||
)
|
||||
// Loops are only exited by `break` expressions.
|
||||
None
|
||||
})
|
||||
}
|
||||
ExprKind::Call { ty: _, fun, args, from_hir_call, fn_span } => {
|
||||
let fun = unpack!(block = this.as_local_operand(block, fun));
|
||||
|
@ -256,10 +230,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
},
|
||||
);
|
||||
this.diverge_from(block);
|
||||
schedule_drop(this);
|
||||
success.unit()
|
||||
}
|
||||
ExprKind::Use { source } => this.into(destination, scope, block, source),
|
||||
ExprKind::Use { source } => this.into(destination, block, source),
|
||||
ExprKind::Borrow { arg, borrow_kind } => {
|
||||
// We don't do this in `as_rvalue` because we use `as_place`
|
||||
// for borrow expressions, so we cannot create an `RValue` that
|
||||
|
@ -348,7 +321,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
destination,
|
||||
Rvalue::Aggregate(adt, fields),
|
||||
);
|
||||
schedule_drop(this);
|
||||
block.unit()
|
||||
}
|
||||
ExprKind::InlineAsm { template, operands, options, line_spans } => {
|
||||
|
@ -445,7 +417,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let place = unpack!(block = this.as_place(block, expr));
|
||||
let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
|
||||
this.cfg.push_assign(block, source_info, destination, rvalue);
|
||||
schedule_drop(this);
|
||||
block.unit()
|
||||
}
|
||||
ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
|
||||
|
@ -463,7 +434,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let place = unpack!(block = this.as_place(block, expr));
|
||||
let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
|
||||
this.cfg.push_assign(block, source_info, destination, rvalue);
|
||||
schedule_drop(this);
|
||||
block.unit()
|
||||
}
|
||||
|
||||
|
@ -478,7 +448,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
TerminatorKind::Yield { value, resume, resume_arg: destination, drop: None },
|
||||
);
|
||||
this.generator_drop_cleanup(block);
|
||||
schedule_drop(this);
|
||||
resume.unit()
|
||||
}
|
||||
|
||||
|
@ -510,7 +479,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
|
||||
let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
|
||||
this.cfg.push_assign(block, source_info, destination, rvalue);
|
||||
schedule_drop(this);
|
||||
block.unit()
|
||||
}
|
||||
};
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
use crate::build::{BlockAnd, Builder};
|
||||
use crate::thir::*;
|
||||
use rustc_middle::middle::region;
|
||||
use rustc_middle::mir::*;
|
||||
|
||||
pub(in crate::build) trait EvalInto<'tcx> {
|
||||
|
@ -14,7 +13,6 @@ pub(in crate::build) trait EvalInto<'tcx> {
|
|||
self,
|
||||
builder: &mut Builder<'_, 'tcx>,
|
||||
destination: Place<'tcx>,
|
||||
scope: Option<region::Scope>,
|
||||
block: BasicBlock,
|
||||
) -> BlockAnd<()>;
|
||||
}
|
||||
|
@ -23,14 +21,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
crate fn into<E>(
|
||||
&mut self,
|
||||
destination: Place<'tcx>,
|
||||
scope: Option<region::Scope>,
|
||||
block: BasicBlock,
|
||||
expr: E,
|
||||
) -> BlockAnd<()>
|
||||
where
|
||||
E: EvalInto<'tcx>,
|
||||
{
|
||||
expr.eval_into(self, destination, scope, block)
|
||||
expr.eval_into(self, destination, block)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,11 +36,10 @@ impl<'tcx> EvalInto<'tcx> for ExprRef<'tcx> {
|
|||
self,
|
||||
builder: &mut Builder<'_, 'tcx>,
|
||||
destination: Place<'tcx>,
|
||||
scope: Option<region::Scope>,
|
||||
block: BasicBlock,
|
||||
) -> BlockAnd<()> {
|
||||
let expr = builder.hir.mirror(self);
|
||||
builder.into_expr(destination, scope, block, expr)
|
||||
builder.into_expr(destination, block, expr)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,9 +48,8 @@ impl<'tcx> EvalInto<'tcx> for Expr<'tcx> {
|
|||
self,
|
||||
builder: &mut Builder<'_, 'tcx>,
|
||||
destination: Place<'tcx>,
|
||||
scope: Option<region::Scope>,
|
||||
block: BasicBlock,
|
||||
) -> BlockAnd<()> {
|
||||
builder.into_expr(destination, scope, block, self)
|
||||
builder.into_expr(destination, block, self)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,7 +87,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
crate fn match_expr(
|
||||
&mut self,
|
||||
destination: Place<'tcx>,
|
||||
destination_scope: Option<region::Scope>,
|
||||
span: Span,
|
||||
mut block: BasicBlock,
|
||||
scrutinee: ExprRef<'tcx>,
|
||||
|
@ -108,7 +107,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
|
||||
self.lower_match_arms(
|
||||
destination,
|
||||
destination_scope,
|
||||
scrutinee_place,
|
||||
scrutinee_span,
|
||||
arm_candidates,
|
||||
|
@ -215,13 +213,76 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Lower the bindings, guards and arm bodies of a `match` expression.
|
||||
///
|
||||
/// The decision tree should have already been created
|
||||
/// (by [Builder::lower_match_tree]).
|
||||
///
|
||||
/// `outer_source_info` is the SourceInfo for the whole match.
|
||||
fn lower_match_arms(
|
||||
&mut self,
|
||||
destination: Place<'tcx>,
|
||||
scrutinee_place: Place<'tcx>,
|
||||
scrutinee_span: Span,
|
||||
arm_candidates: Vec<(&'_ Arm<'tcx>, Candidate<'_, 'tcx>)>,
|
||||
outer_source_info: SourceInfo,
|
||||
fake_borrow_temps: Vec<(Place<'tcx>, Local)>,
|
||||
) -> BlockAnd<()> {
|
||||
let arm_end_blocks: Vec<_> = arm_candidates
|
||||
.into_iter()
|
||||
.map(|(arm, candidate)| {
|
||||
debug!("lowering arm {:?}\ncandidate = {:?}", arm, candidate);
|
||||
|
||||
let arm_source_info = self.source_info(arm.span);
|
||||
let arm_scope = (arm.scope, arm_source_info);
|
||||
self.in_scope(arm_scope, arm.lint_level, |this| {
|
||||
let body = this.hir.mirror(arm.body.clone());
|
||||
let scope = this.declare_bindings(
|
||||
None,
|
||||
arm.span,
|
||||
&arm.pattern,
|
||||
ArmHasGuard(arm.guard.is_some()),
|
||||
Some((Some(&scrutinee_place), scrutinee_span)),
|
||||
);
|
||||
|
||||
let arm_block = this.bind_pattern(
|
||||
outer_source_info,
|
||||
candidate,
|
||||
arm.guard.as_ref(),
|
||||
&fake_borrow_temps,
|
||||
scrutinee_span,
|
||||
Some(arm.span),
|
||||
Some(arm.scope),
|
||||
);
|
||||
|
||||
if let Some(source_scope) = scope {
|
||||
this.source_scope = source_scope;
|
||||
}
|
||||
|
||||
this.into(destination, arm_block, body)
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
// all the arm blocks will rejoin here
|
||||
let end_block = self.cfg.start_new_block();
|
||||
|
||||
for arm_block in arm_end_blocks {
|
||||
self.cfg.goto(unpack!(arm_block), outer_source_info, end_block);
|
||||
}
|
||||
|
||||
self.source_scope = outer_source_info.scope;
|
||||
|
||||
end_block.unit()
|
||||
}
|
||||
|
||||
/// Binds the variables and ascribes types for a given `match` arm or
|
||||
/// `let` binding.
|
||||
///
|
||||
/// Also check if the guard matches, if it's provided.
|
||||
/// `arm_scope` should be `Some` if and only if this is called for a
|
||||
/// `match` arm.
|
||||
crate fn bind_pattern(
|
||||
fn bind_pattern(
|
||||
&mut self,
|
||||
outer_source_info: SourceInfo,
|
||||
candidate: Candidate<'_, 'tcx>,
|
||||
|
@ -308,14 +369,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
PatKind::Binding { mode: BindingMode::ByValue, var, subpattern: None, .. } => {
|
||||
let place =
|
||||
self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true);
|
||||
let region_scope = self.hir.region_scope_tree.var_scope(var.local_id);
|
||||
|
||||
unpack!(block = self.into(place, Some(region_scope), block, initializer));
|
||||
unpack!(block = self.into(place, block, initializer));
|
||||
|
||||
// Inject a fake read, see comments on `FakeReadCause::ForLet`.
|
||||
let source_info = self.source_info(irrefutable_pat.span);
|
||||
self.cfg.push_fake_read(block, source_info, FakeReadCause::ForLet, place);
|
||||
|
||||
self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard);
|
||||
block.unit()
|
||||
}
|
||||
|
||||
|
@ -342,10 +402,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
ascription:
|
||||
thir::pattern::Ascription { user_ty: pat_ascription_ty, variance: _, user_ty_span },
|
||||
} => {
|
||||
let region_scope = self.hir.region_scope_tree.var_scope(var.local_id);
|
||||
let place =
|
||||
self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true);
|
||||
unpack!(block = self.into(place, Some(region_scope), block, initializer));
|
||||
unpack!(block = self.into(place, block, initializer));
|
||||
|
||||
// Inject a fake read, see comments on `FakeReadCause::ForLet`.
|
||||
let pattern_source_info = self.source_info(irrefutable_pat.span);
|
||||
|
@ -383,6 +442,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
},
|
||||
);
|
||||
|
||||
self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard);
|
||||
block.unit()
|
||||
}
|
||||
|
||||
|
@ -629,7 +689,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct Candidate<'pat, 'tcx> {
|
||||
struct Candidate<'pat, 'tcx> {
|
||||
/// [`Span`] of the original pattern that gave rise to this candidate.
|
||||
span: Span,
|
||||
|
||||
|
@ -1362,7 +1422,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
match test.kind {
|
||||
TestKind::SwitchInt { switch_ty, ref mut options } => {
|
||||
for candidate in candidates.iter() {
|
||||
if !self.add_cases_to_switch(&match_place, candidate, switch_ty, options) {
|
||||
if !self.add_cases_to_switch(
|
||||
&match_place,
|
||||
candidate,
|
||||
switch_ty,
|
||||
options,
|
||||
) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1777,11 +1842,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// ```
|
||||
//
|
||||
// and that is clearly not correct.
|
||||
let by_value_bindings = parent_bindings
|
||||
.iter()
|
||||
.flat_map(|(bindings, _)| bindings)
|
||||
.chain(&candidate.bindings)
|
||||
.filter(|binding| matches!(binding.binding_mode, BindingMode::ByValue));
|
||||
let by_value_bindings =
|
||||
parent_bindings
|
||||
.iter()
|
||||
.flat_map(|(bindings, _)| bindings)
|
||||
.chain(&candidate.bindings)
|
||||
.filter(|binding| {
|
||||
matches!(binding.binding_mode, BindingMode::ByValue )
|
||||
});
|
||||
// Read all of the by reference bindings to ensure that the
|
||||
// place they refer to can't be modified by the guard.
|
||||
for binding in by_value_bindings.clone() {
|
||||
|
|
|
@ -76,9 +76,7 @@ fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> Body<'_
|
|||
kind: hir::TraitItemKind::Const(ty, Some(body_id)),
|
||||
..
|
||||
}) => (*body_id, ty.span, None),
|
||||
Node::AnonConst(hir::AnonConst { body, hir_id, .. }) => {
|
||||
(*body, tcx.hir().span(*hir_id), None)
|
||||
}
|
||||
Node::AnonConst(hir::AnonConst { body, hir_id, .. }) => (*body, tcx.hir().span(*hir_id), None),
|
||||
|
||||
_ => span_bug!(tcx.hir().span(id), "can't build MIR for {:?}", def.did),
|
||||
};
|
||||
|
@ -186,7 +184,7 @@ fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> Body<'_
|
|||
return_ty,
|
||||
return_ty_span,
|
||||
body,
|
||||
span_with_body,
|
||||
span_with_body
|
||||
);
|
||||
mir.yield_ty = yield_ty;
|
||||
mir
|
||||
|
@ -584,7 +582,7 @@ fn construct_fn<'a, 'tcx, A>(
|
|||
return_ty: Ty<'tcx>,
|
||||
return_ty_span: Span,
|
||||
body: &'tcx hir::Body<'tcx>,
|
||||
span_with_body: Span,
|
||||
span_with_body: Span
|
||||
) -> Body<'tcx>
|
||||
where
|
||||
A: Iterator<Item = ArgInfo<'tcx>>,
|
||||
|
@ -618,12 +616,8 @@ where
|
|||
let arg_scope_s = (arg_scope, source_info);
|
||||
// Attribute epilogue to function's closing brace
|
||||
let fn_end = span_with_body.shrink_to_hi();
|
||||
let return_block = unpack!(builder.in_breakable_scope(
|
||||
None,
|
||||
Place::return_place(),
|
||||
Some(call_site_scope),
|
||||
fn_end,
|
||||
|builder| {
|
||||
let return_block =
|
||||
unpack!(builder.in_breakable_scope(None, Place::return_place(), fn_end, |builder| {
|
||||
Some(builder.in_scope(arg_scope_s, LintLevel::Inherited, |builder| {
|
||||
builder.args_and_body(
|
||||
START_BLOCK,
|
||||
|
@ -633,13 +627,11 @@ where
|
|||
&body.value,
|
||||
)
|
||||
}))
|
||||
},
|
||||
));
|
||||
}));
|
||||
let source_info = builder.source_info(fn_end);
|
||||
builder.cfg.terminate(return_block, source_info, TerminatorKind::Return);
|
||||
let should_abort = should_abort_on_panic(tcx, fn_def_id, abi);
|
||||
builder.build_drop_trees(should_abort);
|
||||
builder.unschedule_return_place_drop();
|
||||
return_block.unit()
|
||||
}));
|
||||
|
||||
|
@ -666,15 +658,12 @@ fn construct_const<'a, 'tcx>(
|
|||
let owner_id = tcx.hir().body_owner(body_id);
|
||||
let def_id = tcx.hir().local_def_id(owner_id);
|
||||
let span = tcx.hir().span(owner_id);
|
||||
let mut builder =
|
||||
Builder::new(hir, def_id.to_def_id(), span, 0, Safety::Safe, const_ty, const_ty_span, None);
|
||||
let mut builder = Builder::new(hir, def_id.to_def_id(), span, 0, Safety::Safe, const_ty, const_ty_span, None);
|
||||
|
||||
let mut block = START_BLOCK;
|
||||
let ast_expr = &tcx.hir().body(body_id).value;
|
||||
let expr = builder.hir.mirror(ast_expr);
|
||||
// We don't provide a scope because we can't unwind in constants, so won't
|
||||
// need to drop the return place.
|
||||
unpack!(block = builder.into_expr(Place::return_place(), None, block, expr));
|
||||
unpack!(block = builder.into_expr(Place::return_place(), block, expr));
|
||||
|
||||
let source_info = builder.source_info(span);
|
||||
builder.cfg.terminate(block, source_info, TerminatorKind::Return);
|
||||
|
@ -709,8 +698,7 @@ fn construct_error<'a, 'tcx>(hir: Cx<'a, 'tcx>, body_id: hir::BodyId) -> Body<'t
|
|||
hir::BodyOwnerKind::Const => 0,
|
||||
hir::BodyOwnerKind::Static(_) => 0,
|
||||
};
|
||||
let mut builder =
|
||||
Builder::new(hir, def_id.to_def_id(), span, num_params, Safety::Safe, ty, span, None);
|
||||
let mut builder = Builder::new(hir, def_id.to_def_id(), span, num_params, Safety::Safe, ty, span, None);
|
||||
let source_info = builder.source_info(span);
|
||||
// Some MIR passes will expect the number of parameters to match the
|
||||
// function declaration.
|
||||
|
@ -953,9 +941,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
}
|
||||
|
||||
let body = self.hir.mirror(ast_body);
|
||||
let call_site =
|
||||
region::Scope { id: ast_body.hir_id.local_id, data: region::ScopeData::CallSite };
|
||||
self.into(Place::return_place(), Some(call_site), block, body)
|
||||
self.into(Place::return_place(), block, body)
|
||||
}
|
||||
|
||||
fn set_correct_source_scope_for_arg(
|
||||
|
|
|
@ -81,9 +81,8 @@ that contains only loops and breakable blocks. It tracks where a `break`,
|
|||
|
||||
*/
|
||||
|
||||
use crate::build::matches::{ArmHasGuard, Candidate};
|
||||
use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, CFG};
|
||||
use crate::thir::{Arm, Expr, ExprRef, LintLevel};
|
||||
use crate::thir::{Expr, ExprRef, LintLevel};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_index::vec::IndexVec;
|
||||
use rustc_middle::middle::region;
|
||||
|
@ -156,8 +155,6 @@ struct BreakableScope<'tcx> {
|
|||
/// The destination of the loop/block expression itself (i.e., where to put
|
||||
/// the result of a `break` or `return` expression)
|
||||
break_destination: Place<'tcx>,
|
||||
/// The scope that the destination should have its drop scheduled in.
|
||||
destination_scope: Option<region::Scope>,
|
||||
/// Drops that happen on the `break`/`return` path.
|
||||
break_drops: DropTree,
|
||||
/// Drops that happen on the `continue` path.
|
||||
|
@ -440,7 +437,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
&mut self,
|
||||
loop_block: Option<BasicBlock>,
|
||||
break_destination: Place<'tcx>,
|
||||
destination_scope: Option<region::Scope>,
|
||||
span: Span,
|
||||
f: F,
|
||||
) -> BlockAnd<()>
|
||||
|
@ -451,19 +447,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let scope = BreakableScope {
|
||||
region_scope,
|
||||
break_destination,
|
||||
destination_scope,
|
||||
break_drops: DropTree::new(),
|
||||
continue_drops: loop_block.map(|_| DropTree::new()),
|
||||
};
|
||||
let continue_block = loop_block.map(|block| (block, self.diverge_cleanup()));
|
||||
self.scopes.breakable_scopes.push(scope);
|
||||
let normal_exit_block = f(self);
|
||||
let breakable_scope = self.scopes.breakable_scopes.pop().unwrap();
|
||||
assert!(breakable_scope.region_scope == region_scope);
|
||||
if let Some(drops) = breakable_scope.continue_drops {
|
||||
self.build_exit_tree(drops, continue_block);
|
||||
}
|
||||
let break_block = self.build_exit_tree(breakable_scope.break_drops, None);
|
||||
if let Some(drops) = breakable_scope.continue_drops { self.build_exit_tree(drops, loop_block); }
|
||||
match (normal_exit_block, break_block) {
|
||||
(Some(block), None) | (None, Some(block)) => block,
|
||||
(None, None) => self.cfg.start_new_block().unit(),
|
||||
|
@ -592,22 +584,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
.rposition(|breakable_scope| breakable_scope.region_scope == scope)
|
||||
.unwrap_or_else(|| span_bug!(span, "no enclosing breakable scope found"))
|
||||
};
|
||||
let (break_index, destination, dest_scope) = match target {
|
||||
let (break_index, destination) = match target {
|
||||
BreakableTarget::Return => {
|
||||
let scope = &self.scopes.breakable_scopes[0];
|
||||
if scope.break_destination != Place::return_place() {
|
||||
span_bug!(span, "`return` in item with no return scope");
|
||||
}
|
||||
(0, Some(scope.break_destination), scope.destination_scope)
|
||||
(0, Some(scope.break_destination))
|
||||
}
|
||||
BreakableTarget::Break(scope) => {
|
||||
let break_index = get_scope_index(scope);
|
||||
let scope = &self.scopes.breakable_scopes[break_index];
|
||||
(break_index, Some(scope.break_destination), scope.destination_scope)
|
||||
(break_index, Some(scope.break_destination))
|
||||
}
|
||||
BreakableTarget::Continue(scope) => {
|
||||
let break_index = get_scope_index(scope);
|
||||
(break_index, None, None)
|
||||
(break_index, None)
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -615,10 +607,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
if let Some(value) = value {
|
||||
debug!("stmt_expr Break val block_context.push(SubExpr)");
|
||||
self.block_context.push(BlockFrame::SubExpr);
|
||||
unpack!(block = self.into(destination, dest_scope, block, value));
|
||||
if let Some(scope) = dest_scope {
|
||||
self.unschedule_drop(scope, destination.as_local().unwrap())
|
||||
};
|
||||
unpack!(block = self.into(destination, block, value));
|
||||
self.block_context.pop();
|
||||
} else {
|
||||
self.cfg.push_assign_unit(block, source_info, destination, self.hir.tcx())
|
||||
|
@ -858,47 +847,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
span_bug!(span, "region scope {:?} not in scope to drop {:?}", region_scope, local);
|
||||
}
|
||||
|
||||
/// Unschedule a drop. Used for `break`, `return` and `match` expressions,
|
||||
/// where `record_operands_moved` is not powerful enough.
|
||||
///
|
||||
/// The given local is expected to have a value drop scheduled in the given
|
||||
/// scope and for that drop to be the most recent thing scheduled in that
|
||||
/// scope.
|
||||
fn unschedule_drop(&mut self, region_scope: region::Scope, local: Local) {
|
||||
if !self.hir.needs_drop(self.local_decls[local].ty) {
|
||||
return;
|
||||
}
|
||||
for scope in self.scopes.scopes.iter_mut().rev() {
|
||||
scope.invalidate_cache();
|
||||
|
||||
if scope.region_scope == region_scope {
|
||||
let drop = scope.drops.pop();
|
||||
|
||||
match drop {
|
||||
Some(DropData { local: removed_local, kind: DropKind::Value, .. })
|
||||
if removed_local == local =>
|
||||
{
|
||||
return;
|
||||
}
|
||||
_ => bug!(
|
||||
"found wrong drop, expected value drop of {:?}, found {:?}",
|
||||
local,
|
||||
drop,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bug!("region scope {:?} not in scope to unschedule drop of {:?}", region_scope, local);
|
||||
}
|
||||
|
||||
/// Indicates that the "local operands" stored in `local` is
|
||||
/// Indicates that the "local operand" stored in `local` is
|
||||
/// *moved* at some point during execution (see `local_scope` for
|
||||
/// more information about what a "local operand" is -- in short,
|
||||
/// it's an intermediate operand created as part of preparing some
|
||||
/// MIR instruction). We use this information to suppress
|
||||
/// redundant drops. This results in less MIR, but also avoids spurious
|
||||
/// borrow check errors (c.f. #64391).
|
||||
/// redundant drops on the non-unwind paths. This results in less
|
||||
/// MIR, but also avoids spurious borrow check errors
|
||||
/// (c.f. #64391).
|
||||
///
|
||||
/// Example: when compiling the call to `foo` here:
|
||||
///
|
||||
|
@ -1133,97 +1089,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
success_block
|
||||
}
|
||||
|
||||
/// Lower the arms and guards of a match.
|
||||
///
|
||||
/// The decision tree should have already been created (by
|
||||
/// [Builder::lower_match_tree]).
|
||||
///
|
||||
/// This is this module, and not in `build::matches` because we have to do
|
||||
/// some careful scope manipulation to have the drop of the destination be
|
||||
/// scheduled at the end of each arm and then cleared for the next arm.
|
||||
crate fn lower_match_arms(
|
||||
&mut self,
|
||||
destination: Place<'tcx>,
|
||||
destination_scope: Option<region::Scope>,
|
||||
scrutinee_place: Place<'tcx>,
|
||||
scrutinee_span: Span,
|
||||
arm_candidates: Vec<(&'_ Arm<'tcx>, Candidate<'_, 'tcx>)>,
|
||||
outer_source_info: SourceInfo,
|
||||
fake_borrow_temps: Vec<(Place<'tcx>, Local)>,
|
||||
) -> BlockAnd<()> {
|
||||
if arm_candidates.is_empty() {
|
||||
// If there are no arms to schedule drops, then we have to do it
|
||||
// manually.
|
||||
if let Some(scope) = destination_scope {
|
||||
self.schedule_drop(
|
||||
outer_source_info.span,
|
||||
scope,
|
||||
destination.as_local().unwrap(),
|
||||
DropKind::Value,
|
||||
);
|
||||
}
|
||||
return self.cfg.start_new_block().unit();
|
||||
}
|
||||
let mut first_arm = true;
|
||||
let arm_end_blocks: Vec<_> = arm_candidates
|
||||
.into_iter()
|
||||
.map(|(arm, candidate)| {
|
||||
debug!("lowering arm {:?}\ncandidate = {:?}", arm, candidate);
|
||||
|
||||
if first_arm {
|
||||
first_arm = false;
|
||||
} else if let Some(scope) = destination_scope {
|
||||
self.unschedule_drop(scope, destination.as_local().unwrap());
|
||||
}
|
||||
|
||||
let arm_source_info = self.source_info(arm.span);
|
||||
let arm_scope = (arm.scope, arm_source_info);
|
||||
self.in_scope(arm_scope, arm.lint_level, |this| {
|
||||
let body = this.hir.mirror(arm.body.clone());
|
||||
let scope = this.declare_bindings(
|
||||
None,
|
||||
arm.span,
|
||||
&arm.pattern,
|
||||
ArmHasGuard(arm.guard.is_some()),
|
||||
Some((Some(&scrutinee_place), scrutinee_span)),
|
||||
);
|
||||
|
||||
let arm_block = this.bind_pattern(
|
||||
outer_source_info,
|
||||
candidate,
|
||||
arm.guard.as_ref(),
|
||||
&fake_borrow_temps,
|
||||
scrutinee_span,
|
||||
Some(arm.span),
|
||||
Some(arm.scope),
|
||||
);
|
||||
|
||||
if let Some(source_scope) = scope {
|
||||
this.source_scope = source_scope;
|
||||
}
|
||||
|
||||
this.into(destination, destination_scope, arm_block, body)
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
// all the arm blocks will rejoin here
|
||||
let end_block = self.cfg.start_new_block();
|
||||
|
||||
for arm_block in arm_end_blocks {
|
||||
self.cfg.goto(unpack!(arm_block), outer_source_info, end_block);
|
||||
}
|
||||
|
||||
self.source_scope = outer_source_info.scope;
|
||||
|
||||
end_block.unit()
|
||||
}
|
||||
|
||||
/// Unschedules any drops in the top scope.
|
||||
///
|
||||
/// This is only needed for `match` arm scopes, because they have one
|
||||
/// entrance per pattern, but only one exit.
|
||||
pub(super) fn clear_top_scope(&mut self, region_scope: region::Scope) {
|
||||
crate fn clear_top_scope(&mut self, region_scope: region::Scope) {
|
||||
let top_scope = self.scopes.scopes.last_mut().unwrap();
|
||||
|
||||
assert_eq!(top_scope.region_scope, region_scope);
|
||||
|
@ -1231,18 +1101,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
top_scope.drops.clear();
|
||||
top_scope.invalidate_cache();
|
||||
}
|
||||
|
||||
/// Unschedules the drop of the return place.
|
||||
///
|
||||
/// If the return type of a function requires drop, then we schedule it
|
||||
/// in the outermost scope so that it's dropped if there's a panic while
|
||||
/// we drop any local variables. But we don't want to drop it if we
|
||||
/// return normally.
|
||||
crate fn unschedule_return_place_drop(&mut self) {
|
||||
assert_eq!(self.scopes.scopes.len(), 1);
|
||||
assert!(self.scopes.scopes[0].drops.len() <= 1);
|
||||
self.scopes.scopes[0].drops.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds drops for `pop_scope` and `leave_top_scope`.
|
||||
|
@ -1318,24 +1176,20 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
|
|||
/// Build a drop tree for a breakable scope.
|
||||
///
|
||||
/// If `continue_block` is `Some`, then the tree is for `continue` inside a
|
||||
/// loop. Otherwise this is for `break` or `return`. The `DropIdx` is the
|
||||
/// next drop in the case that the drop tree unwinds. This is needed
|
||||
/// because the drop of the break destination has already been scheduled
|
||||
/// but it hasn't been initialized on the `continue` paths.
|
||||
/// loop. Otherwise this is for `break` or `return`.
|
||||
fn build_exit_tree(
|
||||
&mut self,
|
||||
mut drops: DropTree,
|
||||
continue_block: Option<(BasicBlock, DropIdx)>,
|
||||
continue_block: Option<BasicBlock>,
|
||||
) -> Option<BlockAnd<()>> {
|
||||
let mut blocks = IndexVec::from_elem(None, &drops.drops);
|
||||
blocks[ROOT_NODE] = continue_block.map(|(block, _)| block);
|
||||
blocks[ROOT_NODE] = continue_block;
|
||||
|
||||
drops.build_mir::<ExitScopes>(&mut self.cfg, &mut blocks);
|
||||
|
||||
// Link the exit drop tree to unwind drop tree.
|
||||
if drops.drops.iter().any(|(drop, _)| drop.kind == DropKind::Value) {
|
||||
let unwind_target = continue_block
|
||||
.map_or_else(|| self.diverge_cleanup(), |(_, unwind_target)| unwind_target);
|
||||
let unwind_target = self.diverge_cleanup();
|
||||
let mut unwind_indices = IndexVec::from_elem_n(unwind_target, 1);
|
||||
for (drop_idx, drop_data) in drops.drops.iter_enumerated().skip(1) {
|
||||
match drop_data.0.kind {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue