Auto merge of #61872 - matthewjasper:refactor-mir-drop-gen, r=nikomatsakis
Clean up MIR drop generation
* Don't assign twice to the destination of a `while` loop containing a `break` expression
* Use `as_temp` to evaluate statement expression
* Avoid consecutive `StorageLive`s for the condition of a `while` loop
* Unify `return`, `break` and `continue` handling, and move it to `scopes.rs`
* Make some of the `scopes.rs` internals private
* Don't use `Place`s that are always `Local`s in MIR drop generation
Closes #42371
Closes #61579
Closes #61731
Closes #61834
Closes #61910
Closes #62115
diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs
index 749cd6f..7ea08b1 100644
--- a/src/librustc_mir/build/block.rs
+++ b/src/librustc_mir/build/block.rs
@@ -78,7 +78,7 @@
let source_info = this.source_info(span);
for stmt in stmts {
- let Stmt { kind, opt_destruction_scope, span: stmt_span } = this.hir.mirror(stmt);
+ let Stmt { kind, opt_destruction_scope } = this.hir.mirror(stmt);
match kind {
StmtKind::Expr { scope, expr } => {
this.block_context.push(BlockFrame::Statement { ignores_expr_result: true });
@@ -87,7 +87,7 @@
let si = (scope, source_info);
this.in_scope(si, LintLevel::Inherited, |this| {
let expr = this.hir.mirror(expr);
- this.stmt_expr(block, expr, Some(stmt_span))
+ this.stmt_expr(block, expr, Some(scope))
})
}));
}
diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs
index 73ce2a5..17e7b1a 100644
--- a/src/librustc_mir/build/expr/as_rvalue.rs
+++ b/src/librustc_mir/build/expr/as_rvalue.rs
@@ -127,7 +127,7 @@
this.schedule_drop_storage_and_value(
expr_span,
scope,
- &Place::from(result),
+ result,
value.ty,
);
}
@@ -559,7 +559,7 @@
this.schedule_drop_storage_and_value(
upvar_span,
temp_lifetime,
- &Place::from(temp),
+ temp,
upvar_ty,
);
}
diff --git a/src/librustc_mir/build/expr/as_temp.rs b/src/librustc_mir/build/expr/as_temp.rs
index 1b3ebac..1fe6be8 100644
--- a/src/librustc_mir/build/expr/as_temp.rs
+++ b/src/librustc_mir/build/expr/as_temp.rs
@@ -88,7 +88,7 @@
this.schedule_drop(
expr_span,
temp_lifetime,
- temp_place,
+ temp,
expr_ty,
DropKind::Storage,
);
@@ -101,7 +101,7 @@
this.schedule_drop(
expr_span,
temp_lifetime,
- temp_place,
+ temp,
expr_ty,
DropKind::Value,
);
diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs
index f70ecef..0a2ea78 100644
--- a/src/librustc_mir/build/expr/into.rs
+++ b/src/librustc_mir/build/expr/into.rs
@@ -179,20 +179,19 @@
// conduct the test, if necessary
let body_block;
if let Some(cond_expr) = opt_cond_expr {
- let loop_block_end;
- let cond = unpack!(
- loop_block_end = this.as_local_operand(loop_block, cond_expr)
- );
- body_block = this.cfg.start_new_block();
- let term =
- TerminatorKind::if_(this.hir.tcx(), cond, body_block, exit_block);
- this.cfg.terminate(loop_block_end, source_info, term);
+ let cond_expr = this.hir.mirror(cond_expr);
+ let (true_block, false_block)
+ = this.test_bool(loop_block, cond_expr, source_info);
+ body_block = true_block;
// if the test is false, there's no `break` to assign `destination`, so
- // we have to do it; this overwrites any `break`-assigned value but it's
- // always `()` anyway
- this.cfg
- .push_assign_unit(exit_block, source_info, destination);
+ // we have to do it
+ this.cfg.push_assign_unit(false_block, source_info, destination);
+ this.cfg.terminate(
+ false_block,
+ source_info,
+ TerminatorKind::Goto { target: exit_block },
+ );
} else {
body_block = this.cfg.start_new_block();
let diverge_cleanup = this.diverge_cleanup();
diff --git a/src/librustc_mir/build/expr/stmt.rs b/src/librustc_mir/build/expr/stmt.rs
index 4463e7f..cf3d877 100644
--- a/src/librustc_mir/build/expr/stmt.rs
+++ b/src/librustc_mir/build/expr/stmt.rs
@@ -1,6 +1,7 @@
-use crate::build::scope::BreakableScope;
+use crate::build::scope::BreakableTarget;
use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
use crate::hair::*;
+use rustc::middle::region;
use rustc::mir::*;
impl<'a, 'tcx> Builder<'a, 'tcx> {
@@ -8,14 +9,13 @@
/// If the original expression was an AST statement,
/// (e.g., `some().code(&here());`) then `opt_stmt_span` is the
/// span of that statement (including its semicolon, if any).
- /// Diagnostics use this span (which may be larger than that of
- /// `expr`) to identify when statement temporaries are dropped.
- pub fn stmt_expr(&mut self,
- mut block: BasicBlock,
- expr: Expr<'tcx>,
- opt_stmt_span: Option<StatementSpan>)
- -> BlockAnd<()>
- {
+ /// The scope is used if a statement temporary must be dropped.
+ pub fn stmt_expr(
+ &mut self,
+ mut block: BasicBlock,
+ expr: Expr<'tcx>,
+ statement_scope: Option<region::Scope>,
+ ) -> BlockAnd<()> {
let this = self;
let expr_span = expr.span;
let source_info = this.source_info(expr.span);
@@ -30,7 +30,7 @@
} => {
let value = this.hir.mirror(value);
this.in_scope((region_scope, source_info), lint_level, |this| {
- this.stmt_expr(block, value, opt_stmt_span)
+ this.stmt_expr(block, value, statement_scope)
})
}
ExprKind::Assign { lhs, rhs } => {
@@ -98,70 +98,13 @@
block.unit()
}
ExprKind::Continue { label } => {
- let BreakableScope {
- continue_block,
- region_scope,
- ..
- } = *this.find_breakable_scope(expr_span, label);
- let continue_block = continue_block
- .expect("Attempted to continue in non-continuable breakable block");
- this.exit_scope(
- expr_span,
- (region_scope, source_info),
- block,
- continue_block,
- );
- this.cfg.start_new_block().unit()
+ this.break_scope(block, None, BreakableTarget::Continue(label), source_info)
}
ExprKind::Break { label, value } => {
- let (break_block, region_scope, destination) = {
- let BreakableScope {
- break_block,
- region_scope,
- ref break_destination,
- ..
- } = *this.find_breakable_scope(expr_span, label);
- (break_block, region_scope, break_destination.clone())
- };
- if let Some(value) = value {
- debug!("stmt_expr Break val block_context.push(SubExpr) : {:?}", expr2);
- this.block_context.push(BlockFrame::SubExpr);
- unpack!(block = this.into(&destination, block, value));
- this.block_context.pop();
- } else {
- this.cfg.push_assign_unit(block, source_info, &destination)
- }
- this.exit_scope(expr_span, (region_scope, source_info), block, break_block);
- this.cfg.start_new_block().unit()
+ this.break_scope(block, value, BreakableTarget::Break(label), source_info)
}
ExprKind::Return { value } => {
- block = match value {
- Some(value) => {
- debug!("stmt_expr Return val block_context.push(SubExpr) : {:?}", expr2);
- this.block_context.push(BlockFrame::SubExpr);
- let result = unpack!(
- this.into(
- &Place::RETURN_PLACE,
- block,
- value
- )
- );
- this.block_context.pop();
- result
- }
- None => {
- this.cfg.push_assign_unit(
- block,
- source_info,
- &Place::RETURN_PLACE,
- );
- block
- }
- };
- let region_scope = this.region_scope_of_return_scope();
- let return_block = this.return_block();
- this.exit_scope(expr_span, (region_scope, source_info), block, return_block);
- this.cfg.start_new_block().unit()
+ this.break_scope(block, value, BreakableTarget::Return, source_info)
}
ExprKind::InlineAsm {
asm,
@@ -199,7 +142,11 @@
block.unit()
}
_ => {
- let expr_ty = expr.ty;
+ assert!(
+ statement_scope.is_some(),
+ "Should not be calling `stmt_expr` on a general expression \
+ without a statement scope",
+ );
// Issue #54382: When creating temp for the value of
// expression like:
@@ -208,48 +155,34 @@
//
// it is usually better to focus on `the_value` rather
// than the entirety of block(s) surrounding it.
- let mut temp_span = expr_span;
- let mut temp_in_tail_of_block = false;
- if let ExprKind::Block { body } = expr.kind {
- if let Some(tail_expr) = &body.expr {
- let mut expr = tail_expr;
- while let rustc::hir::ExprKind::Block(subblock, _label) = &expr.node {
- if let Some(subtail_expr) = &subblock.expr {
- expr = subtail_expr
- } else {
- break;
+ let adjusted_span = (|| {
+ if let ExprKind::Block { body } = expr.kind {
+ if let Some(tail_expr) = &body.expr {
+ let mut expr = tail_expr;
+ while let rustc::hir::ExprKind::Block(subblock, _label) = &expr.node {
+ if let Some(subtail_expr) = &subblock.expr {
+ expr = subtail_expr
+ } else {
+ break;
+ }
}
- }
- temp_span = expr.span;
- temp_in_tail_of_block = true;
- }
- }
-
- let temp = {
- let mut local_decl = LocalDecl::new_temp(expr.ty.clone(), temp_span);
- if temp_in_tail_of_block {
- if this.block_context.currently_ignores_tail_results() {
- local_decl = local_decl.block_tail(BlockTailInfo {
+ this.block_context.push(BlockFrame::TailExpr {
tail_result_is_ignored: true
});
+ return Some(expr.span);
}
}
- let temp = this.local_decls.push(local_decl);
- let place = Place::from(temp);
- debug!("created temp {:?} for expr {:?} in block_context: {:?}",
- temp, expr, this.block_context);
- place
- };
- unpack!(block = this.into(&temp, block, expr));
+ None
+ })();
- // Attribute drops of the statement's temps to the
- // semicolon at the statement's end.
- let drop_point = this.hir.tcx().sess.source_map().end_point(match opt_stmt_span {
- None => expr_span,
- Some(StatementSpan(span)) => span,
- });
+ let temp = unpack!(block =
+ this.as_temp(block, statement_scope, expr, Mutability::Not));
- unpack!(block = this.build_drop(block, drop_point, temp, expr_ty));
+ if let Some(span) = adjusted_span {
+ this.local_decls[temp].source_info.span = span;
+ this.block_context.pop();
+ }
+
block.unit()
}
}
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index d2e56c4..f831f51 100644
--- a/src/librustc_mir/build/matches/mod.rs
+++ b/src/librustc_mir/build/matches/mod.rs
@@ -531,11 +531,10 @@
kind: StatementKind::StorageLive(local_id),
},
);
- let place = Place::from(local_id);
let var_ty = self.local_decls[local_id].ty;
let region_scope = self.hir.region_scope_tree.var_scope(var.local_id);
- self.schedule_drop(span, region_scope, &place, var_ty, DropKind::Storage);
- place
+ self.schedule_drop(span, region_scope, local_id, var_ty, DropKind::Storage);
+ Place::Base(PlaceBase::Local(local_id))
}
pub fn schedule_drop_for_binding(&mut self, var: HirId, span: Span, for_guard: ForGuard) {
@@ -545,7 +544,7 @@
self.schedule_drop(
span,
region_scope,
- &Place::from(local_id),
+ local_id,
var_ty,
DropKind::Value,
);
@@ -1490,7 +1489,8 @@
};
let source_info = self.source_info(guard.span);
let guard_end = self.source_info(tcx.sess.source_map().end_point(guard.span));
- let cond = unpack!(block = self.as_local_operand(block, guard));
+ let (post_guard_block, otherwise_post_guard_block)
+ = self.test_bool(block, guard, source_info);
let guard_frame = self.guard_context.pop().unwrap();
debug!(
"Exiting guard building context with locals: {:?}",
@@ -1498,7 +1498,7 @@
);
for &(_, temp) in fake_borrows {
- self.cfg.push(block, Statement {
+ self.cfg.push(post_guard_block, Statement {
source_info: guard_end,
kind: StatementKind::FakeRead(
FakeReadCause::ForMatchGuard,
@@ -1507,6 +1507,13 @@
});
}
+ self.exit_scope(
+ source_info.span,
+ region_scope,
+ otherwise_post_guard_block,
+ candidate.otherwise_block.unwrap(),
+ );
+
// We want to ensure that the matched candidates are bound
// after we have confirmed this candidate *and* any
// associated guard; Binding them on `block` is too soon,
@@ -1533,41 +1540,6 @@
// ```
//
// and that is clearly not correct.
- let post_guard_block = self.cfg.start_new_block();
- let otherwise_post_guard_block = self.cfg.start_new_block();
- self.cfg.terminate(
- block,
- source_info,
- TerminatorKind::if_(
- self.hir.tcx(),
- cond.clone(),
- post_guard_block,
- otherwise_post_guard_block,
- ),
- );
-
- self.exit_scope(
- source_info.span,
- region_scope,
- otherwise_post_guard_block,
- candidate.otherwise_block.unwrap(),
- );
-
- if let Operand::Copy(cond_place) | Operand::Move(cond_place) = cond {
- if let Place::Base(PlaceBase::Local(cond_temp)) = cond_place {
- // We will call `clear_top_scope` if there's another guard. So
- // we have to drop this variable now or it will be "storage
- // leaked".
- self.pop_variable(
- post_guard_block,
- region_scope.0,
- cond_temp
- );
- } else {
- bug!("Expected as_local_operand to produce a temporary");
- }
- }
-
let by_value_bindings = candidate.bindings.iter().filter(|binding| {
if let BindingMode::ByValue = binding.binding_mode { true } else { false }
});
@@ -1577,7 +1549,7 @@
let local_id = self.var_local_id(binding.var_id, RefWithinGuard);
let place = Place::from(local_id);
self.cfg.push(
- block,
+ post_guard_block,
Statement {
source_info: guard_end,
kind: StatementKind::FakeRead(FakeReadCause::ForGuardBinding, place),
@@ -1652,11 +1624,6 @@
// denotes *R.
let ref_for_guard =
self.storage_live_binding(block, binding.var_id, binding.span, RefWithinGuard);
- // Question: Why schedule drops if bindings are all
- // shared-&'s?
- // Answer: Because schedule_drop_for_binding also emits
- // StorageDead's for those locals.
- self.schedule_drop_for_binding(binding.var_id, binding.span, RefWithinGuard);
match binding.binding_mode {
BindingMode::ByValue => {
let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, binding.source.clone());
@@ -1670,11 +1637,6 @@
binding.span,
OutsideGuard,
);
- self.schedule_drop_for_binding(
- binding.var_id,
- binding.span,
- OutsideGuard,
- );
let rvalue = Rvalue::Ref(re_erased, borrow_kind, binding.source.clone());
self.cfg
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index 0957dcbf..ad970de 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -251,7 +251,7 @@
/// The current set of scopes, updated as we traverse;
/// see the `scope` module for more details.
- scopes: Vec<scope::Scope<'tcx>>,
+ scopes: scope::Scopes<'tcx>,
/// The block-context: each time we build the code within an hair::Block,
/// we push a frame here tracking whether we are building a statement or
@@ -274,10 +274,6 @@
/// The number of `push_unsafe_block` levels in scope.
push_unsafe_count: usize,
- /// The current set of breakables; see the `scope` module for more
- /// details.
- breakable_scopes: Vec<scope::BreakableScope<'tcx>>,
-
/// The vector of all scopes that we have created thus far;
/// we track this for debuginfo later.
source_scopes: IndexVec<SourceScope, SourceScopeData>,
@@ -714,7 +710,7 @@
fn_span: span,
arg_count,
is_generator,
- scopes: vec![],
+ scopes: Default::default(),
block_context: BlockContext::new(),
source_scopes: IndexVec::new(),
source_scope: OUTERMOST_SOURCE_SCOPE,
@@ -722,7 +718,6 @@
guard_context: vec![],
push_unsafe_count: 0,
unpushed_unsafe: safety,
- breakable_scopes: vec![],
local_decls: IndexVec::from_elem_n(
LocalDecl::new_return_place(return_ty, return_span),
1,
@@ -814,7 +809,7 @@
// Make sure we drop (parts of) the argument even when not matched on.
self.schedule_drop(
pattern.as_ref().map_or(ast_body.span, |pat| pat.span),
- argument_scope, &place, ty, DropKind::Value,
+ argument_scope, local, ty, DropKind::Value,
);
if let Some(pattern) = pattern {
@@ -865,7 +860,11 @@
}
let body = self.hir.mirror(ast_body);
- self.into(&Place::RETURN_PLACE, block, body)
+ // `return_block` is called when we evaluate a `return` expression, so
+ // we just use `START_BLOCK` here.
+ self.in_breakable_scope(None, START_BLOCK, Place::RETURN_PLACE, |this| {
+ this.into(&Place::RETURN_PLACE, block, body)
+ })
}
fn get_unit_temp(&mut self) -> Place<'tcx> {
diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs
index db58a70..1b5fa1c 100644
--- a/src/librustc_mir/build/scope.rs
+++ b/src/librustc_mir/build/scope.rs
@@ -82,8 +82,8 @@
*/
-use crate::build::{BlockAnd, BlockAndExtension, Builder, CFG};
-use crate::hair::LintLevel;
+use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, CFG};
+use crate::hair::{Expr, ExprRef, LintLevel};
use rustc::middle::region;
use rustc::ty::Ty;
use rustc::hir;
@@ -94,7 +94,7 @@
use std::mem;
#[derive(Debug)]
-pub struct Scope<'tcx> {
+struct Scope {
/// The source scope this scope was created in.
source_scope: SourceScope,
@@ -121,7 +121,7 @@
/// out empty but grows as variables are declared during the
/// building process. This is a stack, so we always drop from the
/// end of the vector (top of the stack) first.
- drops: Vec<DropData<'tcx>>,
+ drops: Vec<DropData>,
/// The cache for drop chain on “normal” exit into a particular BasicBlock.
cached_exits: FxHashMap<(BasicBlock, region::Scope), BasicBlock>,
@@ -133,13 +133,20 @@
cached_unwind: CachedBlock,
}
+#[derive(Debug, Default)]
+pub struct Scopes<'tcx> {
+ scopes: Vec<Scope>,
+ /// The current set of breakable scopes. See module comment for more details.
+ breakable_scopes: Vec<BreakableScope<'tcx>>,
+}
+
#[derive(Debug)]
-struct DropData<'tcx> {
+struct DropData {
/// span where drop obligation was incurred (typically where place was declared)
span: Span,
- /// place to drop
- location: Place<'tcx>,
+ /// local to drop
+ local: Local,
/// Whether this is a value Drop or a StorageDead.
kind: DropKind,
@@ -172,17 +179,25 @@
}
#[derive(Clone, Debug)]
-pub struct BreakableScope<'tcx> {
+struct BreakableScope<'tcx> {
/// Region scope of the loop
- pub region_scope: region::Scope,
+ region_scope: region::Scope,
/// Where the body of the loop begins. `None` if block
- pub continue_block: Option<BasicBlock>,
+ continue_block: Option<BasicBlock>,
/// Block to branch into when the loop or block terminates (either by being `break`-en out
/// from, or by having its condition to become false)
- pub break_block: BasicBlock,
+ break_block: BasicBlock,
/// The destination of the loop/block expression itself (i.e., where to put the result of a
/// `break` expression)
- pub break_destination: Place<'tcx>,
+ break_destination: Place<'tcx>,
+}
+
+/// The target of an expression that breaks out of a scope
+#[derive(Clone, Copy, Debug)]
+pub enum BreakableTarget {
+ Continue(region::Scope),
+ Break(region::Scope),
+ Return,
}
impl CachedBlock {
@@ -208,16 +223,7 @@
}
}
-impl DropKind {
- fn may_panic(&self) -> bool {
- match *self {
- DropKind::Value => true,
- DropKind::Storage => false
- }
- }
-}
-
-impl<'tcx> Scope<'tcx> {
+impl Scope {
/// Invalidates all the cached blocks in the scope.
///
/// Should always be run for all inner scopes when a drop is pushed into some scope enclosing a
@@ -257,13 +263,111 @@
}
}
+impl<'tcx> Scopes<'tcx> {
+ fn len(&self) -> usize {
+ self.scopes.len()
+ }
+
+ fn push_scope(&mut self, region_scope: (region::Scope, SourceInfo), vis_scope: SourceScope) {
+ debug!("push_scope({:?})", region_scope);
+ self.scopes.push(Scope {
+ source_scope: vis_scope,
+ region_scope: region_scope.0,
+ region_scope_span: region_scope.1.span,
+ needs_cleanup: false,
+ drops: vec![],
+ cached_generator_drop: None,
+ cached_exits: Default::default(),
+ cached_unwind: CachedBlock::default(),
+ });
+ }
+
+ fn pop_scope(
+ &mut self,
+ region_scope: (region::Scope, SourceInfo),
+ ) -> (Scope, Option<BasicBlock>) {
+ let scope = self.scopes.pop().unwrap();
+ assert_eq!(scope.region_scope, region_scope.0);
+ let unwind_to = self.scopes.last()
+ .and_then(|next_scope| next_scope.cached_unwind.get(false));
+ (scope, unwind_to)
+ }
+
+ fn may_panic(&self, scope_count: usize) -> bool {
+ let len = self.len();
+ self.scopes[(len - scope_count)..].iter().any(|s| s.needs_cleanup)
+ }
+
+ /// Finds the breakable scope for a given label. This is used for
+ /// resolving `return`, `break` and `continue`.
+ fn find_breakable_scope(
+ &self,
+ span: Span,
+ target: BreakableTarget,
+ ) -> (BasicBlock, region::Scope, Option<Place<'tcx>>) {
+ let get_scope = |scope: region::Scope| {
+ // find the loop-scope by its `region::Scope`.
+ self.breakable_scopes.iter()
+ .rfind(|breakable_scope| breakable_scope.region_scope == scope)
+ .unwrap_or_else(|| span_bug!(span, "no enclosing breakable scope found"))
+ };
+ match target {
+ BreakableTarget::Return => {
+ let scope = &self.breakable_scopes[0];
+ if scope.break_destination != Place::RETURN_PLACE {
+ span_bug!(span, "`return` in item with no return scope");
+ }
+ (scope.break_block, scope.region_scope, Some(scope.break_destination.clone()))
+ }
+ BreakableTarget::Break(scope) => {
+ let scope = get_scope(scope);
+ (scope.break_block, scope.region_scope, Some(scope.break_destination.clone()))
+ }
+ BreakableTarget::Continue(scope) => {
+ let scope = get_scope(scope);
+ let continue_block = scope.continue_block
+ .unwrap_or_else(|| span_bug!(span, "missing `continue` block"));
+ (continue_block, scope.region_scope, None)
+ }
+ }
+ }
+
+ fn num_scopes_to(&self, region_scope: (region::Scope, SourceInfo), span: Span) -> usize {
+ let scope_count = 1 + self.scopes.iter().rev()
+ .position(|scope| scope.region_scope == region_scope.0)
+ .unwrap_or_else(|| {
+ span_bug!(span, "region_scope {:?} does not enclose", region_scope)
+ });
+ let len = self.len();
+ assert!(scope_count < len, "should not use `exit_scope` to pop ALL scopes");
+ scope_count
+ }
+
+ fn iter_mut(&mut self) -> impl DoubleEndedIterator<Item=&mut Scope> + '_ {
+ self.scopes.iter_mut().rev()
+ }
+
+ fn top_scopes(&mut self, count: usize) -> impl DoubleEndedIterator<Item=&mut Scope> + '_ {
+ let len = self.len();
+ self.scopes[len - count..].iter_mut()
+ }
+
+ /// Returns the topmost active scope, which is known to be alive until
+ /// the next scope expression.
+ fn topmost(&self) -> region::Scope {
+ self.scopes.last().expect("topmost_scope: no scopes present").region_scope
+ }
+
+ fn source_info(&self, index: usize, span: Span) -> SourceInfo {
+ self.scopes[self.len() - index].source_info(span)
+ }
+}
+
impl<'a, 'tcx> Builder<'a, 'tcx> {
// Adding and removing scopes
// ==========================
- /// Start a breakable scope, which tracks where `continue` and `break`
- /// should branch to. See module comment for more details.
- ///
- /// Returns the might_break attribute of the BreakableScope used.
+ // Start a breakable scope, which tracks where `continue`, `break` and
+ // `return` should branch to.
pub fn in_breakable_scope<F, R>(&mut self,
loop_block: Option<BasicBlock>,
break_block: BasicBlock,
@@ -271,16 +375,16 @@
f: F) -> R
where F: FnOnce(&mut Builder<'a, 'tcx>) -> R
{
- let region_scope = self.topmost_scope();
+ let region_scope = self.scopes.topmost();
let scope = BreakableScope {
region_scope,
continue_block: loop_block,
break_block,
break_destination,
};
- self.breakable_scopes.push(scope);
+ self.scopes.breakable_scopes.push(scope);
let res = f(self);
- let breakable_scope = self.breakable_scopes.pop().unwrap();
+ let breakable_scope = self.scopes.breakable_scopes.pop().unwrap();
assert!(breakable_scope.region_scope == region_scope);
res
}
@@ -350,18 +454,7 @@
/// calls must be paired; using `in_scope` as a convenience
/// wrapper maybe preferable.
pub fn push_scope(&mut self, region_scope: (region::Scope, SourceInfo)) {
- debug!("push_scope({:?})", region_scope);
- let vis_scope = self.source_scope;
- self.scopes.push(Scope {
- source_scope: vis_scope,
- region_scope: region_scope.0,
- region_scope_span: region_scope.1.span,
- needs_cleanup: false,
- drops: vec![],
- cached_generator_drop: None,
- cached_exits: Default::default(),
- cached_unwind: CachedBlock::default(),
- });
+ self.scopes.push_scope(region_scope, self.source_scope);
}
/// Pops a scope, which should have region scope `region_scope`,
@@ -374,17 +467,11 @@
debug!("pop_scope({:?}, {:?})", region_scope, block);
// If we are emitting a `drop` statement, we need to have the cached
// diverge cleanup pads ready in case that drop panics.
- let may_panic =
- self.scopes.last().unwrap().drops.iter().any(|s| s.kind.may_panic());
- if may_panic {
+ if self.scopes.may_panic(1) {
self.diverge_cleanup();
}
- let scope = self.scopes.pop().unwrap();
- assert_eq!(scope.region_scope, region_scope.0);
-
- let unwind_to = self.scopes.last().and_then(|next_scope| {
- next_scope.cached_unwind.get(false)
- }).unwrap_or_else(|| self.resume_block());
+ let (scope, unwind_to) = self.scopes.pop_scope(region_scope);
+ let unwind_to = unwind_to.unwrap_or_else(|| self.resume_block());
unpack!(block = build_scope_drops(
&mut self.cfg,
@@ -399,6 +486,37 @@
block.unit()
}
+ pub fn break_scope(
+ &mut self,
+ mut block: BasicBlock,
+ value: Option<ExprRef<'tcx>>,
+ scope: BreakableTarget,
+ source_info: SourceInfo,
+ ) -> BlockAnd<()> {
+ let (mut target_block, region_scope, destination)
+ = self.scopes.find_breakable_scope(source_info.span, scope);
+ if let BreakableTarget::Return = scope {
+ // We call this now, rather than when we start lowering the
+ // function so that the return block doesn't precede the entire
+ // rest of the CFG. Some passes and LLVM prefer blocks to be in
+ // approximately CFG order.
+ target_block = self.return_block();
+ }
+ if let Some(destination) = destination {
+ 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, block, value));
+ self.block_context.pop();
+ } else {
+ self.cfg.push_assign_unit(block, source_info, &destination)
+ }
+ } else {
+ assert!(value.is_none(), "`return` and `break` should have a destination");
+ }
+ self.exit_scope(source_info.span, (region_scope, source_info), block, target_block);
+ self.cfg.start_new_block().unit()
+ }
/// Branch out of `block` to `target`, exiting all scopes up to
/// and including `region_scope`. This will insert whatever drops are
@@ -410,22 +528,16 @@
target: BasicBlock) {
debug!("exit_scope(region_scope={:?}, block={:?}, target={:?})",
region_scope, block, target);
- let scope_count = 1 + self.scopes.iter().rev()
- .position(|scope| scope.region_scope == region_scope.0)
- .unwrap_or_else(|| {
- span_bug!(span, "region_scope {:?} does not enclose", region_scope)
- });
- let len = self.scopes.len();
- assert!(scope_count < len, "should not use `exit_scope` to pop ALL scopes");
+ let scope_count = self.scopes.num_scopes_to(region_scope, span);
// If we are emitting a `drop` statement, we need to have the cached
// diverge cleanup pads ready in case that drop panics.
- let may_panic = self.scopes[(len - scope_count)..].iter().any(|s| s.needs_cleanup);
+ let may_panic = self.scopes.may_panic(scope_count);
if may_panic {
self.diverge_cleanup();
}
- let mut scopes = self.scopes[(len - scope_count - 1)..].iter_mut().rev();
+ let mut scopes = self.scopes.top_scopes(scope_count + 1).rev();
let mut scope = scopes.next().unwrap();
for next_scope in scopes {
if scope.drops.is_empty() {
@@ -466,9 +578,8 @@
scope = next_scope;
}
- let scope = &self.scopes[len - scope_count];
- self.cfg.terminate(block, scope.source_info(span),
- TerminatorKind::Goto { target });
+ let source_info = self.scopes.source_info(scope_count, span);
+ self.cfg.terminate(block, source_info, TerminatorKind::Goto { target });
}
/// Creates a path that performs all required cleanup for dropping a generator.
@@ -479,9 +590,9 @@
// Fill in the cache for unwinds
self.diverge_cleanup_gen(true);
- let src_info = self.scopes[0].source_info(self.fn_span);
+ let src_info = self.scopes.source_info(self.scopes.len(), self.fn_span);
let resume_block = self.resume_block();
- let mut scopes = self.scopes.iter_mut().rev().peekable();
+ let mut scopes = self.scopes.iter_mut().peekable();
let mut block = self.cfg.start_new_block();
let result = block;
@@ -547,22 +658,6 @@
scope
}
- // Finding scopes
- // ==============
- /// Finds the breakable scope for a given label. This is used for
- /// resolving `break` and `continue`.
- pub fn find_breakable_scope(&self,
- span: Span,
- label: region::Scope)
- -> &BreakableScope<'tcx> {
- // find the loop-scope with the correct id
- self.breakable_scopes.iter()
- .rev()
- .filter(|breakable_scope| breakable_scope.region_scope == label)
- .next()
- .unwrap_or_else(|| span_bug!(span, "no enclosing breakable scope found"))
- }
-
/// Given a span and the current source scope, make a SourceInfo.
pub fn source_info(&self, span: Span) -> SourceInfo {
SourceInfo {
@@ -571,25 +666,8 @@
}
}
- /// Returns the `region::Scope` of the scope which should be exited by a
- /// return.
- pub fn region_scope_of_return_scope(&self) -> region::Scope {
- // The outermost scope (`scopes[0]`) will be the `CallSiteScope`.
- // We want `scopes[1]`, which is the `ParameterScope`.
- assert!(self.scopes.len() >= 2);
- assert!(match self.scopes[1].region_scope.data {
- region::ScopeData::Arguments => true,
- _ => false,
- });
- self.scopes[1].region_scope
- }
-
- /// Returns the topmost active scope, which is known to be alive until
- /// the next scope expression.
- pub fn topmost_scope(&self) -> region::Scope {
- self.scopes.last().expect("topmost_scope: no scopes present").region_scope
- }
-
+ // Finding scopes
+ // ==============
/// Returns the scope that we should use as the lifetime of an
/// operand. Basically, an operand must live until it is consumed.
/// This is similar to, but not quite the same as, the temporary
@@ -620,33 +698,32 @@
None,
hir::BodyOwnerKind::Closure |
hir::BodyOwnerKind::Fn =>
- Some(self.topmost_scope()),
+ Some(self.scopes.topmost()),
}
}
// Schedule an abort block - this is used for some ABIs that cannot unwind
pub fn schedule_abort(&mut self) -> BasicBlock {
- self.scopes[0].needs_cleanup = true;
+ let source_info = self.scopes.source_info(self.scopes.len(), self.fn_span);
let abortblk = self.cfg.start_new_cleanup_block();
- let source_info = self.scopes[0].source_info(self.fn_span);
self.cfg.terminate(abortblk, source_info, TerminatorKind::Abort);
self.cached_resume_block = Some(abortblk);
abortblk
}
+ // Scheduling drops
+ // ================
pub fn schedule_drop_storage_and_value(
&mut self,
span: Span,
region_scope: region::Scope,
- place: &Place<'tcx>,
+ local: Local,
place_ty: Ty<'tcx>,
) {
- self.schedule_drop(span, region_scope, place, place_ty, DropKind::Storage);
- self.schedule_drop(span, region_scope, place, place_ty, DropKind::Value);
+ self.schedule_drop(span, region_scope, local, place_ty, DropKind::Storage);
+ self.schedule_drop(span, region_scope, local, place_ty, DropKind::Value);
}
- // Scheduling drops
- // ================
/// Indicates that `place` should be dropped on exit from
/// `region_scope`.
///
@@ -656,7 +733,7 @@
&mut self,
span: Span,
region_scope: region::Scope,
- place: &Place<'tcx>,
+ local: Local,
place_ty: Ty<'tcx>,
drop_kind: DropKind,
) {
@@ -664,22 +741,17 @@
match drop_kind {
DropKind::Value => if !needs_drop { return },
DropKind::Storage => {
- match *place {
- Place::Base(PlaceBase::Local(index)) => if index.index() <= self.arg_count {
- span_bug!(
- span, "`schedule_drop` called with index {} and arg_count {}",
- index.index(),
- self.arg_count,
- )
- },
- _ => span_bug!(
- span, "`schedule_drop` called with non-`Local` place {:?}", place
- ),
+ if local.index() <= self.arg_count {
+ span_bug!(
+ span, "`schedule_drop` called with local {:?} and arg_count {}",
+ local,
+ self.arg_count,
+ )
}
}
}
- for scope in self.scopes.iter_mut().rev() {
+ for scope in self.scopes.iter_mut() {
let this_scope = scope.region_scope == region_scope;
// When building drops, we try to cache chains of drops in such a way so these drops
// could be reused by the drops which would branch into the cached (already built)
@@ -740,18 +812,84 @@
scope.drops.push(DropData {
span: scope_end,
- location: place.clone(),
+ local,
kind: drop_kind,
cached_block: CachedBlock::default(),
});
return;
}
}
- span_bug!(span, "region scope {:?} not in scope to drop {:?}", region_scope, place);
+ span_bug!(span, "region scope {:?} not in scope to drop {:?}", region_scope, local);
}
// Other
// =====
+ /// Branch based on a boolean condition.
+ ///
+ /// This is a special case because the temporary for the condition needs to
+ /// be dropped on both the true and the false arm.
+ pub fn test_bool(
+ &mut self,
+ mut block: BasicBlock,
+ condition: Expr<'tcx>,
+ source_info: SourceInfo,
+ ) -> (BasicBlock, BasicBlock) {
+ let cond = unpack!(block = self.as_local_operand(block, condition));
+ let true_block = self.cfg.start_new_block();
+ let false_block = self.cfg.start_new_block();
+ let term = TerminatorKind::if_(
+ self.hir.tcx(),
+ cond.clone(),
+ true_block,
+ false_block,
+ );
+ self.cfg.terminate(block, source_info, term);
+
+ match cond {
+ // Don't try to drop a constant
+ Operand::Constant(_) => (),
+ // If constants and statics, we don't generate StorageLive for this
+ // temporary, so don't try to generate StorageDead for it either.
+ _ if self.local_scope().is_none() => (),
+ Operand::Copy(Place::Base(PlaceBase::Local(cond_temp)))
+ | Operand::Move(Place::Base(PlaceBase::Local(cond_temp))) => {
+ // Manually drop the condition on both branches.
+ let top_scope = self.scopes.scopes.last_mut().unwrap();
+ let top_drop_data = top_scope.drops.pop().unwrap();
+
+ match top_drop_data.kind {
+ DropKind::Value { .. } => {
+ bug!("Drop scheduled on top of condition variable")
+ }
+ DropKind::Storage => {
+ let source_info = top_scope.source_info(top_drop_data.span);
+ let local = top_drop_data.local;
+ assert_eq!(local, cond_temp, "Drop scheduled on top of condition");
+ self.cfg.push(
+ true_block,
+ Statement {
+ source_info,
+ kind: StatementKind::StorageDead(local)
+ },
+ );
+ self.cfg.push(
+ false_block,
+ Statement {
+ source_info,
+ kind: StatementKind::StorageDead(local)
+ },
+ );
+ }
+ }
+
+ top_scope.invalidate_cache(true, self.is_generator, true);
+ }
+ _ => bug!("Expected as_local_operand to produce a temporary"),
+ }
+
+ (true_block, false_block)
+ }
+
/// Creates a path that performs all required cleanup for unwinding.
///
/// This path terminates in Resume. Returns the start of the path.
@@ -790,14 +928,15 @@
// Find the last cached block
debug!("diverge_cleanup_gen(self.scopes = {:?})", self.scopes);
- let (mut target, first_uncached) = if let Some(cached_index) = self.scopes.iter()
- .rposition(|scope| scope.cached_unwind.get(generator_drop).is_some()) {
- (self.scopes[cached_index].cached_unwind.get(generator_drop).unwrap(), cached_index + 1)
- } else {
- (self.resume_block(), 0)
- };
+ let cached_cleanup = self.scopes.iter_mut().enumerate()
+ .find_map(|(idx, ref scope)| {
+ let cached_block = scope.cached_unwind.get(generator_drop)?;
+ Some((cached_block, idx))
+ });
+ let (mut target, first_uncached) = cached_cleanup
+ .unwrap_or_else(|| (self.resume_block(), self.scopes.len()));
- for scope in self.scopes[first_uncached..].iter_mut() {
+ for scope in self.scopes.top_scopes(first_uncached) {
target = build_diverge_scope(&mut self.cfg, scope.region_scope_span,
scope, target, generator_drop, self.is_generator);
}
@@ -806,27 +945,6 @@
}
/// Utility function for *non*-scope code to build their own drops
- pub fn build_drop(&mut self,
- block: BasicBlock,
- span: Span,
- location: Place<'tcx>,
- ty: Ty<'tcx>) -> BlockAnd<()> {
- if !self.hir.needs_drop(ty) {
- return block.unit();
- }
- let source_info = self.source_info(span);
- let next_target = self.cfg.start_new_block();
- let diverge_target = self.diverge_cleanup();
- self.cfg.terminate(block, source_info,
- TerminatorKind::Drop {
- location,
- target: next_target,
- unwind: Some(diverge_target),
- });
- next_target.unit()
- }
-
- /// Utility function for *non*-scope code to build their own drops
pub fn build_drop_and_replace(&mut self,
block: BasicBlock,
span: Span,
@@ -877,73 +995,21 @@
///
/// This is only needed for `match` arm scopes, because they have one
/// entrance per pattern, but only one exit.
- pub fn clear_top_scope(&mut self, region_scope: region::Scope) {
- let top_scope = self.scopes.last_mut().unwrap();
+ pub(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);
top_scope.drops.clear();
top_scope.invalidate_cache(false, self.is_generator, true);
}
-
- /// Drops the single variable provided
- ///
- /// * The scope must be the top scope.
- /// * The variable must be in that scope.
- /// * The variable must be at the top of that scope: it's the next thing
- /// scheduled to drop.
- /// * The drop must be of `DropKind::Storage`.
- ///
- /// This is used for the boolean holding the result of the match guard. We
- /// do this because:
- ///
- /// * The boolean is different for each pattern
- /// * There is only one exit for the arm scope
- /// * The guard expression scope is too short, it ends just before the
- /// boolean is tested.
- pub fn pop_variable(
- &mut self,
- block: BasicBlock,
- region_scope: region::Scope,
- variable: Local,
- ) {
- let top_scope = self.scopes.last_mut().unwrap();
-
- assert_eq!(top_scope.region_scope, region_scope);
-
- let top_drop_data = top_scope.drops.pop().unwrap();
-
- match top_drop_data.kind {
- DropKind::Value { .. } => {
- bug!("Should not be calling pop_top_variable on non-copy type!")
- }
- DropKind::Storage => {
- // Drop the storage for both value and storage drops.
- // Only temps and vars need their storage dead.
- match top_drop_data.location {
- Place::Base(PlaceBase::Local(index)) => {
- let source_info = top_scope.source_info(top_drop_data.span);
- assert_eq!(index, variable);
- self.cfg.push(block, Statement {
- source_info,
- kind: StatementKind::StorageDead(index)
- });
- }
- _ => unreachable!(),
- }
- }
- }
-
- top_scope.invalidate_cache(true, self.is_generator, true);
- }
-
}
/// Builds drops for pop_scope and exit_scope.
fn build_scope_drops<'tcx>(
cfg: &mut CFG<'tcx>,
is_generator: bool,
- scope: &Scope<'tcx>,
+ scope: &Scope,
mut block: BasicBlock,
last_unwind_to: BasicBlock,
arg_count: usize,
@@ -973,6 +1039,7 @@
for drop_idx in (0..scope.drops.len()).rev() {
let drop_data = &scope.drops[drop_idx];
let source_info = scope.source_info(drop_data.span);
+ let local = drop_data.local;
match drop_data.kind {
DropKind::Value => {
let unwind_to = get_unwind_to(scope, is_generator, drop_idx, generator_drop)
@@ -980,32 +1047,27 @@
let next = cfg.start_new_block();
cfg.terminate(block, source_info, TerminatorKind::Drop {
- location: drop_data.location.clone(),
+ location: local.into(),
target: next,
unwind: Some(unwind_to)
});
block = next;
}
DropKind::Storage => {
- // Drop the storage for both value and storage drops.
// Only temps and vars need their storage dead.
- match drop_data.location {
- Place::Base(PlaceBase::Local(index)) if index.index() > arg_count => {
- cfg.push(block, Statement {
- source_info,
- kind: StatementKind::StorageDead(index)
- });
- }
- _ => unreachable!(),
- }
+ assert!(local.index() > arg_count);
+ cfg.push(block, Statement {
+ source_info,
+ kind: StatementKind::StorageDead(local)
+ });
}
}
}
block.unit()
}
-fn get_unwind_to<'tcx>(
- scope: &Scope<'tcx>,
+fn get_unwind_to(
+ scope: &Scope,
is_generator: bool,
unwind_from: usize,
generator_drop: bool,
@@ -1031,7 +1093,7 @@
fn build_diverge_scope<'tcx>(cfg: &mut CFG<'tcx>,
span: Span,
- scope: &mut Scope<'tcx>,
+ scope: &mut Scope,
mut target: BasicBlock,
generator_drop: bool,
is_generator: bool)
@@ -1075,26 +1137,20 @@
// this is not what clang does.
match drop_data.kind {
DropKind::Storage if is_generator => {
- // Only temps and vars need their storage dead.
- match drop_data.location {
- Place::Base(PlaceBase::Local(index)) => {
- storage_deads.push(Statement {
- source_info: source_info(drop_data.span),
- kind: StatementKind::StorageDead(index)
- });
- if !target_built_by_us {
- // We cannot add statements to an existing block, so we create a new
- // block for our StorageDead statements.
- let block = cfg.start_new_cleanup_block();
- let source_info = SourceInfo { span: DUMMY_SP, scope: source_scope };
- cfg.terminate(block, source_info,
- TerminatorKind::Goto { target: target });
- target = block;
- target_built_by_us = true;
- }
- }
- _ => unreachable!(),
- };
+ storage_deads.push(Statement {
+ source_info: source_info(drop_data.span),
+ kind: StatementKind::StorageDead(drop_data.local)
+ });
+ if !target_built_by_us {
+ // We cannot add statements to an existing block, so we create a new
+ // block for our StorageDead statements.
+ let block = cfg.start_new_cleanup_block();
+ let source_info = SourceInfo { span: DUMMY_SP, scope: source_scope };
+ cfg.terminate(block, source_info,
+ TerminatorKind::Goto { target: target });
+ target = block;
+ target_built_by_us = true;
+ }
*drop_data.cached_block.ref_mut(generator_drop) = Some(target);
}
DropKind::Storage => {}
@@ -1107,12 +1163,15 @@
} else {
push_storage_deads(cfg, target, &mut storage_deads);
let block = cfg.start_new_cleanup_block();
- cfg.terminate(block, source_info(drop_data.span),
- TerminatorKind::Drop {
- location: drop_data.location.clone(),
- target,
- unwind: None
- });
+ cfg.terminate(
+ block,
+ source_info(drop_data.span),
+ TerminatorKind::Drop {
+ location: drop_data.local.into(),
+ target,
+ unwind: None
+ },
+ );
*cached_block = Some(block);
target_built_by_us = true;
block
diff --git a/src/librustc_mir/hair/cx/block.rs b/src/librustc_mir/hair/cx/block.rs
index d593205..9a73842 100644
--- a/src/librustc_mir/hair/cx/block.rs
+++ b/src/librustc_mir/hair/cx/block.rs
@@ -49,7 +49,6 @@
for (index, stmt) in stmts.iter().enumerate() {
let hir_id = stmt.hir_id;
let opt_dxn_ext = cx.region_scope_tree.opt_destruction_scope(hir_id.local_id);
- let stmt_span = StatementSpan(cx.tcx.hir().span(hir_id));
match stmt.node {
hir::StmtKind::Expr(ref expr) |
hir::StmtKind::Semi(ref expr) => {
@@ -62,7 +61,6 @@
expr: expr.to_ref(),
},
opt_destruction_scope: opt_dxn_ext,
- span: stmt_span,
})))
}
hir::StmtKind::Item(..) => {
@@ -107,7 +105,6 @@
lint_level: LintLevel::Explicit(local.hir_id),
},
opt_destruction_scope: opt_dxn_ext,
- span: stmt_span,
})));
}
}
diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs
index 4694241..5431a31 100644
--- a/src/librustc_mir/hair/mod.rs
+++ b/src/librustc_mir/hair/mod.rs
@@ -56,13 +56,9 @@
}
#[derive(Clone, Debug)]
-pub struct StatementSpan(pub Span);
-
-#[derive(Clone, Debug)]
pub struct Stmt<'tcx> {
pub kind: StmtKind<'tcx>,
pub opt_destruction_scope: Option<region::Scope>,
- pub span: StatementSpan,
}
#[derive(Clone, Debug)]
diff --git a/src/test/mir-opt/box_expr.rs b/src/test/mir-opt/box_expr.rs
index 9f55d84..d9fa3d3 100644
--- a/src/test/mir-opt/box_expr.rs
+++ b/src/test/mir-opt/box_expr.rs
@@ -24,7 +24,7 @@
// let mut _0: ();
// let _1: std::boxed::Box<S>;
// let mut _2: std::boxed::Box<S>;
-// let mut _3: ();
+// let _3: ();
// let mut _4: std::boxed::Box<S>;
// scope 1 {
// }
@@ -50,6 +50,7 @@
//
// bb4: {
// StorageDead(_2);
+// StorageLive(_3);
// StorageLive(_4);
// _4 = move _1;
// _3 = const std::mem::drop::<std::boxed::Box<S>>(move _4) -> [return: bb5, unwind: bb7];
@@ -69,6 +70,7 @@
//
// bb8: {
// StorageDead(_4);
+// StorageDead(_3);
// _0 = ();
// drop(_1) -> bb9;
// }
diff --git a/src/test/mir-opt/copy_propagation_arg.rs b/src/test/mir-opt/copy_propagation_arg.rs
index 4e05484..5e5fed1 100644
--- a/src/test/mir-opt/copy_propagation_arg.rs
+++ b/src/test/mir-opt/copy_propagation_arg.rs
@@ -61,12 +61,14 @@
// END rustc.foo.CopyPropagation.after.mir
// START rustc.bar.CopyPropagation.before.mir
// bb0: {
+// StorageLive(_2);
// StorageLive(_3);
// _3 = _1;
// _2 = const dummy(move _3) -> bb1;
// }
// bb1: {
// StorageDead(_3);
+// StorageDead(_2);
// _1 = const 5u8;
// ...
// return;
diff --git a/src/test/mir-opt/generator-drop-cleanup.rs b/src/test/mir-opt/generator-drop-cleanup.rs
index 30f6d0d..f97e1ba 100644
--- a/src/test/mir-opt/generator-drop-cleanup.rs
+++ b/src/test/mir-opt/generator-drop-cleanup.rs
@@ -18,6 +18,7 @@
// }
// bb1: {
// StorageDead(_3);
+// StorageDead(_2);
// goto -> bb5;
// }
// bb2: {
@@ -36,6 +37,7 @@
// goto -> bb3;
// }
// bb7: {
+// StorageLive(_2);
// StorageLive(_3);
// goto -> bb1;
// }
diff --git a/src/test/mir-opt/generator-storage-dead-unwind.rs b/src/test/mir-opt/generator-storage-dead-unwind.rs
index 7be17c4..bcdb937 100644
--- a/src/test/mir-opt/generator-storage-dead-unwind.rs
+++ b/src/test/mir-opt/generator-storage-dead-unwind.rs
@@ -54,6 +54,7 @@
// }
// bb2: {
// ...
+// StorageLive(_6);
// StorageLive(_7);
// _7 = move _2;
// _6 = const take::<Foo>(move _7) -> [return: bb9, unwind: bb8];
@@ -81,16 +82,20 @@
// }
// bb8 (cleanup): {
// StorageDead(_7);
+// StorageDead(_6);
// goto -> bb7;
// }
// bb9: {
// StorageDead(_7);
+// StorageDead(_6);
+// StorageLive(_8);
// StorageLive(_9);
// _9 = move _3;
// _8 = const take::<Bar>(move _9) -> [return: bb10, unwind: bb11];
// }
// bb10: {
// StorageDead(_9);
+// StorageDead(_8);
// ...
// StorageDead(_3);
// StorageDead(_2);
@@ -98,6 +103,7 @@
// }
// bb11 (cleanup): {
// StorageDead(_9);
+// StorageDead(_8);
// goto -> bb7;
// }
// bb12: {
diff --git a/src/test/mir-opt/issue-38669.rs b/src/test/mir-opt/issue-38669.rs
index 909f9b7..d980cc8 100644
--- a/src/test/mir-opt/issue-38669.rs
+++ b/src/test/mir-opt/issue-38669.rs
@@ -25,6 +25,7 @@
// falseUnwind -> [real: bb3, cleanup: bb1];
// }
// bb3: {
+// StorageLive(_3);
// StorageLive(_4);
// _4 = _1;
// FakeRead(ForMatchedPlace, _4);
@@ -34,6 +35,7 @@
// bb5: {
// _3 = ();
// StorageDead(_4);
+// StorageDead(_3);
// _1 = const true;
// _2 = ();
// goto -> bb2;
@@ -41,6 +43,7 @@
// bb6: {
// _0 = ();
// StorageDead(_4);
+// StorageDead(_3);
// StorageDead(_1);
// return;
// }
diff --git a/src/test/mir-opt/issue-41110.rs b/src/test/mir-opt/issue-41110.rs
index 0b678be..e73390f 100644
--- a/src/test/mir-opt/issue-41110.rs
+++ b/src/test/mir-opt/issue-41110.rs
@@ -42,7 +42,7 @@
// START rustc.test.ElaborateDrops.after.mir
// let mut _0: ();
// let _1: S;
-// let mut _3: ();
+// let _3: ();
// let mut _4: S;
// let mut _5: S;
// let mut _6: bool;
diff --git a/src/test/mir-opt/issue-49232.rs b/src/test/mir-opt/issue-49232.rs
index 9dde6d8..d0dbcbd 100644
--- a/src/test/mir-opt/issue-49232.rs
+++ b/src/test/mir-opt/issue-49232.rs
@@ -21,7 +21,7 @@
// let _2: i32;
// let mut _3: bool;
// let mut _4: !;
-// let mut _5: ();
+// let _5: ();
// let mut _6: &i32;
// scope 1 {
// }
@@ -73,12 +73,14 @@
// bb12: {
// FakeRead(ForLet, _2);
// StorageDead(_3);
+// StorageLive(_5);
// StorageLive(_6);
// _6 = &_2;
// _5 = const std::mem::drop::<&i32>(move _6) -> [return: bb13, unwind: bb4];
// }
// bb13: {
// StorageDead(_6);
+// StorageDead(_5);
// _1 = ();
// StorageDead(_2);
// goto -> bb1;
diff --git a/src/test/mir-opt/loop_test.rs b/src/test/mir-opt/loop_test.rs
index 68ea60d..177080c 100644
--- a/src/test/mir-opt/loop_test.rs
+++ b/src/test/mir-opt/loop_test.rs
@@ -25,6 +25,7 @@
// bb3: { // Entry into the loop
// _1 = ();
// StorageDead(_2);
+// StorageDead(_1);
// goto -> bb5;
// }
// ...
diff --git a/src/test/mir-opt/match-arm-scopes.rs b/src/test/mir-opt/match-arm-scopes.rs
index a2bc238..18e0642 100644
--- a/src/test/mir-opt/match-arm-scopes.rs
+++ b/src/test/mir-opt/match-arm-scopes.rs
@@ -103,10 +103,6 @@
// bb10: { // `else` block - first time
// _9 = (*_6);
// StorageDead(_10);
-// FakeRead(ForMatchGuard, _3);
-// FakeRead(ForMatchGuard, _4);
-// FakeRead(ForGuardBinding, _6);
-// FakeRead(ForGuardBinding, _8);
// switchInt(move _9) -> [false: bb16, otherwise: bb15];
// }
// bb11: { // `return 3` - first time
@@ -128,6 +124,10 @@
// }
// bb15: {
// StorageDead(_9);
+// FakeRead(ForMatchGuard, _3);
+// FakeRead(ForMatchGuard, _4);
+// FakeRead(ForGuardBinding, _6);
+// FakeRead(ForGuardBinding, _8);
// StorageLive(_5);
// _5 = (_2.1: bool);
// StorageLive(_7);
@@ -159,10 +159,6 @@
// bb19: { // `else` block - second time
// _12 = (*_6);
// StorageDead(_13);
-// FakeRead(ForMatchGuard, _3);
-// FakeRead(ForMatchGuard, _4);
-// FakeRead(ForGuardBinding, _6);
-// FakeRead(ForGuardBinding, _8);
// switchInt(move _12) -> [false: bb22, otherwise: bb21];
// }
// bb20: {
@@ -175,6 +171,10 @@
// }
// bb21: { // bindings for arm 1
// StorageDead(_12);
+// FakeRead(ForMatchGuard, _3);
+// FakeRead(ForMatchGuard, _4);
+// FakeRead(ForGuardBinding, _6);
+// FakeRead(ForGuardBinding, _8);
// StorageLive(_5);
// _5 = (_2.0: bool);
// StorageLive(_7);
diff --git a/src/test/mir-opt/match_false_edges.rs b/src/test/mir-opt/match_false_edges.rs
index a62e1b2..b275c06 100644
--- a/src/test/mir-opt/match_false_edges.rs
+++ b/src/test/mir-opt/match_false_edges.rs
@@ -71,12 +71,12 @@
// _7 = const guard() -> [return: bb7, unwind: bb1];
// }
// bb7: { // end of guard
-// FakeRead(ForMatchGuard, _4);
-// FakeRead(ForGuardBinding, _6);
// switchInt(move _7) -> [false: bb9, otherwise: bb8];
// }
// bb8: { // arm1
// StorageDead(_7);
+// FakeRead(ForMatchGuard, _4);
+// FakeRead(ForGuardBinding, _6);
// StorageLive(_5);
// _5 = ((_2 as Some).0: i32);
// StorageLive(_8);
@@ -138,12 +138,12 @@
// _7 = const guard() -> [return: bb6, unwind: bb1];
// }
// bb6: { // end of guard
-// FakeRead(ForMatchGuard, _4);
-// FakeRead(ForGuardBinding, _6);
// switchInt(move _7) -> [false: bb8, otherwise: bb7];
// }
// bb7: {
// StorageDead(_7);
+// FakeRead(ForMatchGuard, _4);
+// FakeRead(ForGuardBinding, _6);
// StorageLive(_5);
// _5 = ((_2 as Some).0: i32);
// StorageLive(_8);
@@ -209,12 +209,12 @@
// _8 = const guard() -> [return: bb6, unwind: bb1];
// }
// bb6: { //end of guard1
-// FakeRead(ForMatchGuard, _5);
-// FakeRead(ForGuardBinding, _7);
// switchInt(move _8) -> [false: bb8, otherwise: bb7];
// }
// bb7: {
// StorageDead(_8);
+// FakeRead(ForMatchGuard, _5);
+// FakeRead(ForGuardBinding, _7);
// StorageLive(_6);
// _6 = ((_2 as Some).0: i32);
// _1 = const 1i32;
@@ -245,12 +245,12 @@
// }
// bb11: { // end of guard2
// StorageDead(_13);
-// FakeRead(ForMatchGuard, _5);
-// FakeRead(ForGuardBinding, _11);
// switchInt(move _12) -> [false: bb13, otherwise: bb12];
// }
// bb12: { // binding4 & arm4
// StorageDead(_12);
+// FakeRead(ForMatchGuard, _5);
+// FakeRead(ForGuardBinding, _11);
// StorageLive(_10);
// _10 = ((_2 as Some).0: i32);
// _1 = const 3i32;
diff --git a/src/test/mir-opt/match_test.rs b/src/test/mir-opt/match_test.rs
index ef60a04..1ca75b1 100644
--- a/src/test/mir-opt/match_test.rs
+++ b/src/test/mir-opt/match_test.rs
@@ -54,11 +54,11 @@
// _8 = &shallow _1;
// StorageLive(_9);
// _9 = _2;
-// FakeRead(ForMatchGuard, _8);
// switchInt(move _9) -> [false: bb11, otherwise: bb10];
// }
// bb10: {
// StorageDead(_9);
+// FakeRead(ForMatchGuard, _8);
// _3 = const 0i32;
// goto -> bb14;
// }
@@ -75,6 +75,7 @@
// goto -> bb14;
// }
// bb14: {
+// StorageDead(_3);
// _0 = ();
// StorageDead(_2);
// StorageDead(_1);
diff --git a/src/test/mir-opt/nll/region-subtyping-basic.rs b/src/test/mir-opt/nll/region-subtyping-basic.rs
index fa0dbe5..8228d97 100644
--- a/src/test/mir-opt/nll/region-subtyping-basic.rs
+++ b/src/test/mir-opt/nll/region-subtyping-basic.rs
@@ -22,9 +22,9 @@
// END RUST SOURCE
// START rustc.main.nll.0.mir
-// | '_#2r | U0 | {bb2[0..=8], bb3[0], bb5[0..=1]}
-// | '_#3r | U0 | {bb2[1..=8], bb3[0], bb5[0..=1]}
-// | '_#4r | U0 | {bb2[4..=8], bb3[0], bb5[0..=1]}
+// | '_#2r | U0 | {bb2[0..=8], bb3[0], bb5[0..=2]}
+// | '_#3r | U0 | {bb2[1..=8], bb3[0], bb5[0..=2]}
+// | '_#4r | U0 | {bb2[4..=8], bb3[0], bb5[0..=2]}
// END rustc.main.nll.0.mir
// START rustc.main.nll.0.mir
// let _2: &'_#3r usize;
diff --git a/src/test/mir-opt/remove_fake_borrows.rs b/src/test/mir-opt/remove_fake_borrows.rs
index 0f9c6f6..3245d38 100644
--- a/src/test/mir-opt/remove_fake_borrows.rs
+++ b/src/test/mir-opt/remove_fake_borrows.rs
@@ -38,14 +38,14 @@
// _7 = &shallow (*(*((_1 as Some).0: &'<empty> &'<empty> i32)));
// StorageLive(_8);
// _8 = _2;
-// FakeRead(ForMatchGuard, _4);
-// FakeRead(ForMatchGuard, _5);
-// FakeRead(ForMatchGuard, _6);
-// FakeRead(ForMatchGuard, _7);
// switchInt(move _8) -> [false: bb6, otherwise: bb5];
// }
// bb5: {
// StorageDead(_8);
+// FakeRead(ForMatchGuard, _4);
+// FakeRead(ForMatchGuard, _5);
+// FakeRead(ForMatchGuard, _6);
+// FakeRead(ForMatchGuard, _7);
// _0 = const 0i32;
// goto -> bb7;
// }
@@ -84,14 +84,14 @@
// nop;
// StorageLive(_8);
// _8 = _2;
-// nop;
-// nop;
-// nop;
-// nop;
// switchInt(move _8) -> [false: bb6, otherwise: bb5];
// }
// bb5: {
// StorageDead(_8);
+// nop;
+// nop;
+// nop;
+// nop;
// _0 = const 0i32;
// goto -> bb7;
// }
diff --git a/src/test/mir-opt/storage_ranges.rs b/src/test/mir-opt/storage_ranges.rs
index 6d22e9c..95570ff 100644
--- a/src/test/mir-opt/storage_ranges.rs
+++ b/src/test/mir-opt/storage_ranges.rs
@@ -12,6 +12,7 @@
// StorageLive(_1);
// _1 = const 0i32;
// FakeRead(ForLet, _1);
+// StorageLive(_2);
// StorageLive(_3);
// StorageLive(_4);
// StorageLive(_5);
@@ -23,6 +24,7 @@
// _2 = ();
// StorageDead(_4);
// StorageDead(_3);
+// StorageDead(_2);
// StorageLive(_6);
// _6 = const 1i32;
// FakeRead(ForLet, _6);
diff --git a/src/test/mir-opt/while-storage.rs b/src/test/mir-opt/while-storage.rs
new file mode 100644
index 0000000..a486bd4
--- /dev/null
+++ b/src/test/mir-opt/while-storage.rs
@@ -0,0 +1,59 @@
+// Test that we correctly generate StorageDead statements for while loop
+// conditions on all branches
+
+fn get_bool(c: bool) -> bool {
+ c
+}
+
+fn while_loop(c: bool) {
+ while get_bool(c) {
+ if get_bool(c) {
+ break;
+ }
+ }
+}
+
+fn main() {
+ while_loop(false);
+}
+
+// END RUST SOURCE
+
+// START rustc.while_loop.PreCodegen.after.mir
+// bb0: {
+// StorageLive(_2);
+// StorageLive(_3);
+// _3 = _1;
+// _2 = const get_bool(move _3) -> bb2;
+// }
+// bb1: {
+// return;
+// }
+// bb2: {
+// StorageDead(_3);
+// switchInt(move _2) -> [false: bb4, otherwise: bb3];
+// }
+// bb3: {
+// StorageDead(_2);
+// StorageLive(_4);
+// StorageLive(_5);
+// _5 = _1;
+// _4 = const get_bool(move _5) -> bb5;
+// }
+// bb4: {
+// StorageDead(_2);
+// goto -> bb1;
+// }
+// bb5: {
+// StorageDead(_5);
+// switchInt(_4) -> [false: bb6, otherwise: bb7];
+// }
+// bb6: {
+// StorageDead(_4);
+// goto -> bb0;
+// }
+// bb7: {
+// StorageDead(_4);
+// goto -> bb1;
+// }
+// END rustc.while_loop.PreCodegen.after.mir
diff --git a/src/test/ui/generator/issue-61442-stmt-expr-with-drop.rs b/src/test/ui/generator/issue-61442-stmt-expr-with-drop.rs
new file mode 100644
index 0000000..ce46420
--- /dev/null
+++ b/src/test/ui/generator/issue-61442-stmt-expr-with-drop.rs
@@ -0,0 +1,32 @@
+// Test that we don't consider temporaries for statement expressions as live
+// across yields
+
+// check-pass
+// edition:2018
+
+#![feature(async_await, generators, generator_trait)]
+
+use std::ops::Generator;
+
+async fn drop_and_await() {
+ async {};
+ async {}.await;
+}
+
+fn drop_and_yield() {
+ let x = || {
+ String::new();
+ yield;
+ };
+ Box::pin(x).as_mut().resume();
+ let y = static || {
+ String::new();
+ yield;
+ };
+ Box::pin(y).as_mut().resume();
+}
+
+fn main() {
+ drop_and_await();
+ drop_and_yield();
+}
diff --git a/src/test/ui/nll/assign-while-to-immutable.rs b/src/test/ui/nll/assign-while-to-immutable.rs
new file mode 100644
index 0000000..c803321
--- /dev/null
+++ b/src/test/ui/nll/assign-while-to-immutable.rs
@@ -0,0 +1,11 @@
+// We used to incorrectly assign to `x` twice when generating MIR for this
+// function, preventing this from compiling.
+
+// check-pass
+
+fn main() {
+ let x = while false {
+ break;
+ };
+ let y = 'l: while break 'l {};
+}