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
This commit is contained in:
commit
d3e2cec292
|
@ -78,7 +78,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
|
|
||||||
let source_info = this.source_info(span);
|
let source_info = this.source_info(span);
|
||||||
for stmt in stmts {
|
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 {
|
match kind {
|
||||||
StmtKind::Expr { scope, expr } => {
|
StmtKind::Expr { scope, expr } => {
|
||||||
this.block_context.push(BlockFrame::Statement { ignores_expr_result: true });
|
this.block_context.push(BlockFrame::Statement { ignores_expr_result: true });
|
||||||
|
@ -87,7 +87,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
let si = (scope, source_info);
|
let si = (scope, source_info);
|
||||||
this.in_scope(si, LintLevel::Inherited, |this| {
|
this.in_scope(si, LintLevel::Inherited, |this| {
|
||||||
let expr = this.hir.mirror(expr);
|
let expr = this.hir.mirror(expr);
|
||||||
this.stmt_expr(block, expr, Some(stmt_span))
|
this.stmt_expr(block, expr, Some(scope))
|
||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,7 +127,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
this.schedule_drop_storage_and_value(
|
this.schedule_drop_storage_and_value(
|
||||||
expr_span,
|
expr_span,
|
||||||
scope,
|
scope,
|
||||||
&Place::from(result),
|
result,
|
||||||
value.ty,
|
value.ty,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -559,7 +559,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
this.schedule_drop_storage_and_value(
|
this.schedule_drop_storage_and_value(
|
||||||
upvar_span,
|
upvar_span,
|
||||||
temp_lifetime,
|
temp_lifetime,
|
||||||
&Place::from(temp),
|
temp,
|
||||||
upvar_ty,
|
upvar_ty,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,7 +88,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
this.schedule_drop(
|
this.schedule_drop(
|
||||||
expr_span,
|
expr_span,
|
||||||
temp_lifetime,
|
temp_lifetime,
|
||||||
temp_place,
|
temp,
|
||||||
expr_ty,
|
expr_ty,
|
||||||
DropKind::Storage,
|
DropKind::Storage,
|
||||||
);
|
);
|
||||||
|
@ -101,7 +101,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
this.schedule_drop(
|
this.schedule_drop(
|
||||||
expr_span,
|
expr_span,
|
||||||
temp_lifetime,
|
temp_lifetime,
|
||||||
temp_place,
|
temp,
|
||||||
expr_ty,
|
expr_ty,
|
||||||
DropKind::Value,
|
DropKind::Value,
|
||||||
);
|
);
|
||||||
|
|
|
@ -179,20 +179,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
// conduct the test, if necessary
|
// conduct the test, if necessary
|
||||||
let body_block;
|
let body_block;
|
||||||
if let Some(cond_expr) = opt_cond_expr {
|
if let Some(cond_expr) = opt_cond_expr {
|
||||||
let loop_block_end;
|
let cond_expr = this.hir.mirror(cond_expr);
|
||||||
let cond = unpack!(
|
let (true_block, false_block)
|
||||||
loop_block_end = this.as_local_operand(loop_block, cond_expr)
|
= this.test_bool(loop_block, cond_expr, source_info);
|
||||||
);
|
body_block = true_block;
|
||||||
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);
|
|
||||||
|
|
||||||
// if the test is false, there's no `break` to assign `destination`, so
|
// 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
|
// we have to do it
|
||||||
// always `()` anyway
|
this.cfg.push_assign_unit(false_block, source_info, destination);
|
||||||
this.cfg
|
this.cfg.terminate(
|
||||||
.push_assign_unit(exit_block, source_info, destination);
|
false_block,
|
||||||
|
source_info,
|
||||||
|
TerminatorKind::Goto { target: exit_block },
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
body_block = this.cfg.start_new_block();
|
body_block = this.cfg.start_new_block();
|
||||||
let diverge_cleanup = this.diverge_cleanup();
|
let diverge_cleanup = this.diverge_cleanup();
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::build::scope::BreakableScope;
|
use crate::build::scope::BreakableTarget;
|
||||||
use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
|
use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
|
||||||
use crate::hair::*;
|
use crate::hair::*;
|
||||||
|
use rustc::middle::region;
|
||||||
use rustc::mir::*;
|
use rustc::mir::*;
|
||||||
|
|
||||||
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
|
@ -8,14 +9,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
/// If the original expression was an AST statement,
|
/// If the original expression was an AST statement,
|
||||||
/// (e.g., `some().code(&here());`) then `opt_stmt_span` is the
|
/// (e.g., `some().code(&here());`) then `opt_stmt_span` is the
|
||||||
/// span of that statement (including its semicolon, if any).
|
/// span of that statement (including its semicolon, if any).
|
||||||
/// Diagnostics use this span (which may be larger than that of
|
/// The scope is used if a statement temporary must be dropped.
|
||||||
/// `expr`) to identify when statement temporaries are dropped.
|
pub fn stmt_expr(
|
||||||
pub fn stmt_expr(&mut self,
|
&mut self,
|
||||||
mut block: BasicBlock,
|
mut block: BasicBlock,
|
||||||
expr: Expr<'tcx>,
|
expr: Expr<'tcx>,
|
||||||
opt_stmt_span: Option<StatementSpan>)
|
statement_scope: Option<region::Scope>,
|
||||||
-> BlockAnd<()>
|
) -> BlockAnd<()> {
|
||||||
{
|
|
||||||
let this = self;
|
let this = self;
|
||||||
let expr_span = expr.span;
|
let expr_span = expr.span;
|
||||||
let source_info = this.source_info(expr.span);
|
let source_info = this.source_info(expr.span);
|
||||||
|
@ -30,7 +30,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
} => {
|
} => {
|
||||||
let value = this.hir.mirror(value);
|
let value = this.hir.mirror(value);
|
||||||
this.in_scope((region_scope, source_info), lint_level, |this| {
|
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 } => {
|
ExprKind::Assign { lhs, rhs } => {
|
||||||
|
@ -98,70 +98,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
block.unit()
|
block.unit()
|
||||||
}
|
}
|
||||||
ExprKind::Continue { label } => {
|
ExprKind::Continue { label } => {
|
||||||
let BreakableScope {
|
this.break_scope(block, None, BreakableTarget::Continue(label), source_info)
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
ExprKind::Break { label, value } => {
|
ExprKind::Break { label, value } => {
|
||||||
let (break_block, region_scope, destination) = {
|
this.break_scope(block, value, BreakableTarget::Break(label), source_info)
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
ExprKind::Return { value } => {
|
ExprKind::Return { value } => {
|
||||||
block = match value {
|
this.break_scope(block, value, BreakableTarget::Return, source_info)
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
ExprKind::InlineAsm {
|
ExprKind::InlineAsm {
|
||||||
asm,
|
asm,
|
||||||
|
@ -199,7 +142,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
block.unit()
|
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
|
// Issue #54382: When creating temp for the value of
|
||||||
// expression like:
|
// expression like:
|
||||||
|
@ -208,48 +155,34 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
//
|
//
|
||||||
// it is usually better to focus on `the_value` rather
|
// it is usually better to focus on `the_value` rather
|
||||||
// than the entirety of block(s) surrounding it.
|
// than the entirety of block(s) surrounding it.
|
||||||
let mut temp_span = expr_span;
|
let adjusted_span = (|| {
|
||||||
let mut temp_in_tail_of_block = false;
|
if let ExprKind::Block { body } = expr.kind {
|
||||||
if let ExprKind::Block { body } = expr.kind {
|
if let Some(tail_expr) = &body.expr {
|
||||||
if let Some(tail_expr) = &body.expr {
|
let mut expr = tail_expr;
|
||||||
let mut expr = tail_expr;
|
while let rustc::hir::ExprKind::Block(subblock, _label) = &expr.node {
|
||||||
while let rustc::hir::ExprKind::Block(subblock, _label) = &expr.node {
|
if let Some(subtail_expr) = &subblock.expr {
|
||||||
if let Some(subtail_expr) = &subblock.expr {
|
expr = subtail_expr
|
||||||
expr = subtail_expr
|
} else {
|
||||||
} else {
|
break;
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
}
|
this.block_context.push(BlockFrame::TailExpr {
|
||||||
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 {
|
|
||||||
tail_result_is_ignored: true
|
tail_result_is_ignored: true
|
||||||
});
|
});
|
||||||
|
return Some(expr.span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let temp = this.local_decls.push(local_decl);
|
None
|
||||||
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));
|
|
||||||
|
|
||||||
// Attribute drops of the statement's temps to the
|
let temp = unpack!(block =
|
||||||
// semicolon at the statement's end.
|
this.as_temp(block, statement_scope, expr, Mutability::Not));
|
||||||
let drop_point = this.hir.tcx().sess.source_map().end_point(match opt_stmt_span {
|
|
||||||
None => expr_span,
|
if let Some(span) = adjusted_span {
|
||||||
Some(StatementSpan(span)) => span,
|
this.local_decls[temp].source_info.span = span;
|
||||||
});
|
this.block_context.pop();
|
||||||
|
}
|
||||||
|
|
||||||
unpack!(block = this.build_drop(block, drop_point, temp, expr_ty));
|
|
||||||
block.unit()
|
block.unit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -531,11 +531,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
kind: StatementKind::StorageLive(local_id),
|
kind: StatementKind::StorageLive(local_id),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
let place = Place::from(local_id);
|
|
||||||
let var_ty = self.local_decls[local_id].ty;
|
let var_ty = self.local_decls[local_id].ty;
|
||||||
let region_scope = self.hir.region_scope_tree.var_scope(var.local_id);
|
let region_scope = self.hir.region_scope_tree.var_scope(var.local_id);
|
||||||
self.schedule_drop(span, region_scope, &place, var_ty, DropKind::Storage);
|
self.schedule_drop(span, region_scope, local_id, var_ty, DropKind::Storage);
|
||||||
place
|
Place::Base(PlaceBase::Local(local_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn schedule_drop_for_binding(&mut self, var: HirId, span: Span, for_guard: ForGuard) {
|
pub fn schedule_drop_for_binding(&mut self, var: HirId, span: Span, for_guard: ForGuard) {
|
||||||
|
@ -545,7 +544,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
self.schedule_drop(
|
self.schedule_drop(
|
||||||
span,
|
span,
|
||||||
region_scope,
|
region_scope,
|
||||||
&Place::from(local_id),
|
local_id,
|
||||||
var_ty,
|
var_ty,
|
||||||
DropKind::Value,
|
DropKind::Value,
|
||||||
);
|
);
|
||||||
|
@ -1490,7 +1489,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
};
|
};
|
||||||
let source_info = self.source_info(guard.span);
|
let source_info = self.source_info(guard.span);
|
||||||
let guard_end = self.source_info(tcx.sess.source_map().end_point(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();
|
let guard_frame = self.guard_context.pop().unwrap();
|
||||||
debug!(
|
debug!(
|
||||||
"Exiting guard building context with locals: {:?}",
|
"Exiting guard building context with locals: {:?}",
|
||||||
|
@ -1498,7 +1498,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
);
|
);
|
||||||
|
|
||||||
for &(_, temp) in fake_borrows {
|
for &(_, temp) in fake_borrows {
|
||||||
self.cfg.push(block, Statement {
|
self.cfg.push(post_guard_block, Statement {
|
||||||
source_info: guard_end,
|
source_info: guard_end,
|
||||||
kind: StatementKind::FakeRead(
|
kind: StatementKind::FakeRead(
|
||||||
FakeReadCause::ForMatchGuard,
|
FakeReadCause::ForMatchGuard,
|
||||||
|
@ -1507,6 +1507,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
// We want to ensure that the matched candidates are bound
|
||||||
// after we have confirmed this candidate *and* any
|
// after we have confirmed this candidate *and* any
|
||||||
// associated guard; Binding them on `block` is too soon,
|
// associated guard; Binding them on `block` is too soon,
|
||||||
|
@ -1533,41 +1540,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
// ```
|
// ```
|
||||||
//
|
//
|
||||||
// and that is clearly not correct.
|
// 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| {
|
let by_value_bindings = candidate.bindings.iter().filter(|binding| {
|
||||||
if let BindingMode::ByValue = binding.binding_mode { true } else { false }
|
if let BindingMode::ByValue = binding.binding_mode { true } else { false }
|
||||||
});
|
});
|
||||||
|
@ -1577,7 +1549,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
let local_id = self.var_local_id(binding.var_id, RefWithinGuard);
|
let local_id = self.var_local_id(binding.var_id, RefWithinGuard);
|
||||||
let place = Place::from(local_id);
|
let place = Place::from(local_id);
|
||||||
self.cfg.push(
|
self.cfg.push(
|
||||||
block,
|
post_guard_block,
|
||||||
Statement {
|
Statement {
|
||||||
source_info: guard_end,
|
source_info: guard_end,
|
||||||
kind: StatementKind::FakeRead(FakeReadCause::ForGuardBinding, place),
|
kind: StatementKind::FakeRead(FakeReadCause::ForGuardBinding, place),
|
||||||
|
@ -1652,11 +1624,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
// denotes *R.
|
// denotes *R.
|
||||||
let ref_for_guard =
|
let ref_for_guard =
|
||||||
self.storage_live_binding(block, binding.var_id, binding.span, RefWithinGuard);
|
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 {
|
match binding.binding_mode {
|
||||||
BindingMode::ByValue => {
|
BindingMode::ByValue => {
|
||||||
let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, binding.source.clone());
|
let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, binding.source.clone());
|
||||||
|
@ -1670,11 +1637,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
binding.span,
|
binding.span,
|
||||||
OutsideGuard,
|
OutsideGuard,
|
||||||
);
|
);
|
||||||
self.schedule_drop_for_binding(
|
|
||||||
binding.var_id,
|
|
||||||
binding.span,
|
|
||||||
OutsideGuard,
|
|
||||||
);
|
|
||||||
|
|
||||||
let rvalue = Rvalue::Ref(re_erased, borrow_kind, binding.source.clone());
|
let rvalue = Rvalue::Ref(re_erased, borrow_kind, binding.source.clone());
|
||||||
self.cfg
|
self.cfg
|
||||||
|
|
|
@ -251,7 +251,7 @@ struct Builder<'a, 'tcx> {
|
||||||
|
|
||||||
/// The current set of scopes, updated as we traverse;
|
/// The current set of scopes, updated as we traverse;
|
||||||
/// see the `scope` module for more details.
|
/// 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,
|
/// 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
|
/// we push a frame here tracking whether we are building a statement or
|
||||||
|
@ -274,10 +274,6 @@ struct Builder<'a, 'tcx> {
|
||||||
/// The number of `push_unsafe_block` levels in scope.
|
/// The number of `push_unsafe_block` levels in scope.
|
||||||
push_unsafe_count: usize,
|
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;
|
/// The vector of all scopes that we have created thus far;
|
||||||
/// we track this for debuginfo later.
|
/// we track this for debuginfo later.
|
||||||
source_scopes: IndexVec<SourceScope, SourceScopeData>,
|
source_scopes: IndexVec<SourceScope, SourceScopeData>,
|
||||||
|
@ -714,7 +710,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
fn_span: span,
|
fn_span: span,
|
||||||
arg_count,
|
arg_count,
|
||||||
is_generator,
|
is_generator,
|
||||||
scopes: vec![],
|
scopes: Default::default(),
|
||||||
block_context: BlockContext::new(),
|
block_context: BlockContext::new(),
|
||||||
source_scopes: IndexVec::new(),
|
source_scopes: IndexVec::new(),
|
||||||
source_scope: OUTERMOST_SOURCE_SCOPE,
|
source_scope: OUTERMOST_SOURCE_SCOPE,
|
||||||
|
@ -722,7 +718,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
guard_context: vec![],
|
guard_context: vec![],
|
||||||
push_unsafe_count: 0,
|
push_unsafe_count: 0,
|
||||||
unpushed_unsafe: safety,
|
unpushed_unsafe: safety,
|
||||||
breakable_scopes: vec![],
|
|
||||||
local_decls: IndexVec::from_elem_n(
|
local_decls: IndexVec::from_elem_n(
|
||||||
LocalDecl::new_return_place(return_ty, return_span),
|
LocalDecl::new_return_place(return_ty, return_span),
|
||||||
1,
|
1,
|
||||||
|
@ -814,7 +809,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
// Make sure we drop (parts of) the argument even when not matched on.
|
// Make sure we drop (parts of) the argument even when not matched on.
|
||||||
self.schedule_drop(
|
self.schedule_drop(
|
||||||
pattern.as_ref().map_or(ast_body.span, |pat| pat.span),
|
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 {
|
if let Some(pattern) = pattern {
|
||||||
|
@ -865,7 +860,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let body = self.hir.mirror(ast_body);
|
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> {
|
fn get_unit_temp(&mut self) -> Place<'tcx> {
|
||||||
|
|
|
@ -82,8 +82,8 @@ should go to.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::build::{BlockAnd, BlockAndExtension, Builder, CFG};
|
use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, CFG};
|
||||||
use crate::hair::LintLevel;
|
use crate::hair::{Expr, ExprRef, LintLevel};
|
||||||
use rustc::middle::region;
|
use rustc::middle::region;
|
||||||
use rustc::ty::Ty;
|
use rustc::ty::Ty;
|
||||||
use rustc::hir;
|
use rustc::hir;
|
||||||
|
@ -94,7 +94,7 @@ use std::collections::hash_map::Entry;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Scope<'tcx> {
|
struct Scope {
|
||||||
/// The source scope this scope was created in.
|
/// The source scope this scope was created in.
|
||||||
source_scope: SourceScope,
|
source_scope: SourceScope,
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ pub struct Scope<'tcx> {
|
||||||
/// out empty but grows as variables are declared during the
|
/// out empty but grows as variables are declared during the
|
||||||
/// building process. This is a stack, so we always drop from the
|
/// building process. This is a stack, so we always drop from the
|
||||||
/// end of the vector (top of the stack) first.
|
/// 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.
|
/// The cache for drop chain on “normal” exit into a particular BasicBlock.
|
||||||
cached_exits: FxHashMap<(BasicBlock, region::Scope), BasicBlock>,
|
cached_exits: FxHashMap<(BasicBlock, region::Scope), BasicBlock>,
|
||||||
|
@ -133,13 +133,20 @@ pub struct Scope<'tcx> {
|
||||||
cached_unwind: CachedBlock,
|
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)]
|
#[derive(Debug)]
|
||||||
struct DropData<'tcx> {
|
struct DropData {
|
||||||
/// span where drop obligation was incurred (typically where place was declared)
|
/// span where drop obligation was incurred (typically where place was declared)
|
||||||
span: Span,
|
span: Span,
|
||||||
|
|
||||||
/// place to drop
|
/// local to drop
|
||||||
location: Place<'tcx>,
|
local: Local,
|
||||||
|
|
||||||
/// Whether this is a value Drop or a StorageDead.
|
/// Whether this is a value Drop or a StorageDead.
|
||||||
kind: DropKind,
|
kind: DropKind,
|
||||||
|
@ -172,17 +179,25 @@ pub(crate) enum DropKind {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct BreakableScope<'tcx> {
|
struct BreakableScope<'tcx> {
|
||||||
/// Region scope of the loop
|
/// Region scope of the loop
|
||||||
pub region_scope: region::Scope,
|
region_scope: region::Scope,
|
||||||
/// Where the body of the loop begins. `None` if block
|
/// 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
|
/// 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)
|
/// 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
|
/// The destination of the loop/block expression itself (i.e., where to put the result of a
|
||||||
/// `break` expression)
|
/// `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 {
|
impl CachedBlock {
|
||||||
|
@ -208,16 +223,7 @@ impl CachedBlock {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DropKind {
|
impl Scope {
|
||||||
fn may_panic(&self) -> bool {
|
|
||||||
match *self {
|
|
||||||
DropKind::Value => true,
|
|
||||||
DropKind::Storage => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'tcx> Scope<'tcx> {
|
|
||||||
/// Invalidates all the cached blocks in the 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
|
/// Should always be run for all inner scopes when a drop is pushed into some scope enclosing a
|
||||||
|
@ -257,13 +263,111 @@ impl<'tcx> Scope<'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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> {
|
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
// Adding and removing scopes
|
// Adding and removing scopes
|
||||||
// ==========================
|
// ==========================
|
||||||
/// Start a breakable scope, which tracks where `continue` and `break`
|
// Start a breakable scope, which tracks where `continue`, `break` and
|
||||||
/// should branch to. See module comment for more details.
|
// `return` should branch to.
|
||||||
///
|
|
||||||
/// Returns the might_break attribute of the BreakableScope used.
|
|
||||||
pub fn in_breakable_scope<F, R>(&mut self,
|
pub fn in_breakable_scope<F, R>(&mut self,
|
||||||
loop_block: Option<BasicBlock>,
|
loop_block: Option<BasicBlock>,
|
||||||
break_block: BasicBlock,
|
break_block: BasicBlock,
|
||||||
|
@ -271,16 +375,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
f: F) -> R
|
f: F) -> R
|
||||||
where F: FnOnce(&mut Builder<'a, 'tcx>) -> R
|
where F: FnOnce(&mut Builder<'a, 'tcx>) -> R
|
||||||
{
|
{
|
||||||
let region_scope = self.topmost_scope();
|
let region_scope = self.scopes.topmost();
|
||||||
let scope = BreakableScope {
|
let scope = BreakableScope {
|
||||||
region_scope,
|
region_scope,
|
||||||
continue_block: loop_block,
|
continue_block: loop_block,
|
||||||
break_block,
|
break_block,
|
||||||
break_destination,
|
break_destination,
|
||||||
};
|
};
|
||||||
self.breakable_scopes.push(scope);
|
self.scopes.breakable_scopes.push(scope);
|
||||||
let res = f(self);
|
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);
|
assert!(breakable_scope.region_scope == region_scope);
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
@ -350,18 +454,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
/// calls must be paired; using `in_scope` as a convenience
|
/// calls must be paired; using `in_scope` as a convenience
|
||||||
/// wrapper maybe preferable.
|
/// wrapper maybe preferable.
|
||||||
pub fn push_scope(&mut self, region_scope: (region::Scope, SourceInfo)) {
|
pub fn push_scope(&mut self, region_scope: (region::Scope, SourceInfo)) {
|
||||||
debug!("push_scope({:?})", region_scope);
|
self.scopes.push_scope(region_scope, self.source_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(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pops a scope, which should have region scope `region_scope`,
|
/// Pops a scope, which should have region scope `region_scope`,
|
||||||
|
@ -374,17 +467,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
debug!("pop_scope({:?}, {:?})", region_scope, block);
|
debug!("pop_scope({:?}, {:?})", region_scope, block);
|
||||||
// If we are emitting a `drop` statement, we need to have the cached
|
// If we are emitting a `drop` statement, we need to have the cached
|
||||||
// diverge cleanup pads ready in case that drop panics.
|
// diverge cleanup pads ready in case that drop panics.
|
||||||
let may_panic =
|
if self.scopes.may_panic(1) {
|
||||||
self.scopes.last().unwrap().drops.iter().any(|s| s.kind.may_panic());
|
|
||||||
if may_panic {
|
|
||||||
self.diverge_cleanup();
|
self.diverge_cleanup();
|
||||||
}
|
}
|
||||||
let scope = self.scopes.pop().unwrap();
|
let (scope, unwind_to) = self.scopes.pop_scope(region_scope);
|
||||||
assert_eq!(scope.region_scope, region_scope.0);
|
let unwind_to = unwind_to.unwrap_or_else(|| self.resume_block());
|
||||||
|
|
||||||
let unwind_to = self.scopes.last().and_then(|next_scope| {
|
|
||||||
next_scope.cached_unwind.get(false)
|
|
||||||
}).unwrap_or_else(|| self.resume_block());
|
|
||||||
|
|
||||||
unpack!(block = build_scope_drops(
|
unpack!(block = build_scope_drops(
|
||||||
&mut self.cfg,
|
&mut self.cfg,
|
||||||
|
@ -399,6 +486,37 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
block.unit()
|
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
|
/// Branch out of `block` to `target`, exiting all scopes up to
|
||||||
/// and including `region_scope`. This will insert whatever drops are
|
/// and including `region_scope`. This will insert whatever drops are
|
||||||
|
@ -410,22 +528,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
target: BasicBlock) {
|
target: BasicBlock) {
|
||||||
debug!("exit_scope(region_scope={:?}, block={:?}, target={:?})",
|
debug!("exit_scope(region_scope={:?}, block={:?}, target={:?})",
|
||||||
region_scope, block, target);
|
region_scope, block, target);
|
||||||
let scope_count = 1 + self.scopes.iter().rev()
|
let scope_count = self.scopes.num_scopes_to(region_scope, span);
|
||||||
.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");
|
|
||||||
|
|
||||||
// If we are emitting a `drop` statement, we need to have the cached
|
// If we are emitting a `drop` statement, we need to have the cached
|
||||||
// diverge cleanup pads ready in case that drop panics.
|
// 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 {
|
if may_panic {
|
||||||
self.diverge_cleanup();
|
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();
|
let mut scope = scopes.next().unwrap();
|
||||||
for next_scope in scopes {
|
for next_scope in scopes {
|
||||||
if scope.drops.is_empty() {
|
if scope.drops.is_empty() {
|
||||||
|
@ -466,9 +578,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
scope = next_scope;
|
scope = next_scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
let scope = &self.scopes[len - scope_count];
|
let source_info = self.scopes.source_info(scope_count, span);
|
||||||
self.cfg.terminate(block, scope.source_info(span),
|
self.cfg.terminate(block, source_info, TerminatorKind::Goto { target });
|
||||||
TerminatorKind::Goto { target });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a path that performs all required cleanup for dropping a generator.
|
/// Creates a path that performs all required cleanup for dropping a generator.
|
||||||
|
@ -479,9 +590,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
// Fill in the cache for unwinds
|
// Fill in the cache for unwinds
|
||||||
self.diverge_cleanup_gen(true);
|
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 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 mut block = self.cfg.start_new_block();
|
||||||
let result = block;
|
let result = block;
|
||||||
|
|
||||||
|
@ -547,22 +658,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
scope
|
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.
|
/// Given a span and the current source scope, make a SourceInfo.
|
||||||
pub fn source_info(&self, span: Span) -> SourceInfo {
|
pub fn source_info(&self, span: Span) -> SourceInfo {
|
||||||
SourceInfo {
|
SourceInfo {
|
||||||
|
@ -571,25 +666,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the `region::Scope` of the scope which should be exited by a
|
// Finding scopes
|
||||||
/// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the scope that we should use as the lifetime of an
|
/// Returns the scope that we should use as the lifetime of an
|
||||||
/// operand. Basically, an operand must live until it is consumed.
|
/// operand. Basically, an operand must live until it is consumed.
|
||||||
/// This is similar to, but not quite the same as, the temporary
|
/// This is similar to, but not quite the same as, the temporary
|
||||||
|
@ -620,33 +698,32 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
None,
|
None,
|
||||||
hir::BodyOwnerKind::Closure |
|
hir::BodyOwnerKind::Closure |
|
||||||
hir::BodyOwnerKind::Fn =>
|
hir::BodyOwnerKind::Fn =>
|
||||||
Some(self.topmost_scope()),
|
Some(self.scopes.topmost()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Schedule an abort block - this is used for some ABIs that cannot unwind
|
// Schedule an abort block - this is used for some ABIs that cannot unwind
|
||||||
pub fn schedule_abort(&mut self) -> BasicBlock {
|
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 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.cfg.terminate(abortblk, source_info, TerminatorKind::Abort);
|
||||||
self.cached_resume_block = Some(abortblk);
|
self.cached_resume_block = Some(abortblk);
|
||||||
abortblk
|
abortblk
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Scheduling drops
|
||||||
|
// ================
|
||||||
pub fn schedule_drop_storage_and_value(
|
pub fn schedule_drop_storage_and_value(
|
||||||
&mut self,
|
&mut self,
|
||||||
span: Span,
|
span: Span,
|
||||||
region_scope: region::Scope,
|
region_scope: region::Scope,
|
||||||
place: &Place<'tcx>,
|
local: Local,
|
||||||
place_ty: Ty<'tcx>,
|
place_ty: Ty<'tcx>,
|
||||||
) {
|
) {
|
||||||
self.schedule_drop(span, region_scope, place, place_ty, DropKind::Storage);
|
self.schedule_drop(span, region_scope, local, 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::Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scheduling drops
|
|
||||||
// ================
|
|
||||||
/// Indicates that `place` should be dropped on exit from
|
/// Indicates that `place` should be dropped on exit from
|
||||||
/// `region_scope`.
|
/// `region_scope`.
|
||||||
///
|
///
|
||||||
|
@ -656,7 +733,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
&mut self,
|
&mut self,
|
||||||
span: Span,
|
span: Span,
|
||||||
region_scope: region::Scope,
|
region_scope: region::Scope,
|
||||||
place: &Place<'tcx>,
|
local: Local,
|
||||||
place_ty: Ty<'tcx>,
|
place_ty: Ty<'tcx>,
|
||||||
drop_kind: DropKind,
|
drop_kind: DropKind,
|
||||||
) {
|
) {
|
||||||
|
@ -664,22 +741,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
match drop_kind {
|
match drop_kind {
|
||||||
DropKind::Value => if !needs_drop { return },
|
DropKind::Value => if !needs_drop { return },
|
||||||
DropKind::Storage => {
|
DropKind::Storage => {
|
||||||
match *place {
|
if local.index() <= self.arg_count {
|
||||||
Place::Base(PlaceBase::Local(index)) => if index.index() <= self.arg_count {
|
span_bug!(
|
||||||
span_bug!(
|
span, "`schedule_drop` called with local {:?} and arg_count {}",
|
||||||
span, "`schedule_drop` called with index {} and arg_count {}",
|
local,
|
||||||
index.index(),
|
self.arg_count,
|
||||||
self.arg_count,
|
)
|
||||||
)
|
|
||||||
},
|
|
||||||
_ => span_bug!(
|
|
||||||
span, "`schedule_drop` called with non-`Local` place {:?}", place
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for scope in self.scopes.iter_mut().rev() {
|
for scope in self.scopes.iter_mut() {
|
||||||
let this_scope = scope.region_scope == region_scope;
|
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
|
// 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)
|
// could be reused by the drops which would branch into the cached (already built)
|
||||||
|
@ -740,18 +812,84 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
|
|
||||||
scope.drops.push(DropData {
|
scope.drops.push(DropData {
|
||||||
span: scope_end,
|
span: scope_end,
|
||||||
location: place.clone(),
|
local,
|
||||||
kind: drop_kind,
|
kind: drop_kind,
|
||||||
cached_block: CachedBlock::default(),
|
cached_block: CachedBlock::default(),
|
||||||
});
|
});
|
||||||
return;
|
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
|
// 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.
|
/// Creates a path that performs all required cleanup for unwinding.
|
||||||
///
|
///
|
||||||
/// This path terminates in Resume. Returns the start of the path.
|
/// This path terminates in Resume. Returns the start of the path.
|
||||||
|
@ -790,14 +928,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
|
|
||||||
// Find the last cached block
|
// Find the last cached block
|
||||||
debug!("diverge_cleanup_gen(self.scopes = {:?})", self.scopes);
|
debug!("diverge_cleanup_gen(self.scopes = {:?})", self.scopes);
|
||||||
let (mut target, first_uncached) = if let Some(cached_index) = self.scopes.iter()
|
let cached_cleanup = self.scopes.iter_mut().enumerate()
|
||||||
.rposition(|scope| scope.cached_unwind.get(generator_drop).is_some()) {
|
.find_map(|(idx, ref scope)| {
|
||||||
(self.scopes[cached_index].cached_unwind.get(generator_drop).unwrap(), cached_index + 1)
|
let cached_block = scope.cached_unwind.get(generator_drop)?;
|
||||||
} else {
|
Some((cached_block, idx))
|
||||||
(self.resume_block(), 0)
|
});
|
||||||
};
|
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,
|
target = build_diverge_scope(&mut self.cfg, scope.region_scope_span,
|
||||||
scope, target, generator_drop, self.is_generator);
|
scope, target, generator_drop, self.is_generator);
|
||||||
}
|
}
|
||||||
|
@ -805,27 +944,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
target
|
target
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
/// Utility function for *non*-scope code to build their own drops
|
||||||
pub fn build_drop_and_replace(&mut self,
|
pub fn build_drop_and_replace(&mut self,
|
||||||
block: BasicBlock,
|
block: BasicBlock,
|
||||||
|
@ -877,73 +995,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
///
|
///
|
||||||
/// This is only needed for `match` arm scopes, because they have one
|
/// This is only needed for `match` arm scopes, because they have one
|
||||||
/// entrance per pattern, but only one exit.
|
/// entrance per pattern, but only one exit.
|
||||||
pub fn clear_top_scope(&mut self, region_scope: region::Scope) {
|
pub(crate) fn clear_top_scope(&mut self, region_scope: region::Scope) {
|
||||||
let top_scope = self.scopes.last_mut().unwrap();
|
let top_scope = self.scopes.scopes.last_mut().unwrap();
|
||||||
|
|
||||||
assert_eq!(top_scope.region_scope, region_scope);
|
assert_eq!(top_scope.region_scope, region_scope);
|
||||||
|
|
||||||
top_scope.drops.clear();
|
top_scope.drops.clear();
|
||||||
top_scope.invalidate_cache(false, self.is_generator, true);
|
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.
|
/// Builds drops for pop_scope and exit_scope.
|
||||||
fn build_scope_drops<'tcx>(
|
fn build_scope_drops<'tcx>(
|
||||||
cfg: &mut CFG<'tcx>,
|
cfg: &mut CFG<'tcx>,
|
||||||
is_generator: bool,
|
is_generator: bool,
|
||||||
scope: &Scope<'tcx>,
|
scope: &Scope,
|
||||||
mut block: BasicBlock,
|
mut block: BasicBlock,
|
||||||
last_unwind_to: BasicBlock,
|
last_unwind_to: BasicBlock,
|
||||||
arg_count: usize,
|
arg_count: usize,
|
||||||
|
@ -973,6 +1039,7 @@ fn build_scope_drops<'tcx>(
|
||||||
for drop_idx in (0..scope.drops.len()).rev() {
|
for drop_idx in (0..scope.drops.len()).rev() {
|
||||||
let drop_data = &scope.drops[drop_idx];
|
let drop_data = &scope.drops[drop_idx];
|
||||||
let source_info = scope.source_info(drop_data.span);
|
let source_info = scope.source_info(drop_data.span);
|
||||||
|
let local = drop_data.local;
|
||||||
match drop_data.kind {
|
match drop_data.kind {
|
||||||
DropKind::Value => {
|
DropKind::Value => {
|
||||||
let unwind_to = get_unwind_to(scope, is_generator, drop_idx, generator_drop)
|
let unwind_to = get_unwind_to(scope, is_generator, drop_idx, generator_drop)
|
||||||
|
@ -980,32 +1047,27 @@ fn build_scope_drops<'tcx>(
|
||||||
|
|
||||||
let next = cfg.start_new_block();
|
let next = cfg.start_new_block();
|
||||||
cfg.terminate(block, source_info, TerminatorKind::Drop {
|
cfg.terminate(block, source_info, TerminatorKind::Drop {
|
||||||
location: drop_data.location.clone(),
|
location: local.into(),
|
||||||
target: next,
|
target: next,
|
||||||
unwind: Some(unwind_to)
|
unwind: Some(unwind_to)
|
||||||
});
|
});
|
||||||
block = next;
|
block = next;
|
||||||
}
|
}
|
||||||
DropKind::Storage => {
|
DropKind::Storage => {
|
||||||
// Drop the storage for both value and storage drops.
|
|
||||||
// Only temps and vars need their storage dead.
|
// Only temps and vars need their storage dead.
|
||||||
match drop_data.location {
|
assert!(local.index() > arg_count);
|
||||||
Place::Base(PlaceBase::Local(index)) if index.index() > arg_count => {
|
cfg.push(block, Statement {
|
||||||
cfg.push(block, Statement {
|
source_info,
|
||||||
source_info,
|
kind: StatementKind::StorageDead(local)
|
||||||
kind: StatementKind::StorageDead(index)
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
block.unit()
|
block.unit()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_unwind_to<'tcx>(
|
fn get_unwind_to(
|
||||||
scope: &Scope<'tcx>,
|
scope: &Scope,
|
||||||
is_generator: bool,
|
is_generator: bool,
|
||||||
unwind_from: usize,
|
unwind_from: usize,
|
||||||
generator_drop: bool,
|
generator_drop: bool,
|
||||||
|
@ -1031,7 +1093,7 @@ fn get_unwind_to<'tcx>(
|
||||||
|
|
||||||
fn build_diverge_scope<'tcx>(cfg: &mut CFG<'tcx>,
|
fn build_diverge_scope<'tcx>(cfg: &mut CFG<'tcx>,
|
||||||
span: Span,
|
span: Span,
|
||||||
scope: &mut Scope<'tcx>,
|
scope: &mut Scope,
|
||||||
mut target: BasicBlock,
|
mut target: BasicBlock,
|
||||||
generator_drop: bool,
|
generator_drop: bool,
|
||||||
is_generator: bool)
|
is_generator: bool)
|
||||||
|
@ -1075,26 +1137,20 @@ fn build_diverge_scope<'tcx>(cfg: &mut CFG<'tcx>,
|
||||||
// this is not what clang does.
|
// this is not what clang does.
|
||||||
match drop_data.kind {
|
match drop_data.kind {
|
||||||
DropKind::Storage if is_generator => {
|
DropKind::Storage if is_generator => {
|
||||||
// Only temps and vars need their storage dead.
|
storage_deads.push(Statement {
|
||||||
match drop_data.location {
|
source_info: source_info(drop_data.span),
|
||||||
Place::Base(PlaceBase::Local(index)) => {
|
kind: StatementKind::StorageDead(drop_data.local)
|
||||||
storage_deads.push(Statement {
|
});
|
||||||
source_info: source_info(drop_data.span),
|
if !target_built_by_us {
|
||||||
kind: StatementKind::StorageDead(index)
|
// We cannot add statements to an existing block, so we create a new
|
||||||
});
|
// block for our StorageDead statements.
|
||||||
if !target_built_by_us {
|
let block = cfg.start_new_cleanup_block();
|
||||||
// We cannot add statements to an existing block, so we create a new
|
let source_info = SourceInfo { span: DUMMY_SP, scope: source_scope };
|
||||||
// block for our StorageDead statements.
|
cfg.terminate(block, source_info,
|
||||||
let block = cfg.start_new_cleanup_block();
|
TerminatorKind::Goto { target: target });
|
||||||
let source_info = SourceInfo { span: DUMMY_SP, scope: source_scope };
|
target = block;
|
||||||
cfg.terminate(block, source_info,
|
target_built_by_us = true;
|
||||||
TerminatorKind::Goto { target: target });
|
}
|
||||||
target = block;
|
|
||||||
target_built_by_us = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
*drop_data.cached_block.ref_mut(generator_drop) = Some(target);
|
*drop_data.cached_block.ref_mut(generator_drop) = Some(target);
|
||||||
}
|
}
|
||||||
DropKind::Storage => {}
|
DropKind::Storage => {}
|
||||||
|
@ -1107,12 +1163,15 @@ fn build_diverge_scope<'tcx>(cfg: &mut CFG<'tcx>,
|
||||||
} else {
|
} else {
|
||||||
push_storage_deads(cfg, target, &mut storage_deads);
|
push_storage_deads(cfg, target, &mut storage_deads);
|
||||||
let block = cfg.start_new_cleanup_block();
|
let block = cfg.start_new_cleanup_block();
|
||||||
cfg.terminate(block, source_info(drop_data.span),
|
cfg.terminate(
|
||||||
TerminatorKind::Drop {
|
block,
|
||||||
location: drop_data.location.clone(),
|
source_info(drop_data.span),
|
||||||
target,
|
TerminatorKind::Drop {
|
||||||
unwind: None
|
location: drop_data.local.into(),
|
||||||
});
|
target,
|
||||||
|
unwind: None
|
||||||
|
},
|
||||||
|
);
|
||||||
*cached_block = Some(block);
|
*cached_block = Some(block);
|
||||||
target_built_by_us = true;
|
target_built_by_us = true;
|
||||||
block
|
block
|
||||||
|
|
|
@ -49,7 +49,6 @@ fn mirror_stmts<'a, 'tcx>(
|
||||||
for (index, stmt) in stmts.iter().enumerate() {
|
for (index, stmt) in stmts.iter().enumerate() {
|
||||||
let hir_id = stmt.hir_id;
|
let hir_id = stmt.hir_id;
|
||||||
let opt_dxn_ext = cx.region_scope_tree.opt_destruction_scope(hir_id.local_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 {
|
match stmt.node {
|
||||||
hir::StmtKind::Expr(ref expr) |
|
hir::StmtKind::Expr(ref expr) |
|
||||||
hir::StmtKind::Semi(ref expr) => {
|
hir::StmtKind::Semi(ref expr) => {
|
||||||
|
@ -62,7 +61,6 @@ fn mirror_stmts<'a, 'tcx>(
|
||||||
expr: expr.to_ref(),
|
expr: expr.to_ref(),
|
||||||
},
|
},
|
||||||
opt_destruction_scope: opt_dxn_ext,
|
opt_destruction_scope: opt_dxn_ext,
|
||||||
span: stmt_span,
|
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
hir::StmtKind::Item(..) => {
|
hir::StmtKind::Item(..) => {
|
||||||
|
@ -107,7 +105,6 @@ fn mirror_stmts<'a, 'tcx>(
|
||||||
lint_level: LintLevel::Explicit(local.hir_id),
|
lint_level: LintLevel::Explicit(local.hir_id),
|
||||||
},
|
},
|
||||||
opt_destruction_scope: opt_dxn_ext,
|
opt_destruction_scope: opt_dxn_ext,
|
||||||
span: stmt_span,
|
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,14 +55,10 @@ pub enum StmtRef<'tcx> {
|
||||||
Mirror(Box<Stmt<'tcx>>),
|
Mirror(Box<Stmt<'tcx>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct StatementSpan(pub Span);
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Stmt<'tcx> {
|
pub struct Stmt<'tcx> {
|
||||||
pub kind: StmtKind<'tcx>,
|
pub kind: StmtKind<'tcx>,
|
||||||
pub opt_destruction_scope: Option<region::Scope>,
|
pub opt_destruction_scope: Option<region::Scope>,
|
||||||
pub span: StatementSpan,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
|
@ -24,7 +24,7 @@ impl Drop for S {
|
||||||
// let mut _0: ();
|
// let mut _0: ();
|
||||||
// let _1: std::boxed::Box<S>;
|
// let _1: std::boxed::Box<S>;
|
||||||
// let mut _2: std::boxed::Box<S>;
|
// let mut _2: std::boxed::Box<S>;
|
||||||
// let mut _3: ();
|
// let _3: ();
|
||||||
// let mut _4: std::boxed::Box<S>;
|
// let mut _4: std::boxed::Box<S>;
|
||||||
// scope 1 {
|
// scope 1 {
|
||||||
// }
|
// }
|
||||||
|
@ -50,6 +50,7 @@ impl Drop for S {
|
||||||
//
|
//
|
||||||
// bb4: {
|
// bb4: {
|
||||||
// StorageDead(_2);
|
// StorageDead(_2);
|
||||||
|
// StorageLive(_3);
|
||||||
// StorageLive(_4);
|
// StorageLive(_4);
|
||||||
// _4 = move _1;
|
// _4 = move _1;
|
||||||
// _3 = const std::mem::drop::<std::boxed::Box<S>>(move _4) -> [return: bb5, unwind: bb7];
|
// _3 = const std::mem::drop::<std::boxed::Box<S>>(move _4) -> [return: bb5, unwind: bb7];
|
||||||
|
@ -69,6 +70,7 @@ impl Drop for S {
|
||||||
//
|
//
|
||||||
// bb8: {
|
// bb8: {
|
||||||
// StorageDead(_4);
|
// StorageDead(_4);
|
||||||
|
// StorageDead(_3);
|
||||||
// _0 = ();
|
// _0 = ();
|
||||||
// drop(_1) -> bb9;
|
// drop(_1) -> bb9;
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -61,12 +61,14 @@ fn main() {
|
||||||
// END rustc.foo.CopyPropagation.after.mir
|
// END rustc.foo.CopyPropagation.after.mir
|
||||||
// START rustc.bar.CopyPropagation.before.mir
|
// START rustc.bar.CopyPropagation.before.mir
|
||||||
// bb0: {
|
// bb0: {
|
||||||
|
// StorageLive(_2);
|
||||||
// StorageLive(_3);
|
// StorageLive(_3);
|
||||||
// _3 = _1;
|
// _3 = _1;
|
||||||
// _2 = const dummy(move _3) -> bb1;
|
// _2 = const dummy(move _3) -> bb1;
|
||||||
// }
|
// }
|
||||||
// bb1: {
|
// bb1: {
|
||||||
// StorageDead(_3);
|
// StorageDead(_3);
|
||||||
|
// StorageDead(_2);
|
||||||
// _1 = const 5u8;
|
// _1 = const 5u8;
|
||||||
// ...
|
// ...
|
||||||
// return;
|
// return;
|
||||||
|
|
|
@ -18,6 +18,7 @@ fn main() {
|
||||||
// }
|
// }
|
||||||
// bb1: {
|
// bb1: {
|
||||||
// StorageDead(_3);
|
// StorageDead(_3);
|
||||||
|
// StorageDead(_2);
|
||||||
// goto -> bb5;
|
// goto -> bb5;
|
||||||
// }
|
// }
|
||||||
// bb2: {
|
// bb2: {
|
||||||
|
@ -36,6 +37,7 @@ fn main() {
|
||||||
// goto -> bb3;
|
// goto -> bb3;
|
||||||
// }
|
// }
|
||||||
// bb7: {
|
// bb7: {
|
||||||
|
// StorageLive(_2);
|
||||||
// StorageLive(_3);
|
// StorageLive(_3);
|
||||||
// goto -> bb1;
|
// goto -> bb1;
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -54,6 +54,7 @@ fn main() {
|
||||||
// }
|
// }
|
||||||
// bb2: {
|
// bb2: {
|
||||||
// ...
|
// ...
|
||||||
|
// StorageLive(_6);
|
||||||
// StorageLive(_7);
|
// StorageLive(_7);
|
||||||
// _7 = move _2;
|
// _7 = move _2;
|
||||||
// _6 = const take::<Foo>(move _7) -> [return: bb9, unwind: bb8];
|
// _6 = const take::<Foo>(move _7) -> [return: bb9, unwind: bb8];
|
||||||
|
@ -81,16 +82,20 @@ fn main() {
|
||||||
// }
|
// }
|
||||||
// bb8 (cleanup): {
|
// bb8 (cleanup): {
|
||||||
// StorageDead(_7);
|
// StorageDead(_7);
|
||||||
|
// StorageDead(_6);
|
||||||
// goto -> bb7;
|
// goto -> bb7;
|
||||||
// }
|
// }
|
||||||
// bb9: {
|
// bb9: {
|
||||||
// StorageDead(_7);
|
// StorageDead(_7);
|
||||||
|
// StorageDead(_6);
|
||||||
|
// StorageLive(_8);
|
||||||
// StorageLive(_9);
|
// StorageLive(_9);
|
||||||
// _9 = move _3;
|
// _9 = move _3;
|
||||||
// _8 = const take::<Bar>(move _9) -> [return: bb10, unwind: bb11];
|
// _8 = const take::<Bar>(move _9) -> [return: bb10, unwind: bb11];
|
||||||
// }
|
// }
|
||||||
// bb10: {
|
// bb10: {
|
||||||
// StorageDead(_9);
|
// StorageDead(_9);
|
||||||
|
// StorageDead(_8);
|
||||||
// ...
|
// ...
|
||||||
// StorageDead(_3);
|
// StorageDead(_3);
|
||||||
// StorageDead(_2);
|
// StorageDead(_2);
|
||||||
|
@ -98,6 +103,7 @@ fn main() {
|
||||||
// }
|
// }
|
||||||
// bb11 (cleanup): {
|
// bb11 (cleanup): {
|
||||||
// StorageDead(_9);
|
// StorageDead(_9);
|
||||||
|
// StorageDead(_8);
|
||||||
// goto -> bb7;
|
// goto -> bb7;
|
||||||
// }
|
// }
|
||||||
// bb12: {
|
// bb12: {
|
||||||
|
|
|
@ -25,6 +25,7 @@ fn main() {
|
||||||
// falseUnwind -> [real: bb3, cleanup: bb1];
|
// falseUnwind -> [real: bb3, cleanup: bb1];
|
||||||
// }
|
// }
|
||||||
// bb3: {
|
// bb3: {
|
||||||
|
// StorageLive(_3);
|
||||||
// StorageLive(_4);
|
// StorageLive(_4);
|
||||||
// _4 = _1;
|
// _4 = _1;
|
||||||
// FakeRead(ForMatchedPlace, _4);
|
// FakeRead(ForMatchedPlace, _4);
|
||||||
|
@ -34,6 +35,7 @@ fn main() {
|
||||||
// bb5: {
|
// bb5: {
|
||||||
// _3 = ();
|
// _3 = ();
|
||||||
// StorageDead(_4);
|
// StorageDead(_4);
|
||||||
|
// StorageDead(_3);
|
||||||
// _1 = const true;
|
// _1 = const true;
|
||||||
// _2 = ();
|
// _2 = ();
|
||||||
// goto -> bb2;
|
// goto -> bb2;
|
||||||
|
@ -41,6 +43,7 @@ fn main() {
|
||||||
// bb6: {
|
// bb6: {
|
||||||
// _0 = ();
|
// _0 = ();
|
||||||
// StorageDead(_4);
|
// StorageDead(_4);
|
||||||
|
// StorageDead(_3);
|
||||||
// StorageDead(_1);
|
// StorageDead(_1);
|
||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -42,7 +42,7 @@ impl S {
|
||||||
// START rustc.test.ElaborateDrops.after.mir
|
// START rustc.test.ElaborateDrops.after.mir
|
||||||
// let mut _0: ();
|
// let mut _0: ();
|
||||||
// let _1: S;
|
// let _1: S;
|
||||||
// let mut _3: ();
|
// let _3: ();
|
||||||
// let mut _4: S;
|
// let mut _4: S;
|
||||||
// let mut _5: S;
|
// let mut _5: S;
|
||||||
// let mut _6: bool;
|
// let mut _6: bool;
|
||||||
|
|
|
@ -21,7 +21,7 @@ fn main() {
|
||||||
// let _2: i32;
|
// let _2: i32;
|
||||||
// let mut _3: bool;
|
// let mut _3: bool;
|
||||||
// let mut _4: !;
|
// let mut _4: !;
|
||||||
// let mut _5: ();
|
// let _5: ();
|
||||||
// let mut _6: &i32;
|
// let mut _6: &i32;
|
||||||
// scope 1 {
|
// scope 1 {
|
||||||
// }
|
// }
|
||||||
|
@ -73,12 +73,14 @@ fn main() {
|
||||||
// bb12: {
|
// bb12: {
|
||||||
// FakeRead(ForLet, _2);
|
// FakeRead(ForLet, _2);
|
||||||
// StorageDead(_3);
|
// StorageDead(_3);
|
||||||
|
// StorageLive(_5);
|
||||||
// StorageLive(_6);
|
// StorageLive(_6);
|
||||||
// _6 = &_2;
|
// _6 = &_2;
|
||||||
// _5 = const std::mem::drop::<&i32>(move _6) -> [return: bb13, unwind: bb4];
|
// _5 = const std::mem::drop::<&i32>(move _6) -> [return: bb13, unwind: bb4];
|
||||||
// }
|
// }
|
||||||
// bb13: {
|
// bb13: {
|
||||||
// StorageDead(_6);
|
// StorageDead(_6);
|
||||||
|
// StorageDead(_5);
|
||||||
// _1 = ();
|
// _1 = ();
|
||||||
// StorageDead(_2);
|
// StorageDead(_2);
|
||||||
// goto -> bb1;
|
// goto -> bb1;
|
||||||
|
|
|
@ -25,6 +25,7 @@ fn main() {
|
||||||
// bb3: { // Entry into the loop
|
// bb3: { // Entry into the loop
|
||||||
// _1 = ();
|
// _1 = ();
|
||||||
// StorageDead(_2);
|
// StorageDead(_2);
|
||||||
|
// StorageDead(_1);
|
||||||
// goto -> bb5;
|
// goto -> bb5;
|
||||||
// }
|
// }
|
||||||
// ...
|
// ...
|
||||||
|
|
|
@ -103,10 +103,6 @@ fn main() {
|
||||||
// bb10: { // `else` block - first time
|
// bb10: { // `else` block - first time
|
||||||
// _9 = (*_6);
|
// _9 = (*_6);
|
||||||
// StorageDead(_10);
|
// StorageDead(_10);
|
||||||
// FakeRead(ForMatchGuard, _3);
|
|
||||||
// FakeRead(ForMatchGuard, _4);
|
|
||||||
// FakeRead(ForGuardBinding, _6);
|
|
||||||
// FakeRead(ForGuardBinding, _8);
|
|
||||||
// switchInt(move _9) -> [false: bb16, otherwise: bb15];
|
// switchInt(move _9) -> [false: bb16, otherwise: bb15];
|
||||||
// }
|
// }
|
||||||
// bb11: { // `return 3` - first time
|
// bb11: { // `return 3` - first time
|
||||||
|
@ -128,6 +124,10 @@ fn main() {
|
||||||
// }
|
// }
|
||||||
// bb15: {
|
// bb15: {
|
||||||
// StorageDead(_9);
|
// StorageDead(_9);
|
||||||
|
// FakeRead(ForMatchGuard, _3);
|
||||||
|
// FakeRead(ForMatchGuard, _4);
|
||||||
|
// FakeRead(ForGuardBinding, _6);
|
||||||
|
// FakeRead(ForGuardBinding, _8);
|
||||||
// StorageLive(_5);
|
// StorageLive(_5);
|
||||||
// _5 = (_2.1: bool);
|
// _5 = (_2.1: bool);
|
||||||
// StorageLive(_7);
|
// StorageLive(_7);
|
||||||
|
@ -159,10 +159,6 @@ fn main() {
|
||||||
// bb19: { // `else` block - second time
|
// bb19: { // `else` block - second time
|
||||||
// _12 = (*_6);
|
// _12 = (*_6);
|
||||||
// StorageDead(_13);
|
// StorageDead(_13);
|
||||||
// FakeRead(ForMatchGuard, _3);
|
|
||||||
// FakeRead(ForMatchGuard, _4);
|
|
||||||
// FakeRead(ForGuardBinding, _6);
|
|
||||||
// FakeRead(ForGuardBinding, _8);
|
|
||||||
// switchInt(move _12) -> [false: bb22, otherwise: bb21];
|
// switchInt(move _12) -> [false: bb22, otherwise: bb21];
|
||||||
// }
|
// }
|
||||||
// bb20: {
|
// bb20: {
|
||||||
|
@ -175,6 +171,10 @@ fn main() {
|
||||||
// }
|
// }
|
||||||
// bb21: { // bindings for arm 1
|
// bb21: { // bindings for arm 1
|
||||||
// StorageDead(_12);
|
// StorageDead(_12);
|
||||||
|
// FakeRead(ForMatchGuard, _3);
|
||||||
|
// FakeRead(ForMatchGuard, _4);
|
||||||
|
// FakeRead(ForGuardBinding, _6);
|
||||||
|
// FakeRead(ForGuardBinding, _8);
|
||||||
// StorageLive(_5);
|
// StorageLive(_5);
|
||||||
// _5 = (_2.0: bool);
|
// _5 = (_2.0: bool);
|
||||||
// StorageLive(_7);
|
// StorageLive(_7);
|
||||||
|
|
|
@ -71,12 +71,12 @@ fn main() {
|
||||||
// _7 = const guard() -> [return: bb7, unwind: bb1];
|
// _7 = const guard() -> [return: bb7, unwind: bb1];
|
||||||
// }
|
// }
|
||||||
// bb7: { // end of guard
|
// bb7: { // end of guard
|
||||||
// FakeRead(ForMatchGuard, _4);
|
|
||||||
// FakeRead(ForGuardBinding, _6);
|
|
||||||
// switchInt(move _7) -> [false: bb9, otherwise: bb8];
|
// switchInt(move _7) -> [false: bb9, otherwise: bb8];
|
||||||
// }
|
// }
|
||||||
// bb8: { // arm1
|
// bb8: { // arm1
|
||||||
// StorageDead(_7);
|
// StorageDead(_7);
|
||||||
|
// FakeRead(ForMatchGuard, _4);
|
||||||
|
// FakeRead(ForGuardBinding, _6);
|
||||||
// StorageLive(_5);
|
// StorageLive(_5);
|
||||||
// _5 = ((_2 as Some).0: i32);
|
// _5 = ((_2 as Some).0: i32);
|
||||||
// StorageLive(_8);
|
// StorageLive(_8);
|
||||||
|
@ -138,12 +138,12 @@ fn main() {
|
||||||
// _7 = const guard() -> [return: bb6, unwind: bb1];
|
// _7 = const guard() -> [return: bb6, unwind: bb1];
|
||||||
// }
|
// }
|
||||||
// bb6: { // end of guard
|
// bb6: { // end of guard
|
||||||
// FakeRead(ForMatchGuard, _4);
|
|
||||||
// FakeRead(ForGuardBinding, _6);
|
|
||||||
// switchInt(move _7) -> [false: bb8, otherwise: bb7];
|
// switchInt(move _7) -> [false: bb8, otherwise: bb7];
|
||||||
// }
|
// }
|
||||||
// bb7: {
|
// bb7: {
|
||||||
// StorageDead(_7);
|
// StorageDead(_7);
|
||||||
|
// FakeRead(ForMatchGuard, _4);
|
||||||
|
// FakeRead(ForGuardBinding, _6);
|
||||||
// StorageLive(_5);
|
// StorageLive(_5);
|
||||||
// _5 = ((_2 as Some).0: i32);
|
// _5 = ((_2 as Some).0: i32);
|
||||||
// StorageLive(_8);
|
// StorageLive(_8);
|
||||||
|
@ -209,12 +209,12 @@ fn main() {
|
||||||
// _8 = const guard() -> [return: bb6, unwind: bb1];
|
// _8 = const guard() -> [return: bb6, unwind: bb1];
|
||||||
// }
|
// }
|
||||||
// bb6: { //end of guard1
|
// bb6: { //end of guard1
|
||||||
// FakeRead(ForMatchGuard, _5);
|
|
||||||
// FakeRead(ForGuardBinding, _7);
|
|
||||||
// switchInt(move _8) -> [false: bb8, otherwise: bb7];
|
// switchInt(move _8) -> [false: bb8, otherwise: bb7];
|
||||||
// }
|
// }
|
||||||
// bb7: {
|
// bb7: {
|
||||||
// StorageDead(_8);
|
// StorageDead(_8);
|
||||||
|
// FakeRead(ForMatchGuard, _5);
|
||||||
|
// FakeRead(ForGuardBinding, _7);
|
||||||
// StorageLive(_6);
|
// StorageLive(_6);
|
||||||
// _6 = ((_2 as Some).0: i32);
|
// _6 = ((_2 as Some).0: i32);
|
||||||
// _1 = const 1i32;
|
// _1 = const 1i32;
|
||||||
|
@ -245,12 +245,12 @@ fn main() {
|
||||||
// }
|
// }
|
||||||
// bb11: { // end of guard2
|
// bb11: { // end of guard2
|
||||||
// StorageDead(_13);
|
// StorageDead(_13);
|
||||||
// FakeRead(ForMatchGuard, _5);
|
|
||||||
// FakeRead(ForGuardBinding, _11);
|
|
||||||
// switchInt(move _12) -> [false: bb13, otherwise: bb12];
|
// switchInt(move _12) -> [false: bb13, otherwise: bb12];
|
||||||
// }
|
// }
|
||||||
// bb12: { // binding4 & arm4
|
// bb12: { // binding4 & arm4
|
||||||
// StorageDead(_12);
|
// StorageDead(_12);
|
||||||
|
// FakeRead(ForMatchGuard, _5);
|
||||||
|
// FakeRead(ForGuardBinding, _11);
|
||||||
// StorageLive(_10);
|
// StorageLive(_10);
|
||||||
// _10 = ((_2 as Some).0: i32);
|
// _10 = ((_2 as Some).0: i32);
|
||||||
// _1 = const 3i32;
|
// _1 = const 3i32;
|
||||||
|
|
|
@ -54,11 +54,11 @@ fn main() {
|
||||||
// _8 = &shallow _1;
|
// _8 = &shallow _1;
|
||||||
// StorageLive(_9);
|
// StorageLive(_9);
|
||||||
// _9 = _2;
|
// _9 = _2;
|
||||||
// FakeRead(ForMatchGuard, _8);
|
|
||||||
// switchInt(move _9) -> [false: bb11, otherwise: bb10];
|
// switchInt(move _9) -> [false: bb11, otherwise: bb10];
|
||||||
// }
|
// }
|
||||||
// bb10: {
|
// bb10: {
|
||||||
// StorageDead(_9);
|
// StorageDead(_9);
|
||||||
|
// FakeRead(ForMatchGuard, _8);
|
||||||
// _3 = const 0i32;
|
// _3 = const 0i32;
|
||||||
// goto -> bb14;
|
// goto -> bb14;
|
||||||
// }
|
// }
|
||||||
|
@ -75,6 +75,7 @@ fn main() {
|
||||||
// goto -> bb14;
|
// goto -> bb14;
|
||||||
// }
|
// }
|
||||||
// bb14: {
|
// bb14: {
|
||||||
|
// StorageDead(_3);
|
||||||
// _0 = ();
|
// _0 = ();
|
||||||
// StorageDead(_2);
|
// StorageDead(_2);
|
||||||
// StorageDead(_1);
|
// StorageDead(_1);
|
||||||
|
|
|
@ -22,9 +22,9 @@ fn main() {
|
||||||
|
|
||||||
// END RUST SOURCE
|
// END RUST SOURCE
|
||||||
// START rustc.main.nll.0.mir
|
// START rustc.main.nll.0.mir
|
||||||
// | '_#2r | U0 | {bb2[0..=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..=1]}
|
// | '_#3r | U0 | {bb2[1..=8], bb3[0], bb5[0..=2]}
|
||||||
// | '_#4r | U0 | {bb2[4..=8], bb3[0], bb5[0..=1]}
|
// | '_#4r | U0 | {bb2[4..=8], bb3[0], bb5[0..=2]}
|
||||||
// END rustc.main.nll.0.mir
|
// END rustc.main.nll.0.mir
|
||||||
// START rustc.main.nll.0.mir
|
// START rustc.main.nll.0.mir
|
||||||
// let _2: &'_#3r usize;
|
// let _2: &'_#3r usize;
|
||||||
|
|
|
@ -38,14 +38,14 @@ fn main() {
|
||||||
// _7 = &shallow (*(*((_1 as Some).0: &'<empty> &'<empty> i32)));
|
// _7 = &shallow (*(*((_1 as Some).0: &'<empty> &'<empty> i32)));
|
||||||
// StorageLive(_8);
|
// StorageLive(_8);
|
||||||
// _8 = _2;
|
// _8 = _2;
|
||||||
// FakeRead(ForMatchGuard, _4);
|
|
||||||
// FakeRead(ForMatchGuard, _5);
|
|
||||||
// FakeRead(ForMatchGuard, _6);
|
|
||||||
// FakeRead(ForMatchGuard, _7);
|
|
||||||
// switchInt(move _8) -> [false: bb6, otherwise: bb5];
|
// switchInt(move _8) -> [false: bb6, otherwise: bb5];
|
||||||
// }
|
// }
|
||||||
// bb5: {
|
// bb5: {
|
||||||
// StorageDead(_8);
|
// StorageDead(_8);
|
||||||
|
// FakeRead(ForMatchGuard, _4);
|
||||||
|
// FakeRead(ForMatchGuard, _5);
|
||||||
|
// FakeRead(ForMatchGuard, _6);
|
||||||
|
// FakeRead(ForMatchGuard, _7);
|
||||||
// _0 = const 0i32;
|
// _0 = const 0i32;
|
||||||
// goto -> bb7;
|
// goto -> bb7;
|
||||||
// }
|
// }
|
||||||
|
@ -84,14 +84,14 @@ fn main() {
|
||||||
// nop;
|
// nop;
|
||||||
// StorageLive(_8);
|
// StorageLive(_8);
|
||||||
// _8 = _2;
|
// _8 = _2;
|
||||||
// nop;
|
|
||||||
// nop;
|
|
||||||
// nop;
|
|
||||||
// nop;
|
|
||||||
// switchInt(move _8) -> [false: bb6, otherwise: bb5];
|
// switchInt(move _8) -> [false: bb6, otherwise: bb5];
|
||||||
// }
|
// }
|
||||||
// bb5: {
|
// bb5: {
|
||||||
// StorageDead(_8);
|
// StorageDead(_8);
|
||||||
|
// nop;
|
||||||
|
// nop;
|
||||||
|
// nop;
|
||||||
|
// nop;
|
||||||
// _0 = const 0i32;
|
// _0 = const 0i32;
|
||||||
// goto -> bb7;
|
// goto -> bb7;
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -12,6 +12,7 @@ fn main() {
|
||||||
// StorageLive(_1);
|
// StorageLive(_1);
|
||||||
// _1 = const 0i32;
|
// _1 = const 0i32;
|
||||||
// FakeRead(ForLet, _1);
|
// FakeRead(ForLet, _1);
|
||||||
|
// StorageLive(_2);
|
||||||
// StorageLive(_3);
|
// StorageLive(_3);
|
||||||
// StorageLive(_4);
|
// StorageLive(_4);
|
||||||
// StorageLive(_5);
|
// StorageLive(_5);
|
||||||
|
@ -23,6 +24,7 @@ fn main() {
|
||||||
// _2 = ();
|
// _2 = ();
|
||||||
// StorageDead(_4);
|
// StorageDead(_4);
|
||||||
// StorageDead(_3);
|
// StorageDead(_3);
|
||||||
|
// StorageDead(_2);
|
||||||
// StorageLive(_6);
|
// StorageLive(_6);
|
||||||
// _6 = const 1i32;
|
// _6 = const 1i32;
|
||||||
// FakeRead(ForLet, _6);
|
// FakeRead(ForLet, _6);
|
||||||
|
|
|
@ -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
|
|
@ -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();
|
||||||
|
}
|
|
@ -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 {};
|
||||||
|
}
|
Loading…
Reference in New Issue