Auto merge of #39409 - pnkfelix:mir-borrowck2, r=nikomatsakis

MIR EndRegion Statements (was MIR dataflow for Borrows)

This PR adds an `EndRegion` statement to MIR (where the `EndRegion` statement is what terminates a borrow).

An earlier version of the PR implemented a dataflow analysis on borrow expressions, but I am now factoring that into a follow-up PR so that reviewing this one is easier. (And also because there are some revisions I want to make to that dataflow code, but I want this PR to get out of WIP status...)

This is a baby step towards MIR borrowck. I just want to get the review process going while I independently work on the remaining steps.
This commit is contained in:
bors 2017-06-19 13:01:27 +00:00
commit 04145943a2
45 changed files with 1048 additions and 74 deletions

View File

@ -226,6 +226,9 @@ for mir::StatementKind<'tcx> {
mir::StatementKind::StorageDead(ref lvalue) => {
lvalue.hash_stable(hcx, hasher);
}
mir::StatementKind::EndRegion(ref extents) => {
extents.hash_stable(hcx, hasher);
}
mir::StatementKind::Nop => {}
mir::StatementKind::InlineAsm { ref asm, ref outputs, ref inputs } => {
asm.hash_stable(hcx, hasher);

View File

@ -12,6 +12,7 @@
use graphviz::IntoCow;
use middle::const_val::ConstVal;
use middle::region::CodeExtent;
use rustc_const_math::{ConstUsize, ConstInt, ConstMathErr};
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
use rustc_data_structures::control_flow_graph::dominators::{Dominators, dominators};
@ -804,6 +805,10 @@ pub enum StatementKind<'tcx> {
inputs: Vec<Operand<'tcx>>
},
/// Mark one terminating point of an extent (i.e. static region).
/// (The starting point(s) arise implicitly from borrows.)
EndRegion(CodeExtent),
/// No-op. Useful for deleting instructions without affecting statement indices.
Nop,
}
@ -813,6 +818,8 @@ impl<'tcx> Debug for Statement<'tcx> {
use self::StatementKind::*;
match self.kind {
Assign(ref lv, ref rv) => write!(fmt, "{:?} = {:?}", lv, rv),
// (reuse lifetime rendering policy from ppaux.)
EndRegion(ref ce) => write!(fmt, "EndRegion({})", ty::ReScope(*ce)),
StorageLive(ref lv) => write!(fmt, "StorageLive({:?})", lv),
StorageDead(ref lv) => write!(fmt, "StorageDead({:?})", lv),
SetDiscriminant{lvalue: ref lv, variant_index: index} => {
@ -1176,12 +1183,22 @@ impl<'tcx> Debug for Rvalue<'tcx> {
UnaryOp(ref op, ref a) => write!(fmt, "{:?}({:?})", op, a),
Discriminant(ref lval) => write!(fmt, "discriminant({:?})", lval),
NullaryOp(ref op, ref t) => write!(fmt, "{:?}({:?})", op, t),
Ref(_, borrow_kind, ref lv) => {
Ref(region, borrow_kind, ref lv) => {
let kind_str = match borrow_kind {
BorrowKind::Shared => "",
BorrowKind::Mut | BorrowKind::Unique => "mut ",
};
write!(fmt, "&{}{:?}", kind_str, lv)
// When identifying regions, add trailing space if
// necessary.
let region = if ppaux::identify_regions() {
let mut region = format!("{}", region);
if region.len() > 0 { region.push(' '); }
region
} else {
"".to_owned()
};
write!(fmt, "&{}{}{:?}", region, kind_str, lv)
}
Aggregate(ref kind, ref lvs) => {
@ -1224,7 +1241,11 @@ impl<'tcx> Debug for Rvalue<'tcx> {
AggregateKind::Closure(def_id, _) => ty::tls::with(|tcx| {
if let Some(node_id) = tcx.hir.as_local_node_id(def_id) {
let name = format!("[closure@{:?}]", tcx.hir.span(node_id));
let name = if tcx.sess.opts.debugging_opts.span_free_formats {
format!("[closure@{:?}]", node_id)
} else {
format!("[closure@{:?}]", tcx.hir.span(node_id))
};
let mut struct_fmt = fmt.debug_struct(&name);
tcx.with_freevars(node_id, |freevars| {
@ -1458,6 +1479,13 @@ impl<'tcx> TypeFoldable<'tcx> for Statement<'tcx> {
outputs: outputs.fold_with(folder),
inputs: inputs.fold_with(folder)
},
// Note for future: If we want to expose the extents
// during the fold, we need to either generalize EndRegion
// to carry `[ty::Region]`, or extend the `TypeFolder`
// trait with a `fn fold_extent`.
EndRegion(ref extent) => EndRegion(extent.clone()),
Nop => Nop,
};
Statement {
@ -1476,6 +1504,13 @@ impl<'tcx> TypeFoldable<'tcx> for Statement<'tcx> {
StorageDead(ref lvalue) => lvalue.visit_with(visitor),
InlineAsm { ref outputs, ref inputs, .. } =>
outputs.visit_with(visitor) || inputs.visit_with(visitor),
// Note for future: If we want to expose the extents
// during the visit, we need to either generalize EndRegion
// to carry `[ty::Region]`, or extend the `TypeVisitor`
// trait with a `fn visit_extent`.
EndRegion(ref _extent) => false,
Nop => false,
}
}

View File

@ -325,6 +325,7 @@ macro_rules! make_mir_visitor {
ref $($mutability)* rvalue) => {
self.visit_assign(block, lvalue, rvalue, location);
}
StatementKind::EndRegion(_) => {}
StatementKind::SetDiscriminant{ ref $($mutability)* lvalue, .. } => {
self.visit_lvalue(lvalue, LvalueContext::Store, location);
}

View File

@ -893,6 +893,10 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
DB_OPTIONS, db_type_desc, dbsetters,
verbose: bool = (false, parse_bool, [UNTRACKED],
"in general, enable more debug printouts"),
span_free_formats: bool = (false, parse_bool, [UNTRACKED],
"when debug-printing compiler state, do not include spans"), // o/w tests have closure@path
identify_regions: bool = (false, parse_bool, [UNTRACKED],
"make unnamed regions display as '# (where # is some non-ident unique id)"),
time_passes: bool = (false, parse_bool, [UNTRACKED],
"measure time of each rustc pass"),
count_llvm_insns: bool = (false, parse_bool,

View File

@ -8,8 +8,10 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use hir::BodyId;
use hir::def_id::DefId;
use hir::map::definitions::DefPathData;
use middle::region::{CodeExtent, BlockRemainder};
use ty::subst::{self, Subst};
use ty::{BrAnon, BrEnv, BrFresh, BrNamed};
use ty::{TyBool, TyChar, TyAdt};
@ -32,6 +34,10 @@ pub fn verbose() -> bool {
ty::tls::with(|tcx| tcx.sess.verbose())
}
pub fn identify_regions() -> bool {
ty::tls::with(|tcx| tcx.sess.opts.debugging_opts.identify_regions)
}
fn fn_sig(f: &mut fmt::Formatter,
inputs: &[Ty],
variadic: bool,
@ -519,6 +525,23 @@ impl fmt::Display for ty::RegionKind {
ty::ReSkolemized(_, br) => {
write!(f, "{}", br)
}
ty::ReScope(code_extent) if identify_regions() => {
match code_extent {
CodeExtent::Misc(node_id) =>
write!(f, "'{}mce", node_id.as_u32()),
CodeExtent::CallSiteScope(BodyId { node_id }) =>
write!(f, "'{}cce", node_id.as_u32()),
CodeExtent::ParameterScope(BodyId { node_id }) =>
write!(f, "'{}pce", node_id.as_u32()),
CodeExtent::DestructionScope(node_id) =>
write!(f, "'{}dce", node_id.as_u32()),
CodeExtent::Remainder(BlockRemainder { block, first_statement_index }) =>
write!(f, "'{}_{}rce", block, first_statement_index),
}
}
ty::ReVar(region_vid) if identify_regions() => {
write!(f, "'{}rv", region_vid.index)
}
ty::ReScope(_) |
ty::ReVar(_) |
ty::ReErased => Ok(()),
@ -789,7 +812,11 @@ impl<'tcx> fmt::Display for ty::TypeVariants<'tcx> {
write!(f, "[closure")?;
if let Some(node_id) = tcx.hir.as_local_node_id(did) {
write!(f, "@{:?}", tcx.hir.span(node_id))?;
if tcx.sess.opts.debugging_opts.span_free_formats {
write!(f, "@{:?}", node_id)?;
} else {
write!(f, "@{:?}", tcx.hir.span(node_id))?;
}
let mut sep = " ";
tcx.with_freevars(node_id, |freevars| {
for (freevar, upvar_ty) in freevars.iter().zip(upvar_tys) {

View File

@ -474,6 +474,7 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
mir::StatementKind::StorageLive(_) |
mir::StatementKind::StorageDead(_) |
mir::StatementKind::InlineAsm { .. } |
mir::StatementKind::EndRegion(_) |
mir::StatementKind::Nop => {}
}
}

View File

@ -105,6 +105,7 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir::StatementKind::StorageLive(_) |
mir::StatementKind::StorageDead(_) |
mir::StatementKind::InlineAsm { .. } |
mir::StatementKind::EndRegion(_) |
mir::StatementKind::Nop => continue,
mir::StatementKind::SetDiscriminant{ .. } =>
span_bug!(stmt.source_info.span,

View File

@ -594,6 +594,11 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
assert!(self.patch.is_patched(bb));
allow_initializations = false;
}
TerminatorKind::Resume => {
// It is possible for `Resume` to be patched
// (in particular it can be patched to be replaced with
// a Goto; see `MirPatch::new`).
}
_ => {
assert!(!self.patch.is_patched(bb));
}

View File

@ -413,6 +413,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
"SetDiscriminant should not exist during borrowck");
}
StatementKind::InlineAsm { .. } |
StatementKind::EndRegion(_) |
StatementKind::Nop => {}
}
}

View File

@ -394,6 +394,7 @@ fn drop_flag_effects_for_location<'a, 'tcx, F>(
mir::StatementKind::StorageLive(_) |
mir::StatementKind::StorageDead(_) |
mir::StatementKind::InlineAsm { .. } |
mir::StatementKind::EndRegion(_) |
mir::StatementKind::Nop => {}
},
None => {

View File

@ -914,6 +914,9 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
let mut passes = Passes::new();
passes.push_hook(mir::transform::dump_mir::DumpMir);
// Remove all `EndRegion` statements that are not involved in borrows.
passes.push_pass(MIR_CONST, mir::transform::clean_end_regions::CleanEndRegions);
// What we need to do constant evaluation.
passes.push_pass(MIR_CONST, mir::transform::simplify::SimplifyCfg::new("initial"));
passes.push_pass(MIR_CONST, mir::transform::type_check::TypeckMir);

View File

@ -21,21 +21,24 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
ast_block: &'tcx hir::Block,
source_info: SourceInfo)
-> BlockAnd<()> {
let Block { extent, span, stmts, expr, targeted_by_break } = self.hir.mirror(ast_block);
self.in_scope(extent, block, move |this| {
if targeted_by_break {
// This is a `break`-able block (currently only `catch { ... }`)
let exit_block = this.cfg.start_new_block();
let block_exit = this.in_breakable_scope(None, exit_block,
destination.clone(), |this| {
let Block { extent, opt_destruction_extent, span, stmts, expr, targeted_by_break } =
self.hir.mirror(ast_block);
self.in_opt_scope(opt_destruction_extent.map(|de|(de, source_info)), block, move |this| {
this.in_scope((extent, source_info), block, move |this| {
if targeted_by_break {
// This is a `break`-able block (currently only `catch { ... }`)
let exit_block = this.cfg.start_new_block();
let block_exit = this.in_breakable_scope(
None, exit_block, destination.clone(), |this| {
this.ast_block_stmts(destination, block, span, stmts, expr)
});
this.cfg.terminate(unpack!(block_exit), source_info,
TerminatorKind::Goto { target: exit_block });
exit_block.unit()
} else {
this.ast_block_stmts(destination, block, span, stmts, expr)
});
this.cfg.terminate(unpack!(block_exit), source_info,
TerminatorKind::Goto { target: exit_block });
exit_block.unit()
} else {
this.ast_block_stmts(destination, block, span, stmts, expr)
}
}
})
})
}
@ -66,14 +69,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// First we build all the statements in the block.
let mut let_extent_stack = Vec::with_capacity(8);
let outer_visibility_scope = this.visibility_scope;
let source_info = this.source_info(span);
for stmt in stmts {
let Stmt { span: _, kind } = this.hir.mirror(stmt);
let Stmt { span, kind, opt_destruction_extent } = this.hir.mirror(stmt);
match kind {
StmtKind::Expr { scope, expr } => {
unpack!(block = this.in_scope(scope, block, |this| {
let expr = this.hir.mirror(expr);
this.stmt_expr(block, expr)
}));
unpack!(block = this.in_opt_scope(
opt_destruction_extent.map(|de|(de, source_info)), block, |this| {
this.in_scope((scope, source_info), block, |this| {
let expr = this.hir.mirror(expr);
this.stmt_expr(block, expr)
})
}));
}
StmtKind::Let { remainder_scope, init_scope, pattern, initializer } => {
let tcx = this.hir.tcx();
@ -89,10 +96,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// Evaluate the initializer, if present.
if let Some(init) = initializer {
unpack!(block = this.in_scope(init_scope, block, move |this| {
// FIXME #30046 ^~~~
this.expr_into_pattern(block, pattern, init)
}));
unpack!(block = this.in_opt_scope(
opt_destruction_extent.map(|de|(de, source_info)), block, move |this| {
this.in_scope((init_scope, source_info), block, move |this| {
// FIXME #30046 ^~~~
this.expr_into_pattern(block, pattern, init)
})
}));
} else {
this.visit_bindings(&pattern, &mut |this, _, _, node, span, _| {
this.storage_live_binding(block, node, span);
@ -112,13 +122,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
if let Some(expr) = expr {
unpack!(block = this.into(destination, block, expr));
} else {
let source_info = this.source_info(span);
this.cfg.push_assign_unit(block, source_info, destination);
}
// Finally, we pop all the let scopes before exiting out from the scope of block
// itself.
for extent in let_extent_stack.into_iter().rev() {
unpack!(block = this.pop_scope(extent, block));
unpack!(block = this.pop_scope((extent, source_info), block));
}
// Restore the original visibility scope.
this.visibility_scope = outer_visibility_scope;

View File

@ -14,6 +14,7 @@
//! Routines for manipulating the control-flow graph.
use build::CFG;
use rustc::middle::region::CodeExtent;
use rustc::mir::*;
impl<'tcx> CFG<'tcx> {
@ -43,6 +44,16 @@ impl<'tcx> CFG<'tcx> {
self.block_data_mut(block).statements.push(statement);
}
pub fn push_end_region(&mut self,
block: BasicBlock,
source_info: SourceInfo,
extent: CodeExtent) {
self.push(block, Statement {
source_info: source_info,
kind: StatementKind::EndRegion(extent),
});
}
pub fn push_assign(&mut self,
block: BasicBlock,
source_info: SourceInfo,

View File

@ -40,7 +40,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let source_info = this.source_info(expr_span);
match expr.kind {
ExprKind::Scope { extent, value } => {
this.in_scope(extent, block, |this| this.as_lvalue(block, value))
this.in_scope((extent, source_info), block, |this| this.as_lvalue(block, value))
}
ExprKind::Field { lhs, name } => {
let lvalue = unpack!(block = this.as_lvalue(block, lhs));

View File

@ -56,6 +56,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let this = self;
if let ExprKind::Scope { extent, value } = expr.kind {
let source_info = this.source_info(expr.span);
let extent = (extent, source_info);
return this.in_scope(extent, block, |this| {
this.as_operand(block, scope, value)
});

View File

@ -59,6 +59,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
match expr.kind {
ExprKind::Scope { extent, value } => {
let extent = (extent, source_info);
this.in_scope(extent, block, |this| this.as_rvalue(block, scope, value))
}
ExprKind::Repeat { value, count } => {
@ -99,7 +100,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// to start, malloc some memory of suitable type (thus far, uninitialized):
let box_ = Rvalue::NullaryOp(NullOp::Box, value.ty);
this.cfg.push_assign(block, source_info, &result, box_);
this.in_scope(value_extents, block, |this| {
this.in_scope((value_extents, source_info), block, |this| {
// schedule a shallow free of that memory, lest we unwind:
this.schedule_box_free(expr_span, value_extents, &result, value.ty);
// initialize the box contents:

View File

@ -39,16 +39,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
block, temp_lifetime, expr);
let this = self;
let expr_span = expr.span;
let source_info = this.source_info(expr_span);
if let ExprKind::Scope { extent, value } = expr.kind {
return this.in_scope(extent, block, |this| {
return this.in_scope((extent, source_info), block, |this| {
this.as_temp(block, temp_lifetime, value)
});
}
let expr_ty = expr.ty.clone();
let expr_span = expr.span;
let temp = this.temp(expr_ty.clone(), expr_span);
let source_info = this.source_info(expr_span);
if !expr_ty.is_never() && temp_lifetime.is_some() {
this.cfg.push(block, Statement {

View File

@ -39,6 +39,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
match expr.kind {
ExprKind::Scope { extent, value } => {
let extent = (extent, source_info);
this.in_scope(extent, block, |this| this.into(destination, block, value))
}
ExprKind::Block { body: ast_block } => {
@ -233,7 +234,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
.collect();
let success = this.cfg.start_new_block();
let cleanup = this.diverge_cleanup();
let cleanup = this.diverge_cleanup(expr_span);
this.cfg.terminate(block, source_info, TerminatorKind::Call {
func: fun,
args: args,

View File

@ -24,7 +24,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
match expr.kind {
ExprKind::Scope { extent, value } => {
let value = this.hir.mirror(value);
this.in_scope(extent, block, |this| this.stmt_expr(block, value))
this.in_scope((extent, source_info), block, |this| this.stmt_expr(block, value))
}
ExprKind::Assign { lhs, rhs } => {
let lhs = this.hir.mirror(lhs);
@ -81,7 +81,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
*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, extent, block, continue_block);
this.exit_scope(expr_span, (extent, source_info), block, continue_block);
this.cfg.start_new_block().unit()
}
ExprKind::Break { label, value } => {
@ -99,7 +99,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
} else {
this.cfg.push_assign_unit(block, source_info, &destination)
}
this.exit_scope(expr_span, extent, block, break_block);
this.exit_scope(expr_span, (extent, source_info), block, break_block);
this.cfg.start_new_block().unit()
}
ExprKind::Return { value } => {
@ -116,7 +116,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
};
let extent = this.extent_of_return_scope();
let return_block = this.return_block();
this.exit_scope(expr_span, extent, block, return_block);
this.exit_scope(expr_span, (extent, source_info), block, return_block);
this.cfg.start_new_block().unit()
}
ExprKind::InlineAsm { asm, outputs, inputs } => {

View File

@ -306,7 +306,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let bool_ty = self.hir.bool_ty();
let eq_result = self.temp(bool_ty, test.span);
let eq_block = self.cfg.start_new_block();
let cleanup = self.diverge_cleanup();
let cleanup = self.diverge_cleanup(test.span);
self.cfg.terminate(block, source_info, TerminatorKind::Call {
func: Operand::Constant(box Constant {
span: test.span,

View File

@ -339,8 +339,9 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
let call_site_extent = CodeExtent::CallSiteScope(body.id());
let arg_extent = CodeExtent::ParameterScope(body.id());
let mut block = START_BLOCK;
unpack!(block = builder.in_scope(call_site_extent, block, |builder| {
unpack!(block = builder.in_scope(arg_extent, block, |builder| {
let source_info = builder.source_info(span);
unpack!(block = builder.in_scope((call_site_extent, source_info), block, |builder| {
unpack!(block = builder.in_scope((arg_extent, source_info), block, |builder| {
builder.args_and_body(block, &arguments, arg_extent, &body.value)
}));
// Attribute epilogue to function's closing brace

View File

@ -94,10 +94,11 @@ use rustc::ty::subst::{Kind, Subst};
use rustc::ty::{Ty, TyCtxt};
use rustc::mir::*;
use rustc::mir::transform::MirSource;
use syntax_pos::Span;
use syntax_pos::{Span};
use rustc_data_structures::indexed_vec::Idx;
use rustc_data_structures::fx::FxHashMap;
#[derive(Debug)]
pub struct Scope<'tcx> {
/// The visibility scope this scope was created in.
visibility_scope: VisibilityScope,
@ -114,7 +115,7 @@ pub struct Scope<'tcx> {
/// * pollutting the cleanup MIR with StorageDead creates
/// landing pads even though there's no actual destructors
/// * freeing up stack space has no effect during unwinding
needs_cleanup: bool,
pub(super) needs_cleanup: bool,
/// set of lvalues to drop when exiting this scope. This starts
/// out empty but grows as variables are declared during the
@ -141,6 +142,7 @@ pub struct Scope<'tcx> {
cached_exits: FxHashMap<(BasicBlock, CodeExtent), BasicBlock>,
}
#[derive(Debug)]
struct DropData<'tcx> {
/// span where drop obligation was incurred (typically where lvalue was declared)
span: Span,
@ -152,6 +154,7 @@ struct DropData<'tcx> {
kind: DropKind
}
#[derive(Debug)]
enum DropKind {
Value {
/// The cached block for the cleanups-on-diverge path. This block
@ -163,6 +166,7 @@ enum DropKind {
Storage
}
#[derive(Debug)]
struct FreeData<'tcx> {
/// span where free obligation was incurred
span: Span,
@ -269,17 +273,34 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
res
}
pub fn in_opt_scope<F, R>(&mut self,
opt_extent: Option<(CodeExtent, SourceInfo)>,
mut block: BasicBlock,
f: F)
-> BlockAnd<R>
where F: FnOnce(&mut Builder<'a, 'gcx, 'tcx>) -> BlockAnd<R>
{
debug!("in_opt_scope(opt_extent={:?}, block={:?})", opt_extent, block);
if let Some(extent) = opt_extent { self.push_scope(extent.0); }
let rv = unpack!(block = f(self));
if let Some(extent) = opt_extent {
unpack!(block = self.pop_scope(extent, block));
}
debug!("in_scope: exiting opt_extent={:?} block={:?}", opt_extent, block);
block.and(rv)
}
/// Convenience wrapper that pushes a scope and then executes `f`
/// to build its contents, popping the scope afterwards.
pub fn in_scope<F, R>(&mut self,
extent: CodeExtent,
extent: (CodeExtent, SourceInfo),
mut block: BasicBlock,
f: F)
-> BlockAnd<R>
where F: FnOnce(&mut Builder<'a, 'gcx, 'tcx>) -> BlockAnd<R>
{
debug!("in_scope(extent={:?}, block={:?})", extent, block);
self.push_scope(extent);
self.push_scope(extent.0);
let rv = unpack!(block = f(self));
unpack!(block = self.pop_scope(extent, block));
debug!("in_scope: exiting extent={:?} block={:?}", extent, block);
@ -307,20 +328,22 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
/// drops onto the end of `block` that are needed. This must
/// match 1-to-1 with `push_scope`.
pub fn pop_scope(&mut self,
extent: CodeExtent,
extent: (CodeExtent, SourceInfo),
mut block: BasicBlock)
-> BlockAnd<()> {
debug!("pop_scope({:?}, {:?})", extent, block);
// We need to have `cached_block`s available for all the drops, so we call diverge_cleanup
// to make sure all the `cached_block`s are filled in.
self.diverge_cleanup();
self.diverge_cleanup(extent.1.span);
let scope = self.scopes.pop().unwrap();
assert_eq!(scope.extent, extent);
assert_eq!(scope.extent, extent.0);
unpack!(block = build_scope_drops(&mut self.cfg,
&scope,
&self.scopes,
block,
self.arg_count));
self.cfg.push_end_region(block, extent.1, scope.extent);
block.unit()
}
@ -331,11 +354,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
/// module comment for details.
pub fn exit_scope(&mut self,
span: Span,
extent: CodeExtent,
extent: (CodeExtent, SourceInfo),
mut block: BasicBlock,
target: BasicBlock) {
debug!("exit_scope(extent={:?}, block={:?}, target={:?})", extent, block, target);
let scope_count = 1 + self.scopes.iter().rev().position(|scope| scope.extent == extent)
let scope_count = 1 + self.scopes.iter().rev().position(|scope| scope.extent == extent.0)
.unwrap_or_else(||{
span_bug!(span, "extent {:?} does not enclose", extent)
});
@ -346,7 +369,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let mut rest = &mut self.scopes[(len - scope_count)..];
while let Some((scope, rest_)) = {rest}.split_last_mut() {
rest = rest_;
block = if let Some(&e) = scope.cached_exits.get(&(target, extent)) {
block = if let Some(&e) = scope.cached_exits.get(&(target, extent.0)) {
self.cfg.terminate(block, scope.source_info(span),
TerminatorKind::Goto { target: e });
return;
@ -354,7 +377,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let b = self.cfg.start_new_block();
self.cfg.terminate(block, scope.source_info(span),
TerminatorKind::Goto { target: b });
scope.cached_exits.insert((target, extent), b);
scope.cached_exits.insert((target, extent.0), b);
b
};
unpack!(block = build_scope_drops(&mut self.cfg,
@ -362,6 +385,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
rest,
block,
self.arg_count));
// End all regions for scopes out of which we are breaking.
self.cfg.push_end_region(block, extent.1, scope.extent);
if let Some(ref free_data) = scope.free {
let next = self.cfg.start_new_block();
let free = build_free(self.hir.tcx(), &tmp, free_data, next);
@ -590,7 +617,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
/// This path terminates in Resume. Returns the start of the path.
/// See module comment for more details. None indicates theres no
/// cleanup to do at this point.
pub fn diverge_cleanup(&mut self) -> Option<BasicBlock> {
pub fn diverge_cleanup(&mut self, span: Span) -> Option<BasicBlock> {
if !self.scopes.iter().any(|scope| scope.needs_cleanup) {
return None;
}
@ -623,8 +650,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
resumeblk
};
for scope in scopes.iter_mut().filter(|s| s.needs_cleanup) {
target = build_diverge_scope(hir.tcx(), cfg, &unit_temp, scope, target);
for scope in scopes.iter_mut() {
target = build_diverge_scope(hir.tcx(), cfg, &unit_temp, span, scope, target);
}
Some(target)
}
@ -640,7 +667,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
}
let source_info = self.source_info(span);
let next_target = self.cfg.start_new_block();
let diverge_target = self.diverge_cleanup();
let diverge_target = self.diverge_cleanup(span);
self.cfg.terminate(block, source_info,
TerminatorKind::Drop {
location: location,
@ -658,7 +685,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
value: Operand<'tcx>) -> BlockAnd<()> {
let source_info = self.source_info(span);
let next_target = self.cfg.start_new_block();
let diverge_target = self.diverge_cleanup();
let diverge_target = self.diverge_cleanup(span);
self.cfg.terminate(block, source_info,
TerminatorKind::DropAndReplace {
location: location,
@ -681,7 +708,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let source_info = self.source_info(span);
let success_block = self.cfg.start_new_block();
let cleanup = self.diverge_cleanup();
let cleanup = self.diverge_cleanup(span);
self.cfg.terminate(block, source_info,
TerminatorKind::Assert {
@ -750,6 +777,7 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
cfg: &mut CFG<'tcx>,
unit_temp: &Lvalue<'tcx>,
span: Span,
scope: &mut Scope<'tcx>,
mut target: BasicBlock)
-> BasicBlock
@ -757,9 +785,9 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
// Build up the drops in **reverse** order. The end result will
// look like:
//
// [drops[n]] -...-> [drops[0]] -> [Free] -> [target]
// | |
// +------------------------------------+
// [EndRegion Block] -> [drops[n]] -...-> [drops[0]] -> [Free] -> [target]
// | |
// +---------------------------------------------------------+
// code for scope
//
// The code in this function reads from right to left. At each
@ -789,9 +817,16 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
// Next, build up the drops. Here we iterate the vector in
// *forward* order, so that we generate drops[0] first (right to
// left in diagram above).
for drop_data in &mut scope.drops {
for (j, drop_data) in scope.drops.iter_mut().enumerate() {
debug!("build_diverge_scope drop_data[{}]: {:?}", j, drop_data);
// Only full value drops are emitted in the diverging path,
// not StorageDead.
//
// Note: This may not actually be what we desire (are we
// "freeing" stack storage as we unwind, or merely observing a
// frozen stack)? In particular, the intent may have been to
// match the behavior of clang, but on inspection eddyb says
// this is not what clang does.
let cached_block = match drop_data.kind {
DropKind::Value { ref mut cached_block } => cached_block,
DropKind::Storage => continue
@ -811,6 +846,15 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
};
}
// Finally, push the EndRegion block, used by mir-borrowck. (Block
// becomes trivial goto after pass that removes all EndRegions.)
{
let block = cfg.start_new_cleanup_block();
cfg.push_end_region(block, source_info(span), scope.extent);
cfg.terminate(block, source_info(span), TerminatorKind::Goto { target: target });
target = block
}
target
}

View File

@ -22,9 +22,14 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Block {
// We have to eagerly translate the "spine" of the statements
// in order to get the lexical scoping correctly.
let stmts = mirror_stmts(cx, self.id, &*self.stmts);
let opt_def_id = cx.tcx.hir.opt_local_def_id(self.id);
let opt_destruction_extent = opt_def_id.and_then(|def_id| {
cx.tcx.region_maps(def_id).opt_destruction_extent(self.id)
});
Block {
targeted_by_break: self.targeted_by_break,
extent: CodeExtent::Misc(self.id),
opt_destruction_extent: opt_destruction_extent,
span: self.span,
stmts: stmts,
expr: self.expr.to_ref(),
@ -37,7 +42,11 @@ fn mirror_stmts<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
stmts: &'tcx [hir::Stmt])
-> Vec<StmtRef<'tcx>> {
let mut result = vec![];
let opt_def_id = cx.tcx.hir.opt_local_def_id(block_id);
for (index, stmt) in stmts.iter().enumerate() {
let opt_dxn_ext = opt_def_id.and_then(|def_id| {
cx.tcx.region_maps(def_id).opt_destruction_extent(stmt.node.id())
});
match stmt.node {
hir::StmtExpr(ref expr, id) |
hir::StmtSemi(ref expr, id) => {
@ -47,6 +56,7 @@ fn mirror_stmts<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
scope: CodeExtent::Misc(id),
expr: expr.to_ref(),
},
opt_destruction_extent: opt_dxn_ext,
})))
}
hir::StmtDecl(ref decl, id) => {
@ -69,6 +79,7 @@ fn mirror_stmts<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
pattern: pattern,
initializer: local.init.to_ref(),
},
opt_destruction_extent: opt_dxn_ext,
})));
}
}

View File

@ -33,6 +33,7 @@ pub use rustc_const_eval::pattern::{BindingMode, Pattern, PatternKind, FieldPatt
pub struct Block<'tcx> {
pub targeted_by_break: bool,
pub extent: CodeExtent,
pub opt_destruction_extent: Option<CodeExtent>,
pub span: Span,
pub stmts: Vec<StmtRef<'tcx>>,
pub expr: Option<ExprRef<'tcx>>,
@ -47,6 +48,7 @@ pub enum StmtRef<'tcx> {
pub struct Stmt<'tcx> {
pub span: Span,
pub kind: StmtKind<'tcx>,
pub opt_destruction_extent: Option<CodeExtent>,
}
#[derive(Clone, Debug)]

View File

@ -0,0 +1,84 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! This module provides one pass, `CleanEndRegions`, that reduces the
//! set of `EndRegion` statements in the MIR.
//!
//! The "pass" is actually implemented as two traversals (aka visits)
//! of the input MIR. The first traversal, `GatherBorrowedRegions`,
//! finds all of the regions in the MIR that are involved in a borrow.
//!
//! The second traversal, `DeleteTrivialEndRegions`, walks over the
//! MIR and removes any `EndRegion` that is applied to a region that
//! was not seen in the previous pass.
use rustc_data_structures::fx::FxHashSet;
use rustc::middle::region::CodeExtent;
use rustc::mir::transform::{MirPass, MirSource};
use rustc::mir::{BasicBlock, Location, Mir, Rvalue, Statement, StatementKind};
use rustc::mir::visit::{MutVisitor, Visitor};
use rustc::ty::{RegionKind, TyCtxt};
pub struct CleanEndRegions;
struct GatherBorrowedRegions {
seen_regions: FxHashSet<CodeExtent>,
}
struct DeleteTrivialEndRegions<'a> {
seen_regions: &'a FxHashSet<CodeExtent>,
}
impl MirPass for CleanEndRegions {
fn run_pass<'a, 'tcx>(&self,
_tcx: TyCtxt<'a, 'tcx, 'tcx>,
_source: MirSource,
mir: &mut Mir<'tcx>) {
let mut gather = GatherBorrowedRegions { seen_regions: FxHashSet() };
gather.visit_mir(mir);
let mut delete = DeleteTrivialEndRegions { seen_regions: &mut gather.seen_regions };
delete.visit_mir(mir);
}
}
impl<'tcx> Visitor<'tcx> for GatherBorrowedRegions {
fn visit_rvalue(&mut self,
rvalue: &Rvalue<'tcx>,
location: Location) {
if let Rvalue::Ref(r, _, _) = *rvalue {
if let RegionKind::ReScope(ce) = *r {
self.seen_regions.insert(ce);
}
}
self.super_rvalue(rvalue, location);
}
}
impl<'a, 'tcx> MutVisitor<'tcx> for DeleteTrivialEndRegions<'a> {
fn visit_statement(&mut self,
block: BasicBlock,
statement: &mut Statement<'tcx>,
location: Location) {
let mut delete_it = false;
if let StatementKind::EndRegion(ref extent) = statement.kind {
if !self.seen_regions.contains(extent) {
delete_it = true;
}
}
if delete_it {
statement.kind = StatementKind::Nop;
}
self.super_statement(block, statement, location);
}
}

View File

@ -65,6 +65,15 @@ impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> {
substs: &mut ClosureSubsts<'tcx>) {
*substs = self.tcx.erase_regions(substs);
}
fn visit_statement(&mut self,
_block: BasicBlock,
statement: &mut Statement<'tcx>,
_location: Location) {
if let StatementKind::EndRegion(_) = statement.kind {
statement.kind = StatementKind::Nop;
}
}
}
pub struct EraseRegions;

View File

@ -24,6 +24,7 @@ use syntax::ast;
use syntax_pos::{DUMMY_SP, Span};
use transform;
pub mod clean_end_regions;
pub mod simplify_branches;
pub mod simplify;
pub mod erase_regions;

View File

@ -894,6 +894,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
StatementKind::StorageLive(_) |
StatementKind::StorageDead(_) |
StatementKind::InlineAsm {..} |
StatementKind::EndRegion(_) |
StatementKind::Nop => {}
}
});

View File

@ -413,6 +413,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
}
}
StatementKind::InlineAsm { .. } |
StatementKind::EndRegion(_) |
StatementKind::Nop => {}
}
}

View File

@ -46,6 +46,7 @@ impl<'tcx> MirPatch<'tcx> {
for (bb, block) in mir.basic_blocks().iter_enumerated() {
if let TerminatorKind::Resume = block.terminator().kind {
if block.statements.len() > 0 {
assert!(resume_stmt_block.is_none());
resume_stmt_block = Some(bb);
} else {
resume_block = Some(bb);

View File

@ -125,6 +125,7 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> {
self.record("Statement", statement);
self.record(match statement.kind {
StatementKind::Assign(..) => "StatementKind::Assign",
StatementKind::EndRegion(..) => "StatementKind::EndRegion",
StatementKind::SetDiscriminant { .. } => "StatementKind::SetDiscriminant",
StatementKind::StorageLive(..) => "StatementKind::StorageLive",
StatementKind::StorageDead(..) => "StatementKind::StorageDead",

View File

@ -284,6 +284,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
}
mir::StatementKind::StorageLive(_) |
mir::StatementKind::StorageDead(_) |
mir::StatementKind::EndRegion(_) |
mir::StatementKind::Nop => {}
mir::StatementKind::InlineAsm { .. } |
mir::StatementKind::SetDiscriminant{ .. } => {

View File

@ -86,6 +86,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
asm::trans_inline_asm(&bcx, asm, outputs, input_vals);
bcx
}
mir::StatementKind::EndRegion(_) |
mir::StatementKind::Nop => bcx,
}
}

View File

@ -32,9 +32,9 @@ pub fn droppy() {
// CHECK-NOT: invoke{{.*}}drop{{.*}}SomeUniqueName
// CHECK: call{{.*}}drop{{.*}}SomeUniqueName
// CHECK: call{{.*}}drop{{.*}}SomeUniqueName
// CHECK: call{{.*}}drop{{.*}}SomeUniqueName
// CHECK-NOT: call{{.*}}drop{{.*}}SomeUniqueName
// CHECK: invoke{{.*}}drop{{.*}}SomeUniqueName
// CHECK: call{{.*}}drop{{.*}}SomeUniqueName
// CHECK: invoke{{.*}}drop{{.*}}SomeUniqueName
// CHECK: call{{.*}}drop{{.*}}SomeUniqueName
// CHECK-NOT: {{(call|invoke).*}}drop{{.*}}SomeUniqueName

View File

@ -22,7 +22,32 @@ All the test information is in comments so the test is runnable.
For each $file_name, compiletest expects [$expected_line_0, ...,
$expected_line_N] to appear in the dumped MIR in order. Currently it allows
other non-matched lines before, after and in-between.
other non-matched lines before, after and in-between. Note that this includes
lines that end basic blocks or begin new ones; it is good practice
in your tests to include the terminator for each of your basic blocks as an
internal sanity check guarding against a test like:
```
bb0: {
StorageLive(_1);
_1 = const true;
StorageDead(_1);
}
```
that will inadvertantly pattern-matching against:
```
bb0: {
StorageLive(_1);
_1 = const true;
goto -> bb1
}
bb1: {
StorageDead(_1);
return;
}
```
Lines match ignoring whitespace, and the prefix "//" is removed.

View File

@ -50,7 +50,7 @@ fn main() {
// StorageLive(_6);
// StorageLive(_7);
// _7 = _4;
// replace(_6 <- _7) -> [return: bb5, unwind: bb4];
// replace(_6 <- _7) -> [return: bb6, unwind: bb7];
// }
// bb1: {
// resume;
@ -59,24 +59,30 @@ fn main() {
// drop(_4) -> bb1;
// }
// bb3: {
// drop(_6) -> bb2;
// goto -> bb2;
// }
// bb4: {
// drop(_7) -> bb3;
// drop(_6) -> bb3;
// }
// bb5: {
// drop(_7) -> [return: bb6, unwind: bb3];
// goto -> bb4;
// }
// bb6: {
// StorageDead(_7);
// _0 = ();
// drop(_6) -> [return: bb7, unwind: bb2];
// drop(_7) -> [return: bb8, unwind: bb4];
// }
// bb7: {
// StorageDead(_6);
// drop(_4) -> bb8;
// drop(_7) -> bb5;
// }
// bb8: {
// StorageDead(_7);
// _0 = ();
// drop(_6) -> [return: bb9, unwind: bb2];
// }
// bb9: {
// StorageDead(_6);
// drop(_4) -> bb10;
// }
// bb10: {
// StorageDead(_4);
// StorageDead(_2);
// StorageDead(_1);

View File

@ -0,0 +1,38 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -Z identify_regions
// ignore-tidy-linelength
// This is just about the simplest program that exhibits an EndRegion.
fn main() {
let a = 3;
let b = &a;
}
// END RUST SOURCE
// START rustc.node4.SimplifyCfg-qualify-consts.after.mir
// let mut _0: ();
// let _1: i32;
// let _2: &'6_1rce i32;
//
// bb0: {
// StorageLive(_1);
// _1 = const 3i32;
// StorageLive(_2);
// _2 = &'6_1rce _1;
// _0 = ();
// StorageDead(_2);
// EndRegion('6_1rce);
// StorageDead(_1);
// return;
// }
// END rustc.node4.SimplifyCfg-qualify-consts.after.mir

View File

@ -0,0 +1,66 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -Z identify_regions
// ignore-tidy-linelength
// We will EndRegion for borrows in a loop that occur before break but
// not those after break.
fn main() {
loop {
let a = true;
let b = &a;
if a { break; }
let c = &a;
}
}
// END RUST SOURCE
// START rustc.node4.SimplifyCfg-qualify-consts.after.mir
// let mut _0: ();
// let _2: bool;
// let _3: &'7_1rce bool;
// let _7: &'7_3rce bool;
// let mut _4: ();
// let mut _5: bool;
// bb0: {
// goto -> bb1;
// }
// bb1: {
// StorageLive(_2);
// _2 = const true;
// StorageLive(_3);
// _3 = &'7_1rce _2;
// StorageLive(_5);
// _5 = _2;
// switchInt(_5) -> [0u8: bb3, otherwise: bb2];
// }
// bb2: {
// _0 = ();
// StorageDead(_5);
// StorageDead(_3);
// EndRegion('7_1rce);
// StorageDead(_2);
// return;
// }
// bb3: {
// StorageDead(_5);
// StorageLive(_7);
// _7 = &'7_3rce _2;
// _1 = ();
// StorageDead(_7);
// EndRegion('7_3rce);
// StorageDead(_3);
// EndRegion('7_1rce);
// StorageDead(_2);
// goto -> bb1;
// }
// END rustc.node4.SimplifyCfg-qualify-consts.after.mir

View File

@ -0,0 +1,69 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -Z identify_regions
// ignore-tidy-linelength
// Binding the borrow's subject outside the loop does not increase the
// scope of the borrow.
fn main() {
let mut a;
loop {
a = true;
let b = &a;
if a { break; }
let c = &a;
}
}
// END RUST SOURCE
// START rustc.node4.SimplifyCfg-qualify-consts.after.mir
// let mut _0: ();
// let mut _1: bool;
// let _3: &'9_1rce bool;
// let _7: &'9_3rce bool;
// let mut _2: ();
// let mut _4: ();
// let mut _5: bool;
//
// bb0: {
// StorageLive(_1);
// goto -> bb1;
// }
// bb1: {
// _1 = const true;
// StorageLive(_3);
// _3 = &'9_1rce _1;
// StorageLive(_5);
// _5 = _1;
// switchInt(_5) -> [0u8: bb3, otherwise: bb2];
// }
// bb2: {
// _0 = ();
// StorageDead(_5);
// StorageDead(_3);
// EndRegion('9_1rce);
// StorageDead(_1);
// return;
// }
// bb3: {
// _4 = ();
// StorageDead(_5);
// StorageLive(_7);
// _7 = &'9_3rce _1;
// _2 = ();
// StorageDead(_7);
// EndRegion('9_3rce);
// StorageDead(_3);
// EndRegion('9_1rce);
// goto -> bb1;
// }
// END rustc.node4.SimplifyCfg-qualify-consts.after.mir

View File

@ -0,0 +1,75 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -Z identify_regions
// ignore-tidy-linelength
// Unwinding should EndRegion for in-scope borrows: Direct borrows.
fn main() {
let d = D(0);
let a = 0;
let b = &a;
foo(*b);
let c = &a;
}
struct D(i32);
impl Drop for D { fn drop(&mut self) { println!("dropping D({})", self.0); } }
fn foo(i: i32) {
if i > 0 { panic!("im positive"); }
}
// END RUST SOURCE
// START rustc.node4.SimplifyCfg-qualify-consts.after.mir
// let mut _0: ();
// let _1: D;
// let _3: i32;
// let _4: &'6_2rce i32;
// let _7: &'6_4rce i32;
// let mut _5: ();
// let mut _6: i32;
//
// bb0: {
// StorageLive(_1);
// _1 = D::{{constructor}}(const 0i32,);
// StorageLive(_3);
// _3 = const 0i32;
// StorageLive(_4);
// _4 = &'6_2rce _3;
// StorageLive(_6);
// _6 = (*_4);
// _5 = const foo(_6) -> [return: bb2, unwind: bb3];
// }
// bb1: {
// resume;
// }
// bb2: {
// StorageDead(_6);
// StorageLive(_7);
// _7 = &'6_4rce _3;
// _0 = ();
// StorageDead(_7);
// EndRegion('6_4rce);
// StorageDead(_4);
// EndRegion('6_2rce);
// StorageDead(_3);
// drop(_1) -> bb4;
// }
// bb3: {
// EndRegion('6_2rce);
// drop(_1) -> bb1;
// }
// bb4: {
// StorageDead(_1);
// return;
// }
// END rustc.node4.SimplifyCfg-qualify-consts.after.mir

View File

@ -0,0 +1,80 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -Z identify_regions -Z span_free_formats
// ignore-tidy-linelength
// Unwinding should EndRegion for in-scope borrows: Borrowing via by-ref closure.
fn main() {
let d = D(0);
foo(|| -> i32 { d.0 });
}
struct D(i32);
impl Drop for D { fn drop(&mut self) { println!("dropping D({})", self.0); } }
fn foo<F>(f: F) where F: FnOnce() -> i32 {
if f() > 0 { panic!("im positive"); }
}
// END RUST SOURCE
// START rustc.node4.SimplifyCfg-qualify-consts.after.mir
// fn main() -> () {
// let mut _0: ();
// let _1: D;
// let mut _2: ();
// let mut _3: ();
// let mut _4: [closure@NodeId(18) d: &'19mce D];
// let mut _5: &'19mce D;
//
// bb0: {
// StorageLive(_1);
// _1 = D::{{constructor}}(const 0i32,);
// StorageLive(_4);
// StorageLive(_5);
// _5 = &'19mce _1;
// _4 = [closure@NodeId(18)] { d: _5 };
// StorageDead(_5);
// _3 = const foo(_4) -> [return: bb2, unwind: bb3];
// }
// bb1: {
// resume;
// }
// bb2: {
// StorageDead(_4);
// EndRegion('19mce);
// _0 = ();
// drop(_1) -> bb4;
// }
// bb3: {
// EndRegion('19mce);
// drop(_1) -> bb1;
// }
// bb4: {
// StorageDead(_1);
// return;
// }
// }
// END rustc.node4.SimplifyCfg-qualify-consts.after.mir
// START rustc.node18.SimplifyCfg-qualify-consts.after.mir
// fn main::{{closure}}(_1: [closure@NodeId(18) d:&'19mce D]) -> i32 {
// let mut _0: i32;
// let mut _2: i32;
//
// bb0: {
// StorageLive(_2);
// _2 = ((*(_1.0: &'19mce D)).0: i32);
// _0 = _2;
// StorageDead(_2);
// return;
// }
// END rustc.node18.SimplifyCfg-qualify-consts.after.mir

View File

@ -0,0 +1,83 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -Z identify_regions -Z span_free_formats
// ignore-tidy-linelength
// Unwinding should EndRegion for in-scope borrows: 2nd borrow within by-ref closure.
fn main() {
let d = D(0);
foo(|| -> i32 { let r = &d; r.0 });
}
struct D(i32);
impl Drop for D { fn drop(&mut self) { println!("dropping D({})", self.0); } }
fn foo<F>(f: F) where F: FnOnce() -> i32 {
if f() > 0 { panic!("im positive"); }
}
// END RUST SOURCE
// START rustc.node4.SimplifyCfg-qualify-consts.after.mir
// let mut _0: ();
// let _1: D;
// let mut _2: ();
// let mut _3: ();
// let mut _4: [closure@NodeId(22) d:&'23mce D];
// let mut _5: &'23mce D;
//
// bb0: {
// StorageLive(_1);
// _1 = D::{{constructor}}(const 0i32,);
// StorageLive(_4);
// StorageLive(_5);
// _5 = &'23mce _1;
// _4 = [closure@NodeId(22)] { d: _5 };
// StorageDead(_5);
// _3 = const foo(_4) -> [return: bb2, unwind: bb3];
// }
// bb1: {
// resume;
// }
// bb2: {
// StorageDead(_4);
// EndRegion('23mce);
// _0 = ();
// drop(_1) -> bb4;
// }
// bb3: {
// EndRegion('23mce);
// drop(_1) -> bb1;
// }
// bb4: {
// StorageDead(_1);
// return;
// }
// END rustc.node4.SimplifyCfg-qualify-consts.after.mir
// START rustc.node22.SimplifyCfg-qualify-consts.after.mir
// fn main::{{closure}}(_1: [closure@NodeId(22) d:&'23mce D]) -> i32 {
// let mut _0: i32;
// let _2: &'14_0rce D;
// let mut _3: i32;
//
// bb0: {
// StorageLive(_2);
// _2 = &'14_0rce (*(_1.0: &'23mce D));
// StorageLive(_3);
// _3 = ((*_2).0: i32);
// _0 = _3;
// StorageDead(_3);
// StorageDead(_2);
// EndRegion('14_0rce);
// return;
// }
// END rustc.node22.SimplifyCfg-qualify-consts.after.mir

View File

@ -0,0 +1,97 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -Z identify_regions -Z span_free_formats
// ignore-tidy-linelength
// Unwinding should EndRegion for in-scope borrows: Borrow of moved data.
fn main() {
let d = D(0);
foo(move || -> i32 { let r = &d; r.0 });
}
struct D(i32);
impl Drop for D { fn drop(&mut self) { println!("dropping D({})", self.0); } }
fn foo<F>(f: F) where F: FnOnce() -> i32 {
if f() > 0 { panic!("im positive"); }
}
// END RUST SOURCE
// START rustc.node4.SimplifyCfg-qualify-consts.after.mir
// fn main() -> () {
// let mut _0: ();
// let _1: D;
// let mut _2: ();
// let mut _3: ();
// let mut _4: [closure@NodeId(22) d:D];
// let mut _5: D;
//
// bb0: {
// StorageLive(_1);
// _1 = D::{{constructor}}(const 0i32,);
// StorageLive(_4);
// StorageLive(_5);
// _5 = _1;
// _4 = [closure@NodeId(22)] { d: _5 };
// drop(_5) -> [return: bb4, unwind: bb3];
// }
// bb1: {
// resume;
// }
// bb2: {
// drop(_1) -> bb1;
// }
// bb3: {
// drop(_4) -> bb2;
// }
// bb4: {
// StorageDead(_5);
// _3 = const foo(_4) -> [return: bb5, unwind: bb3];
// }
// bb5: {
// drop(_4) -> [return: bb6, unwind: bb2];
// }
// bb6: {
// StorageDead(_4);
// _0 = ();
// drop(_1) -> bb7;
// }
// bb7: {
// StorageDead(_1);
// return;
// }
// }
// END rustc.node4.SimplifyCfg-qualify-consts.after.mir
// START rustc.node22.SimplifyCfg-qualify-consts.after.mir
// fn main::{{closure}}(_1: [closure@NodeId(22) d:D]) -> i32 {
// let mut _0: i32;
// let _2: &'14_0rce D;
// let mut _3: ();
// let mut _4: i32;
//
// bb0: {
// StorageLive(_2);
// _2 = &'14_0rce (_1.0: D);
// StorageLive(_4);
// _4 = ((*_2).0: i32);
// _0 = _4;
// StorageDead(_4);
// StorageDead(_2);
// EndRegion('14_0rce);
// drop(_1) -> bb1;
// }
// bb1: {
// return;
// }
// }
// END rustc.node22.SimplifyCfg-qualify-consts.after.mir

View File

@ -0,0 +1,86 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -Z identify_regions -Z span_free_formats
// ignore-tidy-linelength
// Unwinding should EndRegion for in-scope borrows: Move of borrow into closure.
fn main() {
let d = D(0);
let r = &d;
foo(move || -> i32 { r.0 });
}
struct D(i32);
impl Drop for D { fn drop(&mut self) { println!("dropping D({})", self.0); } }
fn foo<F>(f: F) where F: FnOnce() -> i32 {
if f() > 0 { panic!("im positive"); }
}
// END RUST SOURCE
// START rustc.node4.SimplifyCfg-qualify-consts.after.mir
// fn main() -> () {
// let mut _0: ();
// let _1: D;
// let _3: &'6_1rce D;
// let mut _2: ();
// let mut _4: ();
// let mut _5: [closure@NodeId(22) r:&'6_1rce D];
// let mut _6: &'6_1rce D;
//
// bb0: {
// StorageLive(_1);
// _1 = D::{{constructor}}(const 0i32,);
// StorageLive(_3);
// _3 = &'6_1rce _1;
// StorageLive(_5);
// StorageLive(_6);
// _6 = _3;
// _5 = [closure@NodeId(22)] { r: _6 };
// StorageDead(_6);
// _4 = const foo(_5) -> [return: bb2, unwind: bb3];
// }
// bb1: {
// resume;
// }
// bb2: {
// StorageDead(_5);
// _0 = ();
// StorageDead(_3);
// EndRegion('6_1rce);
// drop(_1) -> bb4;
// }
// bb3: {
// EndRegion('6_1rce);
// drop(_1) -> bb1;
// }
// bb4: {
// StorageDead(_1);
// return;
// }
// }
// END rustc.node4.SimplifyCfg-qualify-consts.after.mir
// START rustc.node22.SimplifyCfg-qualify-consts.after.mir
// fn main::{{closure}}(_1: [closure@NodeId(22) r:&'6_1rce D]) -> i32 {
// let mut _0: i32;
// let mut _2: i32;
//
// bb0: {
// StorageLive(_2);
// _2 = ((*(_1.0: &'6_1rce D)).0: i32);
// _0 = _2;
// StorageDead(_2);
// return;
// }
// }
// END rustc.node22.SimplifyCfg-qualify-consts.after.mir

View File

@ -0,0 +1,85 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -Z identify_regions -Z span_free_formats
// ignore-tidy-linelength
// This test models a scenario that arielb1 found during review.
// Namely, any filtering of EndRegions must ensure to continue to emit
// any necessary EndRegions that occur earlier in the source than the
// first borrow involving that region.
//
// It is tricky to actually construct examples of this, which is the
// main reason that I am keeping this test even though I have now
// removed the pre-filter that motivated the test in the first place.
fn main() {
let mut second_iter = false;
let x = 3;
'a: loop {
let mut y;
loop {
if second_iter {
break 'a; // want to generate `EndRegion('a)` here
} else {
y = &/*'a*/ x;
}
second_iter = true;
}
}
}
// END RUST SOURCE
// START rustc.node4.SimplifyCfg-qualify-consts.after.mir
// fn main() -> () {
// let mut _0: ();
// let mut _1: bool;
// let _2: i32;
// let mut _4: &'13_0rce i32;
// let mut _3: ();
// let mut _5: !;
// let mut _6: ();
// let mut _7: bool;
// let mut _8: !;
//
// bb0: {
// StorageLive(_1);
// _1 = const false;
// StorageLive(_2);
// _2 = const 3i32;
// StorageLive(_4);
// goto -> bb1;
// }
//
// bb1: {
// StorageLive(_7);
// _7 = _1;
// switchInt(_7) -> [0u8: bb3, otherwise: bb2];
// }
//
// bb2: {
// _0 = ();
// StorageDead(_7);
// StorageDead(_4);
// EndRegion('13_0rce);
// StorageDead(_2);
// StorageDead(_1);
// return;
// }
//
// bb3: {
// _4 = &'13_0rce _2;
// _6 = ();
// StorageDead(_7);
// _1 = const true;
// _3 = ();
// goto -> bb1;
// }
// }