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 @@ for mir::StatementKind<'tcx> {
|
||||||
mir::StatementKind::StorageDead(ref lvalue) => {
|
mir::StatementKind::StorageDead(ref lvalue) => {
|
||||||
lvalue.hash_stable(hcx, hasher);
|
lvalue.hash_stable(hcx, hasher);
|
||||||
}
|
}
|
||||||
|
mir::StatementKind::EndRegion(ref extents) => {
|
||||||
|
extents.hash_stable(hcx, hasher);
|
||||||
|
}
|
||||||
mir::StatementKind::Nop => {}
|
mir::StatementKind::Nop => {}
|
||||||
mir::StatementKind::InlineAsm { ref asm, ref outputs, ref inputs } => {
|
mir::StatementKind::InlineAsm { ref asm, ref outputs, ref inputs } => {
|
||||||
asm.hash_stable(hcx, hasher);
|
asm.hash_stable(hcx, hasher);
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
use graphviz::IntoCow;
|
use graphviz::IntoCow;
|
||||||
use middle::const_val::ConstVal;
|
use middle::const_val::ConstVal;
|
||||||
|
use middle::region::CodeExtent;
|
||||||
use rustc_const_math::{ConstUsize, ConstInt, ConstMathErr};
|
use rustc_const_math::{ConstUsize, ConstInt, ConstMathErr};
|
||||||
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
|
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
|
||||||
use rustc_data_structures::control_flow_graph::dominators::{Dominators, dominators};
|
use rustc_data_structures::control_flow_graph::dominators::{Dominators, dominators};
|
||||||
|
@ -804,6 +805,10 @@ pub enum StatementKind<'tcx> {
|
||||||
inputs: Vec<Operand<'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.
|
/// No-op. Useful for deleting instructions without affecting statement indices.
|
||||||
Nop,
|
Nop,
|
||||||
}
|
}
|
||||||
|
@ -813,6 +818,8 @@ impl<'tcx> Debug for Statement<'tcx> {
|
||||||
use self::StatementKind::*;
|
use self::StatementKind::*;
|
||||||
match self.kind {
|
match self.kind {
|
||||||
Assign(ref lv, ref rv) => write!(fmt, "{:?} = {:?}", lv, rv),
|
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),
|
StorageLive(ref lv) => write!(fmt, "StorageLive({:?})", lv),
|
||||||
StorageDead(ref lv) => write!(fmt, "StorageDead({:?})", lv),
|
StorageDead(ref lv) => write!(fmt, "StorageDead({:?})", lv),
|
||||||
SetDiscriminant{lvalue: ref lv, variant_index: index} => {
|
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),
|
UnaryOp(ref op, ref a) => write!(fmt, "{:?}({:?})", op, a),
|
||||||
Discriminant(ref lval) => write!(fmt, "discriminant({:?})", lval),
|
Discriminant(ref lval) => write!(fmt, "discriminant({:?})", lval),
|
||||||
NullaryOp(ref op, ref t) => write!(fmt, "{:?}({:?})", op, t),
|
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 {
|
let kind_str = match borrow_kind {
|
||||||
BorrowKind::Shared => "",
|
BorrowKind::Shared => "",
|
||||||
BorrowKind::Mut | BorrowKind::Unique => "mut ",
|
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) => {
|
Aggregate(ref kind, ref lvs) => {
|
||||||
|
@ -1224,7 +1241,11 @@ impl<'tcx> Debug for Rvalue<'tcx> {
|
||||||
|
|
||||||
AggregateKind::Closure(def_id, _) => ty::tls::with(|tcx| {
|
AggregateKind::Closure(def_id, _) => ty::tls::with(|tcx| {
|
||||||
if let Some(node_id) = tcx.hir.as_local_node_id(def_id) {
|
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);
|
let mut struct_fmt = fmt.debug_struct(&name);
|
||||||
|
|
||||||
tcx.with_freevars(node_id, |freevars| {
|
tcx.with_freevars(node_id, |freevars| {
|
||||||
|
@ -1458,6 +1479,13 @@ impl<'tcx> TypeFoldable<'tcx> for Statement<'tcx> {
|
||||||
outputs: outputs.fold_with(folder),
|
outputs: outputs.fold_with(folder),
|
||||||
inputs: inputs.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,
|
Nop => Nop,
|
||||||
};
|
};
|
||||||
Statement {
|
Statement {
|
||||||
|
@ -1476,6 +1504,13 @@ impl<'tcx> TypeFoldable<'tcx> for Statement<'tcx> {
|
||||||
StorageDead(ref lvalue) => lvalue.visit_with(visitor),
|
StorageDead(ref lvalue) => lvalue.visit_with(visitor),
|
||||||
InlineAsm { ref outputs, ref inputs, .. } =>
|
InlineAsm { ref outputs, ref inputs, .. } =>
|
||||||
outputs.visit_with(visitor) || inputs.visit_with(visitor),
|
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,
|
Nop => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -325,6 +325,7 @@ macro_rules! make_mir_visitor {
|
||||||
ref $($mutability)* rvalue) => {
|
ref $($mutability)* rvalue) => {
|
||||||
self.visit_assign(block, lvalue, rvalue, location);
|
self.visit_assign(block, lvalue, rvalue, location);
|
||||||
}
|
}
|
||||||
|
StatementKind::EndRegion(_) => {}
|
||||||
StatementKind::SetDiscriminant{ ref $($mutability)* lvalue, .. } => {
|
StatementKind::SetDiscriminant{ ref $($mutability)* lvalue, .. } => {
|
||||||
self.visit_lvalue(lvalue, LvalueContext::Store, location);
|
self.visit_lvalue(lvalue, LvalueContext::Store, location);
|
||||||
}
|
}
|
||||||
|
|
|
@ -893,6 +893,10 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
|
||||||
DB_OPTIONS, db_type_desc, dbsetters,
|
DB_OPTIONS, db_type_desc, dbsetters,
|
||||||
verbose: bool = (false, parse_bool, [UNTRACKED],
|
verbose: bool = (false, parse_bool, [UNTRACKED],
|
||||||
"in general, enable more debug printouts"),
|
"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],
|
time_passes: bool = (false, parse_bool, [UNTRACKED],
|
||||||
"measure time of each rustc pass"),
|
"measure time of each rustc pass"),
|
||||||
count_llvm_insns: bool = (false, parse_bool,
|
count_llvm_insns: bool = (false, parse_bool,
|
||||||
|
|
|
@ -8,8 +8,10 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
|
use hir::BodyId;
|
||||||
use hir::def_id::DefId;
|
use hir::def_id::DefId;
|
||||||
use hir::map::definitions::DefPathData;
|
use hir::map::definitions::DefPathData;
|
||||||
|
use middle::region::{CodeExtent, BlockRemainder};
|
||||||
use ty::subst::{self, Subst};
|
use ty::subst::{self, Subst};
|
||||||
use ty::{BrAnon, BrEnv, BrFresh, BrNamed};
|
use ty::{BrAnon, BrEnv, BrFresh, BrNamed};
|
||||||
use ty::{TyBool, TyChar, TyAdt};
|
use ty::{TyBool, TyChar, TyAdt};
|
||||||
|
@ -32,6 +34,10 @@ pub fn verbose() -> bool {
|
||||||
ty::tls::with(|tcx| tcx.sess.verbose())
|
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,
|
fn fn_sig(f: &mut fmt::Formatter,
|
||||||
inputs: &[Ty],
|
inputs: &[Ty],
|
||||||
variadic: bool,
|
variadic: bool,
|
||||||
|
@ -519,6 +525,23 @@ impl fmt::Display for ty::RegionKind {
|
||||||
ty::ReSkolemized(_, br) => {
|
ty::ReSkolemized(_, br) => {
|
||||||
write!(f, "{}", 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::ReScope(_) |
|
||||||
ty::ReVar(_) |
|
ty::ReVar(_) |
|
||||||
ty::ReErased => Ok(()),
|
ty::ReErased => Ok(()),
|
||||||
|
@ -789,7 +812,11 @@ impl<'tcx> fmt::Display for ty::TypeVariants<'tcx> {
|
||||||
write!(f, "[closure")?;
|
write!(f, "[closure")?;
|
||||||
|
|
||||||
if let Some(node_id) = tcx.hir.as_local_node_id(did) {
|
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 = " ";
|
let mut sep = " ";
|
||||||
tcx.with_freevars(node_id, |freevars| {
|
tcx.with_freevars(node_id, |freevars| {
|
||||||
for (freevar, upvar_ty) in freevars.iter().zip(upvar_tys) {
|
for (freevar, upvar_ty) in freevars.iter().zip(upvar_tys) {
|
||||||
|
|
|
@ -474,6 +474,7 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
|
||||||
mir::StatementKind::StorageLive(_) |
|
mir::StatementKind::StorageLive(_) |
|
||||||
mir::StatementKind::StorageDead(_) |
|
mir::StatementKind::StorageDead(_) |
|
||||||
mir::StatementKind::InlineAsm { .. } |
|
mir::StatementKind::InlineAsm { .. } |
|
||||||
|
mir::StatementKind::EndRegion(_) |
|
||||||
mir::StatementKind::Nop => {}
|
mir::StatementKind::Nop => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,6 +105,7 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
mir::StatementKind::StorageLive(_) |
|
mir::StatementKind::StorageLive(_) |
|
||||||
mir::StatementKind::StorageDead(_) |
|
mir::StatementKind::StorageDead(_) |
|
||||||
mir::StatementKind::InlineAsm { .. } |
|
mir::StatementKind::InlineAsm { .. } |
|
||||||
|
mir::StatementKind::EndRegion(_) |
|
||||||
mir::StatementKind::Nop => continue,
|
mir::StatementKind::Nop => continue,
|
||||||
mir::StatementKind::SetDiscriminant{ .. } =>
|
mir::StatementKind::SetDiscriminant{ .. } =>
|
||||||
span_bug!(stmt.source_info.span,
|
span_bug!(stmt.source_info.span,
|
||||||
|
|
|
@ -594,6 +594,11 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
|
||||||
assert!(self.patch.is_patched(bb));
|
assert!(self.patch.is_patched(bb));
|
||||||
allow_initializations = false;
|
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));
|
assert!(!self.patch.is_patched(bb));
|
||||||
}
|
}
|
||||||
|
|
|
@ -413,6 +413,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
|
||||||
"SetDiscriminant should not exist during borrowck");
|
"SetDiscriminant should not exist during borrowck");
|
||||||
}
|
}
|
||||||
StatementKind::InlineAsm { .. } |
|
StatementKind::InlineAsm { .. } |
|
||||||
|
StatementKind::EndRegion(_) |
|
||||||
StatementKind::Nop => {}
|
StatementKind::Nop => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -394,6 +394,7 @@ fn drop_flag_effects_for_location<'a, 'tcx, F>(
|
||||||
mir::StatementKind::StorageLive(_) |
|
mir::StatementKind::StorageLive(_) |
|
||||||
mir::StatementKind::StorageDead(_) |
|
mir::StatementKind::StorageDead(_) |
|
||||||
mir::StatementKind::InlineAsm { .. } |
|
mir::StatementKind::InlineAsm { .. } |
|
||||||
|
mir::StatementKind::EndRegion(_) |
|
||||||
mir::StatementKind::Nop => {}
|
mir::StatementKind::Nop => {}
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
|
|
|
@ -914,6 +914,9 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
|
||||||
let mut passes = Passes::new();
|
let mut passes = Passes::new();
|
||||||
passes.push_hook(mir::transform::dump_mir::DumpMir);
|
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.
|
// 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::simplify::SimplifyCfg::new("initial"));
|
||||||
passes.push_pass(MIR_CONST, mir::transform::type_check::TypeckMir);
|
passes.push_pass(MIR_CONST, mir::transform::type_check::TypeckMir);
|
||||||
|
|
|
@ -21,21 +21,24 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
ast_block: &'tcx hir::Block,
|
ast_block: &'tcx hir::Block,
|
||||||
source_info: SourceInfo)
|
source_info: SourceInfo)
|
||||||
-> BlockAnd<()> {
|
-> BlockAnd<()> {
|
||||||
let Block { extent, span, stmts, expr, targeted_by_break } = self.hir.mirror(ast_block);
|
let Block { extent, opt_destruction_extent, span, stmts, expr, targeted_by_break } =
|
||||||
self.in_scope(extent, block, move |this| {
|
self.hir.mirror(ast_block);
|
||||||
if targeted_by_break {
|
self.in_opt_scope(opt_destruction_extent.map(|de|(de, source_info)), block, move |this| {
|
||||||
// This is a `break`-able block (currently only `catch { ... }`)
|
this.in_scope((extent, source_info), block, move |this| {
|
||||||
let exit_block = this.cfg.start_new_block();
|
if targeted_by_break {
|
||||||
let block_exit = this.in_breakable_scope(None, exit_block,
|
// This is a `break`-able block (currently only `catch { ... }`)
|
||||||
destination.clone(), |this| {
|
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.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.
|
// First we build all the statements in the block.
|
||||||
let mut let_extent_stack = Vec::with_capacity(8);
|
let mut let_extent_stack = Vec::with_capacity(8);
|
||||||
let outer_visibility_scope = this.visibility_scope;
|
let outer_visibility_scope = this.visibility_scope;
|
||||||
|
let source_info = this.source_info(span);
|
||||||
for stmt in stmts {
|
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 {
|
match kind {
|
||||||
StmtKind::Expr { scope, expr } => {
|
StmtKind::Expr { scope, expr } => {
|
||||||
unpack!(block = this.in_scope(scope, block, |this| {
|
unpack!(block = this.in_opt_scope(
|
||||||
let expr = this.hir.mirror(expr);
|
opt_destruction_extent.map(|de|(de, source_info)), block, |this| {
|
||||||
this.stmt_expr(block, expr)
|
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 } => {
|
StmtKind::Let { remainder_scope, init_scope, pattern, initializer } => {
|
||||||
let tcx = this.hir.tcx();
|
let tcx = this.hir.tcx();
|
||||||
|
@ -89,10 +96,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
|
|
||||||
// Evaluate the initializer, if present.
|
// Evaluate the initializer, if present.
|
||||||
if let Some(init) = initializer {
|
if let Some(init) = initializer {
|
||||||
unpack!(block = this.in_scope(init_scope, block, move |this| {
|
unpack!(block = this.in_opt_scope(
|
||||||
// FIXME #30046 ^~~~
|
opt_destruction_extent.map(|de|(de, source_info)), block, move |this| {
|
||||||
this.expr_into_pattern(block, pattern, init)
|
this.in_scope((init_scope, source_info), block, move |this| {
|
||||||
}));
|
// FIXME #30046 ^~~~
|
||||||
|
this.expr_into_pattern(block, pattern, init)
|
||||||
|
})
|
||||||
|
}));
|
||||||
} else {
|
} else {
|
||||||
this.visit_bindings(&pattern, &mut |this, _, _, node, span, _| {
|
this.visit_bindings(&pattern, &mut |this, _, _, node, span, _| {
|
||||||
this.storage_live_binding(block, 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 {
|
if let Some(expr) = expr {
|
||||||
unpack!(block = this.into(destination, block, expr));
|
unpack!(block = this.into(destination, block, expr));
|
||||||
} else {
|
} else {
|
||||||
let source_info = this.source_info(span);
|
|
||||||
this.cfg.push_assign_unit(block, source_info, destination);
|
this.cfg.push_assign_unit(block, source_info, destination);
|
||||||
}
|
}
|
||||||
// Finally, we pop all the let scopes before exiting out from the scope of block
|
// Finally, we pop all the let scopes before exiting out from the scope of block
|
||||||
// itself.
|
// itself.
|
||||||
for extent in let_extent_stack.into_iter().rev() {
|
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.
|
// Restore the original visibility scope.
|
||||||
this.visibility_scope = outer_visibility_scope;
|
this.visibility_scope = outer_visibility_scope;
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
//! Routines for manipulating the control-flow graph.
|
//! Routines for manipulating the control-flow graph.
|
||||||
|
|
||||||
use build::CFG;
|
use build::CFG;
|
||||||
|
use rustc::middle::region::CodeExtent;
|
||||||
use rustc::mir::*;
|
use rustc::mir::*;
|
||||||
|
|
||||||
impl<'tcx> CFG<'tcx> {
|
impl<'tcx> CFG<'tcx> {
|
||||||
|
@ -43,6 +44,16 @@ impl<'tcx> CFG<'tcx> {
|
||||||
self.block_data_mut(block).statements.push(statement);
|
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,
|
pub fn push_assign(&mut self,
|
||||||
block: BasicBlock,
|
block: BasicBlock,
|
||||||
source_info: SourceInfo,
|
source_info: SourceInfo,
|
||||||
|
|
|
@ -40,7 +40,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
let source_info = this.source_info(expr_span);
|
let source_info = this.source_info(expr_span);
|
||||||
match expr.kind {
|
match expr.kind {
|
||||||
ExprKind::Scope { extent, value } => {
|
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 } => {
|
ExprKind::Field { lhs, name } => {
|
||||||
let lvalue = unpack!(block = this.as_lvalue(block, lhs));
|
let lvalue = unpack!(block = this.as_lvalue(block, lhs));
|
||||||
|
|
|
@ -56,6 +56,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
let this = self;
|
let this = self;
|
||||||
|
|
||||||
if let ExprKind::Scope { extent, value } = expr.kind {
|
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| {
|
return this.in_scope(extent, block, |this| {
|
||||||
this.as_operand(block, scope, value)
|
this.as_operand(block, scope, value)
|
||||||
});
|
});
|
||||||
|
|
|
@ -59,6 +59,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
|
|
||||||
match expr.kind {
|
match expr.kind {
|
||||||
ExprKind::Scope { extent, value } => {
|
ExprKind::Scope { extent, value } => {
|
||||||
|
let extent = (extent, source_info);
|
||||||
this.in_scope(extent, block, |this| this.as_rvalue(block, scope, value))
|
this.in_scope(extent, block, |this| this.as_rvalue(block, scope, value))
|
||||||
}
|
}
|
||||||
ExprKind::Repeat { value, count } => {
|
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):
|
// to start, malloc some memory of suitable type (thus far, uninitialized):
|
||||||
let box_ = Rvalue::NullaryOp(NullOp::Box, value.ty);
|
let box_ = Rvalue::NullaryOp(NullOp::Box, value.ty);
|
||||||
this.cfg.push_assign(block, source_info, &result, box_);
|
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:
|
// schedule a shallow free of that memory, lest we unwind:
|
||||||
this.schedule_box_free(expr_span, value_extents, &result, value.ty);
|
this.schedule_box_free(expr_span, value_extents, &result, value.ty);
|
||||||
// initialize the box contents:
|
// initialize the box contents:
|
||||||
|
|
|
@ -39,16 +39,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
block, temp_lifetime, expr);
|
block, temp_lifetime, expr);
|
||||||
let this = self;
|
let this = self;
|
||||||
|
|
||||||
|
let expr_span = expr.span;
|
||||||
|
let source_info = this.source_info(expr_span);
|
||||||
if let ExprKind::Scope { extent, value } = expr.kind {
|
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)
|
this.as_temp(block, temp_lifetime, value)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let expr_ty = expr.ty.clone();
|
let expr_ty = expr.ty.clone();
|
||||||
let expr_span = expr.span;
|
|
||||||
let temp = this.temp(expr_ty.clone(), 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() {
|
if !expr_ty.is_never() && temp_lifetime.is_some() {
|
||||||
this.cfg.push(block, Statement {
|
this.cfg.push(block, Statement {
|
||||||
|
|
|
@ -39,6 +39,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
|
|
||||||
match expr.kind {
|
match expr.kind {
|
||||||
ExprKind::Scope { extent, value } => {
|
ExprKind::Scope { extent, value } => {
|
||||||
|
let extent = (extent, source_info);
|
||||||
this.in_scope(extent, block, |this| this.into(destination, block, value))
|
this.in_scope(extent, block, |this| this.into(destination, block, value))
|
||||||
}
|
}
|
||||||
ExprKind::Block { body: ast_block } => {
|
ExprKind::Block { body: ast_block } => {
|
||||||
|
@ -233,7 +234,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let success = this.cfg.start_new_block();
|
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 {
|
this.cfg.terminate(block, source_info, TerminatorKind::Call {
|
||||||
func: fun,
|
func: fun,
|
||||||
args: args,
|
args: args,
|
||||||
|
|
|
@ -24,7 +24,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
match expr.kind {
|
match expr.kind {
|
||||||
ExprKind::Scope { extent, value } => {
|
ExprKind::Scope { extent, value } => {
|
||||||
let value = this.hir.mirror(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 } => {
|
ExprKind::Assign { lhs, rhs } => {
|
||||||
let lhs = this.hir.mirror(lhs);
|
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);
|
*this.find_breakable_scope(expr_span, label);
|
||||||
let continue_block = continue_block.expect(
|
let continue_block = continue_block.expect(
|
||||||
"Attempted to continue in non-continuable breakable block");
|
"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()
|
this.cfg.start_new_block().unit()
|
||||||
}
|
}
|
||||||
ExprKind::Break { label, value } => {
|
ExprKind::Break { label, value } => {
|
||||||
|
@ -99,7 +99,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
} else {
|
} else {
|
||||||
this.cfg.push_assign_unit(block, source_info, &destination)
|
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()
|
this.cfg.start_new_block().unit()
|
||||||
}
|
}
|
||||||
ExprKind::Return { value } => {
|
ExprKind::Return { value } => {
|
||||||
|
@ -116,7 +116,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
};
|
};
|
||||||
let extent = this.extent_of_return_scope();
|
let extent = this.extent_of_return_scope();
|
||||||
let return_block = this.return_block();
|
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()
|
this.cfg.start_new_block().unit()
|
||||||
}
|
}
|
||||||
ExprKind::InlineAsm { asm, outputs, inputs } => {
|
ExprKind::InlineAsm { asm, outputs, inputs } => {
|
||||||
|
|
|
@ -306,7 +306,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
let bool_ty = self.hir.bool_ty();
|
let bool_ty = self.hir.bool_ty();
|
||||||
let eq_result = self.temp(bool_ty, test.span);
|
let eq_result = self.temp(bool_ty, test.span);
|
||||||
let eq_block = self.cfg.start_new_block();
|
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 {
|
self.cfg.terminate(block, source_info, TerminatorKind::Call {
|
||||||
func: Operand::Constant(box Constant {
|
func: Operand::Constant(box Constant {
|
||||||
span: test.span,
|
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 call_site_extent = CodeExtent::CallSiteScope(body.id());
|
||||||
let arg_extent = CodeExtent::ParameterScope(body.id());
|
let arg_extent = CodeExtent::ParameterScope(body.id());
|
||||||
let mut block = START_BLOCK;
|
let mut block = START_BLOCK;
|
||||||
unpack!(block = builder.in_scope(call_site_extent, block, |builder| {
|
let source_info = builder.source_info(span);
|
||||||
unpack!(block = builder.in_scope(arg_extent, block, |builder| {
|
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)
|
builder.args_and_body(block, &arguments, arg_extent, &body.value)
|
||||||
}));
|
}));
|
||||||
// Attribute epilogue to function's closing brace
|
// Attribute epilogue to function's closing brace
|
||||||
|
|
|
@ -94,10 +94,11 @@ use rustc::ty::subst::{Kind, Subst};
|
||||||
use rustc::ty::{Ty, TyCtxt};
|
use rustc::ty::{Ty, TyCtxt};
|
||||||
use rustc::mir::*;
|
use rustc::mir::*;
|
||||||
use rustc::mir::transform::MirSource;
|
use rustc::mir::transform::MirSource;
|
||||||
use syntax_pos::Span;
|
use syntax_pos::{Span};
|
||||||
use rustc_data_structures::indexed_vec::Idx;
|
use rustc_data_structures::indexed_vec::Idx;
|
||||||
use rustc_data_structures::fx::FxHashMap;
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Scope<'tcx> {
|
pub struct Scope<'tcx> {
|
||||||
/// The visibility scope this scope was created in.
|
/// The visibility scope this scope was created in.
|
||||||
visibility_scope: VisibilityScope,
|
visibility_scope: VisibilityScope,
|
||||||
|
@ -114,7 +115,7 @@ pub struct Scope<'tcx> {
|
||||||
/// * pollutting the cleanup MIR with StorageDead creates
|
/// * pollutting the cleanup MIR with StorageDead creates
|
||||||
/// landing pads even though there's no actual destructors
|
/// landing pads even though there's no actual destructors
|
||||||
/// * freeing up stack space has no effect during unwinding
|
/// * 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
|
/// set of lvalues to drop when exiting this scope. This starts
|
||||||
/// out empty but grows as variables are declared during the
|
/// out empty but grows as variables are declared during the
|
||||||
|
@ -141,6 +142,7 @@ pub struct Scope<'tcx> {
|
||||||
cached_exits: FxHashMap<(BasicBlock, CodeExtent), BasicBlock>,
|
cached_exits: FxHashMap<(BasicBlock, CodeExtent), BasicBlock>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct DropData<'tcx> {
|
struct DropData<'tcx> {
|
||||||
/// span where drop obligation was incurred (typically where lvalue was declared)
|
/// span where drop obligation was incurred (typically where lvalue was declared)
|
||||||
span: Span,
|
span: Span,
|
||||||
|
@ -152,6 +154,7 @@ struct DropData<'tcx> {
|
||||||
kind: DropKind
|
kind: DropKind
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
enum DropKind {
|
enum DropKind {
|
||||||
Value {
|
Value {
|
||||||
/// The cached block for the cleanups-on-diverge path. This block
|
/// The cached block for the cleanups-on-diverge path. This block
|
||||||
|
@ -163,6 +166,7 @@ enum DropKind {
|
||||||
Storage
|
Storage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct FreeData<'tcx> {
|
struct FreeData<'tcx> {
|
||||||
/// span where free obligation was incurred
|
/// span where free obligation was incurred
|
||||||
span: Span,
|
span: Span,
|
||||||
|
@ -269,17 +273,34 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
res
|
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`
|
/// Convenience wrapper that pushes a scope and then executes `f`
|
||||||
/// to build its contents, popping the scope afterwards.
|
/// to build its contents, popping the scope afterwards.
|
||||||
pub fn in_scope<F, R>(&mut self,
|
pub fn in_scope<F, R>(&mut self,
|
||||||
extent: CodeExtent,
|
extent: (CodeExtent, SourceInfo),
|
||||||
mut block: BasicBlock,
|
mut block: BasicBlock,
|
||||||
f: F)
|
f: F)
|
||||||
-> BlockAnd<R>
|
-> BlockAnd<R>
|
||||||
where F: FnOnce(&mut Builder<'a, 'gcx, 'tcx>) -> BlockAnd<R>
|
where F: FnOnce(&mut Builder<'a, 'gcx, 'tcx>) -> BlockAnd<R>
|
||||||
{
|
{
|
||||||
debug!("in_scope(extent={:?}, block={:?})", extent, block);
|
debug!("in_scope(extent={:?}, block={:?})", extent, block);
|
||||||
self.push_scope(extent);
|
self.push_scope(extent.0);
|
||||||
let rv = unpack!(block = f(self));
|
let rv = unpack!(block = f(self));
|
||||||
unpack!(block = self.pop_scope(extent, block));
|
unpack!(block = self.pop_scope(extent, block));
|
||||||
debug!("in_scope: exiting extent={:?} block={:?}", 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
|
/// drops onto the end of `block` that are needed. This must
|
||||||
/// match 1-to-1 with `push_scope`.
|
/// match 1-to-1 with `push_scope`.
|
||||||
pub fn pop_scope(&mut self,
|
pub fn pop_scope(&mut self,
|
||||||
extent: CodeExtent,
|
extent: (CodeExtent, SourceInfo),
|
||||||
mut block: BasicBlock)
|
mut block: BasicBlock)
|
||||||
-> BlockAnd<()> {
|
-> BlockAnd<()> {
|
||||||
debug!("pop_scope({:?}, {:?})", extent, block);
|
debug!("pop_scope({:?}, {:?})", extent, block);
|
||||||
// We need to have `cached_block`s available for all the drops, so we call diverge_cleanup
|
// 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.
|
// 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();
|
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,
|
unpack!(block = build_scope_drops(&mut self.cfg,
|
||||||
&scope,
|
&scope,
|
||||||
&self.scopes,
|
&self.scopes,
|
||||||
block,
|
block,
|
||||||
self.arg_count));
|
self.arg_count));
|
||||||
|
|
||||||
|
self.cfg.push_end_region(block, extent.1, scope.extent);
|
||||||
block.unit()
|
block.unit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,11 +354,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
/// module comment for details.
|
/// module comment for details.
|
||||||
pub fn exit_scope(&mut self,
|
pub fn exit_scope(&mut self,
|
||||||
span: Span,
|
span: Span,
|
||||||
extent: CodeExtent,
|
extent: (CodeExtent, SourceInfo),
|
||||||
mut block: BasicBlock,
|
mut block: BasicBlock,
|
||||||
target: BasicBlock) {
|
target: BasicBlock) {
|
||||||
debug!("exit_scope(extent={:?}, block={:?}, target={:?})", extent, block, target);
|
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(||{
|
.unwrap_or_else(||{
|
||||||
span_bug!(span, "extent {:?} does not enclose", extent)
|
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)..];
|
let mut rest = &mut self.scopes[(len - scope_count)..];
|
||||||
while let Some((scope, rest_)) = {rest}.split_last_mut() {
|
while let Some((scope, rest_)) = {rest}.split_last_mut() {
|
||||||
rest = rest_;
|
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),
|
self.cfg.terminate(block, scope.source_info(span),
|
||||||
TerminatorKind::Goto { target: e });
|
TerminatorKind::Goto { target: e });
|
||||||
return;
|
return;
|
||||||
|
@ -354,7 +377,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
let b = self.cfg.start_new_block();
|
let b = self.cfg.start_new_block();
|
||||||
self.cfg.terminate(block, scope.source_info(span),
|
self.cfg.terminate(block, scope.source_info(span),
|
||||||
TerminatorKind::Goto { target: b });
|
TerminatorKind::Goto { target: b });
|
||||||
scope.cached_exits.insert((target, extent), b);
|
scope.cached_exits.insert((target, extent.0), b);
|
||||||
b
|
b
|
||||||
};
|
};
|
||||||
unpack!(block = build_scope_drops(&mut self.cfg,
|
unpack!(block = build_scope_drops(&mut self.cfg,
|
||||||
|
@ -362,6 +385,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
rest,
|
rest,
|
||||||
block,
|
block,
|
||||||
self.arg_count));
|
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 {
|
if let Some(ref free_data) = scope.free {
|
||||||
let next = self.cfg.start_new_block();
|
let next = self.cfg.start_new_block();
|
||||||
let free = build_free(self.hir.tcx(), &tmp, free_data, next);
|
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.
|
/// This path terminates in Resume. Returns the start of the path.
|
||||||
/// See module comment for more details. None indicates there’s no
|
/// See module comment for more details. None indicates there’s no
|
||||||
/// cleanup to do at this point.
|
/// 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) {
|
if !self.scopes.iter().any(|scope| scope.needs_cleanup) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -623,8 +650,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
resumeblk
|
resumeblk
|
||||||
};
|
};
|
||||||
|
|
||||||
for scope in scopes.iter_mut().filter(|s| s.needs_cleanup) {
|
for scope in scopes.iter_mut() {
|
||||||
target = build_diverge_scope(hir.tcx(), cfg, &unit_temp, scope, target);
|
target = build_diverge_scope(hir.tcx(), cfg, &unit_temp, span, scope, target);
|
||||||
}
|
}
|
||||||
Some(target)
|
Some(target)
|
||||||
}
|
}
|
||||||
|
@ -640,7 +667,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
}
|
}
|
||||||
let source_info = self.source_info(span);
|
let source_info = self.source_info(span);
|
||||||
let next_target = self.cfg.start_new_block();
|
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,
|
self.cfg.terminate(block, source_info,
|
||||||
TerminatorKind::Drop {
|
TerminatorKind::Drop {
|
||||||
location: location,
|
location: location,
|
||||||
|
@ -658,7 +685,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
value: Operand<'tcx>) -> BlockAnd<()> {
|
value: Operand<'tcx>) -> BlockAnd<()> {
|
||||||
let source_info = self.source_info(span);
|
let source_info = self.source_info(span);
|
||||||
let next_target = self.cfg.start_new_block();
|
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,
|
self.cfg.terminate(block, source_info,
|
||||||
TerminatorKind::DropAndReplace {
|
TerminatorKind::DropAndReplace {
|
||||||
location: location,
|
location: location,
|
||||||
|
@ -681,7 +708,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
let source_info = self.source_info(span);
|
let source_info = self.source_info(span);
|
||||||
|
|
||||||
let success_block = self.cfg.start_new_block();
|
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,
|
self.cfg.terminate(block, source_info,
|
||||||
TerminatorKind::Assert {
|
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>,
|
fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||||
cfg: &mut CFG<'tcx>,
|
cfg: &mut CFG<'tcx>,
|
||||||
unit_temp: &Lvalue<'tcx>,
|
unit_temp: &Lvalue<'tcx>,
|
||||||
|
span: Span,
|
||||||
scope: &mut Scope<'tcx>,
|
scope: &mut Scope<'tcx>,
|
||||||
mut target: BasicBlock)
|
mut target: BasicBlock)
|
||||||
-> 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
|
// Build up the drops in **reverse** order. The end result will
|
||||||
// look like:
|
// look like:
|
||||||
//
|
//
|
||||||
// [drops[n]] -...-> [drops[0]] -> [Free] -> [target]
|
// [EndRegion Block] -> [drops[n]] -...-> [drops[0]] -> [Free] -> [target]
|
||||||
// | |
|
// | |
|
||||||
// +------------------------------------+
|
// +---------------------------------------------------------+
|
||||||
// code for scope
|
// code for scope
|
||||||
//
|
//
|
||||||
// The code in this function reads from right to left. At each
|
// 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
|
// Next, build up the drops. Here we iterate the vector in
|
||||||
// *forward* order, so that we generate drops[0] first (right to
|
// *forward* order, so that we generate drops[0] first (right to
|
||||||
// left in diagram above).
|
// 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,
|
// Only full value drops are emitted in the diverging path,
|
||||||
// not StorageDead.
|
// 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 {
|
let cached_block = match drop_data.kind {
|
||||||
DropKind::Value { ref mut cached_block } => cached_block,
|
DropKind::Value { ref mut cached_block } => cached_block,
|
||||||
DropKind::Storage => continue
|
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
|
target
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,9 +22,14 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Block {
|
||||||
// We have to eagerly translate the "spine" of the statements
|
// We have to eagerly translate the "spine" of the statements
|
||||||
// in order to get the lexical scoping correctly.
|
// in order to get the lexical scoping correctly.
|
||||||
let stmts = mirror_stmts(cx, self.id, &*self.stmts);
|
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 {
|
Block {
|
||||||
targeted_by_break: self.targeted_by_break,
|
targeted_by_break: self.targeted_by_break,
|
||||||
extent: CodeExtent::Misc(self.id),
|
extent: CodeExtent::Misc(self.id),
|
||||||
|
opt_destruction_extent: opt_destruction_extent,
|
||||||
span: self.span,
|
span: self.span,
|
||||||
stmts: stmts,
|
stmts: stmts,
|
||||||
expr: self.expr.to_ref(),
|
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])
|
stmts: &'tcx [hir::Stmt])
|
||||||
-> Vec<StmtRef<'tcx>> {
|
-> Vec<StmtRef<'tcx>> {
|
||||||
let mut result = vec![];
|
let mut result = vec![];
|
||||||
|
let opt_def_id = cx.tcx.hir.opt_local_def_id(block_id);
|
||||||
for (index, stmt) in stmts.iter().enumerate() {
|
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 {
|
match stmt.node {
|
||||||
hir::StmtExpr(ref expr, id) |
|
hir::StmtExpr(ref expr, id) |
|
||||||
hir::StmtSemi(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),
|
scope: CodeExtent::Misc(id),
|
||||||
expr: expr.to_ref(),
|
expr: expr.to_ref(),
|
||||||
},
|
},
|
||||||
|
opt_destruction_extent: opt_dxn_ext,
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
hir::StmtDecl(ref decl, id) => {
|
hir::StmtDecl(ref decl, id) => {
|
||||||
|
@ -69,6 +79,7 @@ fn mirror_stmts<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
|
||||||
pattern: pattern,
|
pattern: pattern,
|
||||||
initializer: local.init.to_ref(),
|
initializer: local.init.to_ref(),
|
||||||
},
|
},
|
||||||
|
opt_destruction_extent: opt_dxn_ext,
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ pub use rustc_const_eval::pattern::{BindingMode, Pattern, PatternKind, FieldPatt
|
||||||
pub struct Block<'tcx> {
|
pub struct Block<'tcx> {
|
||||||
pub targeted_by_break: bool,
|
pub targeted_by_break: bool,
|
||||||
pub extent: CodeExtent,
|
pub extent: CodeExtent,
|
||||||
|
pub opt_destruction_extent: Option<CodeExtent>,
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
pub stmts: Vec<StmtRef<'tcx>>,
|
pub stmts: Vec<StmtRef<'tcx>>,
|
||||||
pub expr: Option<ExprRef<'tcx>>,
|
pub expr: Option<ExprRef<'tcx>>,
|
||||||
|
@ -47,6 +48,7 @@ pub enum StmtRef<'tcx> {
|
||||||
pub struct Stmt<'tcx> {
|
pub struct Stmt<'tcx> {
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
pub kind: StmtKind<'tcx>,
|
pub kind: StmtKind<'tcx>,
|
||||||
|
pub opt_destruction_extent: Option<CodeExtent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
|
@ -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 @@ impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> {
|
||||||
substs: &mut ClosureSubsts<'tcx>) {
|
substs: &mut ClosureSubsts<'tcx>) {
|
||||||
*substs = self.tcx.erase_regions(substs);
|
*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;
|
pub struct EraseRegions;
|
||||||
|
|
|
@ -24,6 +24,7 @@ use syntax::ast;
|
||||||
use syntax_pos::{DUMMY_SP, Span};
|
use syntax_pos::{DUMMY_SP, Span};
|
||||||
use transform;
|
use transform;
|
||||||
|
|
||||||
|
pub mod clean_end_regions;
|
||||||
pub mod simplify_branches;
|
pub mod simplify_branches;
|
||||||
pub mod simplify;
|
pub mod simplify;
|
||||||
pub mod erase_regions;
|
pub mod erase_regions;
|
||||||
|
|
|
@ -894,6 +894,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
|
||||||
StatementKind::StorageLive(_) |
|
StatementKind::StorageLive(_) |
|
||||||
StatementKind::StorageDead(_) |
|
StatementKind::StorageDead(_) |
|
||||||
StatementKind::InlineAsm {..} |
|
StatementKind::InlineAsm {..} |
|
||||||
|
StatementKind::EndRegion(_) |
|
||||||
StatementKind::Nop => {}
|
StatementKind::Nop => {}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -413,6 +413,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StatementKind::InlineAsm { .. } |
|
StatementKind::InlineAsm { .. } |
|
||||||
|
StatementKind::EndRegion(_) |
|
||||||
StatementKind::Nop => {}
|
StatementKind::Nop => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@ impl<'tcx> MirPatch<'tcx> {
|
||||||
for (bb, block) in mir.basic_blocks().iter_enumerated() {
|
for (bb, block) in mir.basic_blocks().iter_enumerated() {
|
||||||
if let TerminatorKind::Resume = block.terminator().kind {
|
if let TerminatorKind::Resume = block.terminator().kind {
|
||||||
if block.statements.len() > 0 {
|
if block.statements.len() > 0 {
|
||||||
|
assert!(resume_stmt_block.is_none());
|
||||||
resume_stmt_block = Some(bb);
|
resume_stmt_block = Some(bb);
|
||||||
} else {
|
} else {
|
||||||
resume_block = Some(bb);
|
resume_block = Some(bb);
|
||||||
|
|
|
@ -125,6 +125,7 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> {
|
||||||
self.record("Statement", statement);
|
self.record("Statement", statement);
|
||||||
self.record(match statement.kind {
|
self.record(match statement.kind {
|
||||||
StatementKind::Assign(..) => "StatementKind::Assign",
|
StatementKind::Assign(..) => "StatementKind::Assign",
|
||||||
|
StatementKind::EndRegion(..) => "StatementKind::EndRegion",
|
||||||
StatementKind::SetDiscriminant { .. } => "StatementKind::SetDiscriminant",
|
StatementKind::SetDiscriminant { .. } => "StatementKind::SetDiscriminant",
|
||||||
StatementKind::StorageLive(..) => "StatementKind::StorageLive",
|
StatementKind::StorageLive(..) => "StatementKind::StorageLive",
|
||||||
StatementKind::StorageDead(..) => "StatementKind::StorageDead",
|
StatementKind::StorageDead(..) => "StatementKind::StorageDead",
|
||||||
|
|
|
@ -284,6 +284,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
mir::StatementKind::StorageLive(_) |
|
mir::StatementKind::StorageLive(_) |
|
||||||
mir::StatementKind::StorageDead(_) |
|
mir::StatementKind::StorageDead(_) |
|
||||||
|
mir::StatementKind::EndRegion(_) |
|
||||||
mir::StatementKind::Nop => {}
|
mir::StatementKind::Nop => {}
|
||||||
mir::StatementKind::InlineAsm { .. } |
|
mir::StatementKind::InlineAsm { .. } |
|
||||||
mir::StatementKind::SetDiscriminant{ .. } => {
|
mir::StatementKind::SetDiscriminant{ .. } => {
|
||||||
|
|
|
@ -86,6 +86,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
|
||||||
asm::trans_inline_asm(&bcx, asm, outputs, input_vals);
|
asm::trans_inline_asm(&bcx, asm, outputs, input_vals);
|
||||||
bcx
|
bcx
|
||||||
}
|
}
|
||||||
|
mir::StatementKind::EndRegion(_) |
|
||||||
mir::StatementKind::Nop => bcx,
|
mir::StatementKind::Nop => bcx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,9 +32,9 @@ pub fn droppy() {
|
||||||
// CHECK-NOT: invoke{{.*}}drop{{.*}}SomeUniqueName
|
// CHECK-NOT: invoke{{.*}}drop{{.*}}SomeUniqueName
|
||||||
// CHECK: call{{.*}}drop{{.*}}SomeUniqueName
|
// CHECK: call{{.*}}drop{{.*}}SomeUniqueName
|
||||||
// CHECK: call{{.*}}drop{{.*}}SomeUniqueName
|
// CHECK: call{{.*}}drop{{.*}}SomeUniqueName
|
||||||
// CHECK: call{{.*}}drop{{.*}}SomeUniqueName
|
|
||||||
// CHECK-NOT: call{{.*}}drop{{.*}}SomeUniqueName
|
// CHECK-NOT: call{{.*}}drop{{.*}}SomeUniqueName
|
||||||
// CHECK: invoke{{.*}}drop{{.*}}SomeUniqueName
|
// CHECK: invoke{{.*}}drop{{.*}}SomeUniqueName
|
||||||
|
// CHECK: call{{.*}}drop{{.*}}SomeUniqueName
|
||||||
// CHECK: invoke{{.*}}drop{{.*}}SomeUniqueName
|
// CHECK: invoke{{.*}}drop{{.*}}SomeUniqueName
|
||||||
// CHECK: call{{.*}}drop{{.*}}SomeUniqueName
|
// CHECK: call{{.*}}drop{{.*}}SomeUniqueName
|
||||||
// CHECK-NOT: {{(call|invoke).*}}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, ...,
|
For each $file_name, compiletest expects [$expected_line_0, ...,
|
||||||
$expected_line_N] to appear in the dumped MIR in order. Currently it allows
|
$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.
|
Lines match ignoring whitespace, and the prefix "//" is removed.
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ fn main() {
|
||||||
// StorageLive(_6);
|
// StorageLive(_6);
|
||||||
// StorageLive(_7);
|
// StorageLive(_7);
|
||||||
// _7 = _4;
|
// _7 = _4;
|
||||||
// replace(_6 <- _7) -> [return: bb5, unwind: bb4];
|
// replace(_6 <- _7) -> [return: bb6, unwind: bb7];
|
||||||
// }
|
// }
|
||||||
// bb1: {
|
// bb1: {
|
||||||
// resume;
|
// resume;
|
||||||
|
@ -59,24 +59,30 @@ fn main() {
|
||||||
// drop(_4) -> bb1;
|
// drop(_4) -> bb1;
|
||||||
// }
|
// }
|
||||||
// bb3: {
|
// bb3: {
|
||||||
// drop(_6) -> bb2;
|
// goto -> bb2;
|
||||||
// }
|
// }
|
||||||
// bb4: {
|
// bb4: {
|
||||||
// drop(_7) -> bb3;
|
// drop(_6) -> bb3;
|
||||||
// }
|
// }
|
||||||
// bb5: {
|
// bb5: {
|
||||||
// drop(_7) -> [return: bb6, unwind: bb3];
|
// goto -> bb4;
|
||||||
// }
|
// }
|
||||||
// bb6: {
|
// bb6: {
|
||||||
// StorageDead(_7);
|
// drop(_7) -> [return: bb8, unwind: bb4];
|
||||||
// _0 = ();
|
|
||||||
// drop(_6) -> [return: bb7, unwind: bb2];
|
|
||||||
// }
|
// }
|
||||||
// bb7: {
|
// bb7: {
|
||||||
// StorageDead(_6);
|
// drop(_7) -> bb5;
|
||||||
// drop(_4) -> bb8;
|
|
||||||
// }
|
// }
|
||||||
// bb8: {
|
// bb8: {
|
||||||
|
// StorageDead(_7);
|
||||||
|
// _0 = ();
|
||||||
|
// drop(_6) -> [return: bb9, unwind: bb2];
|
||||||
|
// }
|
||||||
|
// bb9: {
|
||||||
|
// StorageDead(_6);
|
||||||
|
// drop(_4) -> bb10;
|
||||||
|
// }
|
||||||
|
// bb10: {
|
||||||
// StorageDead(_4);
|
// StorageDead(_4);
|
||||||
// StorageDead(_2);
|
// StorageDead(_2);
|
||||||
// StorageDead(_1);
|
// StorageDead(_1);
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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 New Issue