mirror of
https://github.com/rust-lang/rust
synced 2024-09-15 22:50:55 +00:00
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:
commit
04145943a2
|
@ -226,6 +226,9 @@ fn hash_stable<W: StableHasherResult>(&self,
|
|||
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);
|
||||
|
|
|
@ -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 @@ fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
|
|||
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 @@ fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
|
|||
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 @@ fn fmt_tuple(fmt: &mut Formatter, lvs: &[Operand]) -> fmt::Result {
|
|||
|
||||
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 @@ fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F)
|
|||
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 @@ fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
|
|||
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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -325,6 +325,7 @@ fn super_statement(&mut self,
|
|||
ref $($mutability)* rvalue) => {
|
||||
self.visit_assign(block, lvalue, rvalue, location);
|
||||
}
|
||||
StatementKind::EndRegion(_) => {}
|
||||
StatementKind::SetDiscriminant{ ref $($mutability)* lvalue, .. } => {
|
||||
self.visit_lvalue(lvalue, LvalueContext::Store, location);
|
||||
}
|
||||
|
|
|
@ -893,6 +893,10 @@ fn parse_optimization_fuel(slot: &mut Option<(String, u64)>, v: Option<&str>) ->
|
|||
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,
|
||||
|
|
|
@ -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 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|||
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 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|||
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) {
|
||||
|
|
|
@ -474,6 +474,7 @@ fn statement_effect(&self,
|
|||
mir::StatementKind::StorageLive(_) |
|
||||
mir::StatementKind::StorageDead(_) |
|
||||
mir::StatementKind::InlineAsm { .. } |
|
||||
mir::StatementKind::EndRegion(_) |
|
||||
mir::StatementKind::Nop => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -594,6 +594,11 @@ fn drop_flags_for_locs(&mut self) {
|
|||
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));
|
||||
}
|
||||
|
|
|
@ -413,6 +413,7 @@ fn gather_statement(&mut self, loc: Location, stmt: &Statement<'tcx>) {
|
|||
"SetDiscriminant should not exist during borrowck");
|
||||
}
|
||||
StatementKind::InlineAsm { .. } |
|
||||
StatementKind::EndRegion(_) |
|
||||
StatementKind::Nop => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -914,6 +914,9 @@ macro_rules! try_with_f {
|
|||
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);
|
||||
|
|
|
@ -21,21 +21,24 @@ pub fn ast_block(&mut self,
|
|||
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 @@ fn ast_block_stmts(&mut self,
|
|||
// 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 @@ fn ast_block_stmts(&mut self,
|
|||
|
||||
// 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 @@ fn ast_block_stmts(&mut self,
|
|||
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;
|
||||
|
|
|
@ -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 @@ pub fn push(&mut self, block: BasicBlock, statement: Statement<'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,
|
||||
|
|
|
@ -40,7 +40,7 @@ fn expr_as_lvalue(&mut self,
|
|||
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));
|
||||
|
|
|
@ -56,6 +56,8 @@ fn expr_as_operand(&mut self,
|
|||
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)
|
||||
});
|
||||
|
|
|
@ -59,6 +59,7 @@ fn expr_as_rvalue(&mut self,
|
|||
|
||||
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 @@ fn expr_as_rvalue(&mut self,
|
|||
// 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:
|
||||
|
|
|
@ -39,16 +39,16 @@ fn expr_as_temp(&mut self,
|
|||
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 {
|
||||
|
|
|
@ -39,6 +39,7 @@ pub fn into_expr(&mut self,
|
|||
|
||||
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 @@ pub fn into_expr(&mut self,
|
|||
.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,
|
||||
|
|
|
@ -24,7 +24,7 @@ pub fn stmt_expr(&mut self, mut block: BasicBlock, expr: Expr<'tcx>) -> BlockAnd
|
|||
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 @@ pub fn stmt_expr(&mut self, mut block: BasicBlock, expr: Expr<'tcx>) -> BlockAnd
|
|||
*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 @@ pub fn stmt_expr(&mut self, mut block: BasicBlock, expr: Expr<'tcx>) -> BlockAnd
|
|||
} 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 @@ pub fn stmt_expr(&mut self, mut block: BasicBlock, expr: Expr<'tcx>) -> BlockAnd
|
|||
};
|
||||
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 } => {
|
||||
|
|
|
@ -306,7 +306,7 @@ pub fn perform_test(&mut self,
|
|||
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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -94,10 +94,11 @@
|
|||
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 @@ pub fn in_breakable_scope<F, R>(&mut self,
|
|||
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 @@ pub fn push_scope(&mut self, extent: CodeExtent) {
|
|||
/// 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 @@ pub fn pop_scope(&mut self,
|
|||
/// 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 @@ pub fn exit_scope(&mut self,
|
|||
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 @@ pub fn exit_scope(&mut self,
|
|||
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 @@ pub fn exit_scope(&mut self,
|
|||
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 @@ pub fn schedule_box_free(&mut self,
|
|||
/// This path terminates in Resume. Returns the start of the path.
|
||||
/// See module comment for more details. None indicates there’s 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 @@ pub fn diverge_cleanup(&mut self) -> Option<BasicBlock> {
|
|||
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 @@ pub fn build_drop(&mut self,
|
|||
}
|
||||
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 @@ pub fn build_drop_and_replace(&mut self,
|
|||
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 @@ pub fn assert(&mut self, block: BasicBlock,
|
|||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -22,9 +22,14 @@ fn make_mirror<'a, 'gcx>(self, cx: &mut Cx<'a, 'gcx, 'tcx>) -> Block<'tcx> {
|
|||
// 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,
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
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)]
|
||||
|
|
84
src/librustc_mir/transform/clean_end_regions.rs
Normal file
84
src/librustc_mir/transform/clean_end_regions.rs
Normal 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);
|
||||
}
|
||||
}
|
|
@ -65,6 +65,15 @@ fn visit_closure_substs(&mut self,
|
|||
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;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
use syntax_pos::{DUMMY_SP, Span};
|
||||
use transform;
|
||||
|
||||
pub mod clean_end_regions;
|
||||
pub mod simplify_branches;
|
||||
pub mod simplify;
|
||||
pub mod erase_regions;
|
||||
|
|
|
@ -894,6 +894,7 @@ fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>, locat
|
|||
StatementKind::StorageLive(_) |
|
||||
StatementKind::StorageDead(_) |
|
||||
StatementKind::InlineAsm {..} |
|
||||
StatementKind::EndRegion(_) |
|
||||
StatementKind::Nop => {}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -413,6 +413,7 @@ fn check_stmt(&mut self, mir: &Mir<'tcx>, stmt: &Statement<'tcx>) {
|
|||
}
|
||||
}
|
||||
StatementKind::InlineAsm { .. } |
|
||||
StatementKind::EndRegion(_) |
|
||||
StatementKind::Nop => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ pub fn new(mir: &Mir<'tcx>) -> Self {
|
|||
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);
|
||||
|
|
|
@ -125,6 +125,7 @@ fn visit_statement(&mut self,
|
|||
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",
|
||||
|
|
|
@ -284,6 +284,7 @@ fn trans(&mut self) -> Result<Const<'tcx>, ConstEvalErr<'tcx>> {
|
|||
}
|
||||
mir::StatementKind::StorageLive(_) |
|
||||
mir::StatementKind::StorageDead(_) |
|
||||
mir::StatementKind::EndRegion(_) |
|
||||
mir::StatementKind::Nop => {}
|
||||
mir::StatementKind::InlineAsm { .. } |
|
||||
mir::StatementKind::SetDiscriminant{ .. } => {
|
||||
|
|
|
@ -86,6 +86,7 @@ pub fn trans_statement(&mut self,
|
|||
asm::trans_inline_asm(&bcx, asm, outputs, input_vals);
|
||||
bcx
|
||||
}
|
||||
mir::StatementKind::EndRegion(_) |
|
||||
mir::StatementKind::Nop => bcx,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
38
src/test/mir-opt/end_region_1.rs
Normal file
38
src/test/mir-opt/end_region_1.rs
Normal 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
|
66
src/test/mir-opt/end_region_2.rs
Normal file
66
src/test/mir-opt/end_region_2.rs
Normal 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
|
69
src/test/mir-opt/end_region_3.rs
Normal file
69
src/test/mir-opt/end_region_3.rs
Normal 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
|
75
src/test/mir-opt/end_region_4.rs
Normal file
75
src/test/mir-opt/end_region_4.rs
Normal 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
|
80
src/test/mir-opt/end_region_5.rs
Normal file
80
src/test/mir-opt/end_region_5.rs
Normal 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
|
83
src/test/mir-opt/end_region_6.rs
Normal file
83
src/test/mir-opt/end_region_6.rs
Normal 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
|
97
src/test/mir-opt/end_region_7.rs
Normal file
97
src/test/mir-opt/end_region_7.rs
Normal 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
|
86
src/test/mir-opt/end_region_8.rs
Normal file
86
src/test/mir-opt/end_region_8.rs
Normal 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
|
85
src/test/mir-opt/end_region_9.rs
Normal file
85
src/test/mir-opt/end_region_9.rs
Normal 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;
|
||||
// }
|
||||
// }
|
Loading…
Reference in a new issue