From 464c02e336f15c6fedb7235e93ec6f8f69411b57 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 9 Mar 2016 11:04:26 -0500 Subject: [PATCH 01/24] integrate scopes into MIR --- src/librustc/mir/repr.rs | 56 +++++++++++++++++++++++- src/librustc_mir/build/block.rs | 2 +- src/librustc_mir/build/cfg.rs | 7 ++- src/librustc_mir/build/mod.rs | 76 +++++++++++++++++++++++++++++---- src/librustc_mir/build/scope.rs | 36 +++++++++++++--- src/librustc_mir/mir_map.rs | 12 ++++-- 6 files changed, 168 insertions(+), 21 deletions(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index ff3e292f45818..6723fc72a40eb 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -32,6 +32,10 @@ pub struct Mir<'tcx> { /// that indexes into this vector. pub basic_blocks: Vec>, + /// List of lexical scopes; these are referenced by statements and + /// used (eventually) for debuginfo. Indexed by a `ScopeId`. + pub scopes: ScopeDataVec, + /// Return type of the function. pub return_ty: FnOutput<'tcx>, @@ -613,13 +617,61 @@ impl<'tcx> Debug for Lvalue<'tcx> { } } +/////////////////////////////////////////////////////////////////////////// +// Scopes + +#[derive(Clone, Debug, RustcEncodable, RustcDecodable)] +pub struct ScopeDataVec { + pub vec: Vec +} + +impl ScopeDataVec { + pub fn new() -> Self { + ScopeDataVec { vec: Vec::new() } + } +} + +impl Index for ScopeDataVec { + type Output = ScopeData; + + #[inline] + fn index(&self, index: ScopeId) -> &ScopeData { + &self.vec[index.index()] + } +} + +impl IndexMut for ScopeDataVec { + #[inline] + fn index_mut(&mut self, index: ScopeId) -> &mut ScopeData { + &mut self.vec[index.index()] + } +} + +#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)] +pub struct ScopeId(u32); + +impl ScopeId { + pub fn new(index: usize) -> ScopeId { + assert!(index < (u32::MAX as usize)); + ScopeId(index as u32) + } + + pub fn index(self) -> usize { + self.0 as usize + } +} + +#[derive(Clone, Debug, RustcEncodable, RustcDecodable)] +pub struct ScopeData { + pub parent_scope: Option, +} + /////////////////////////////////////////////////////////////////////////// // Operands -// + /// These are values that can appear inside an rvalue (or an index /// lvalue). They are intentionally limited to prevent rvalues from /// being nested in one another. - #[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)] pub enum Operand<'tcx> { Consume(Lvalue<'tcx>), diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs index 4c80eab102fc5..ca23be7dc0618 100644 --- a/src/librustc_mir/build/block.rs +++ b/src/librustc_mir/build/block.rs @@ -51,7 +51,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { })); } StmtKind::Let { remainder_scope, init_scope, pattern, initializer } => { - this.push_scope(remainder_scope); + this.push_scope(remainder_scope, block); let_extent_stack.push(remainder_scope); unpack!(block = this.in_scope(init_scope, block, move |this| { // FIXME #30046 ^~~~ diff --git a/src/librustc_mir/build/cfg.rs b/src/librustc_mir/build/cfg.rs index c7147d111aace..d804fc8635a92 100644 --- a/src/librustc_mir/build/cfg.rs +++ b/src/librustc_mir/build/cfg.rs @@ -13,7 +13,7 @@ //! Routines for manipulating the control-flow graph. -use build::CFG; +use build::{CFG, Location}; use rustc::mir::repr::*; use syntax::codemap::Span; @@ -43,6 +43,11 @@ impl<'tcx> CFG<'tcx> { self.block_data_mut(block).statements.push(statement); } + pub fn current_location(&mut self, block: BasicBlock) -> Location { + let index = self.block_data(block).statements.len(); + Location { block: block, statement_index: index } + } + pub fn push_assign(&mut self, block: BasicBlock, span: Span, diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index b40775f939f75..41259f8a281de 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -21,8 +21,26 @@ use syntax::codemap::Span; pub struct Builder<'a, 'tcx: 'a> { hir: Cx<'a, 'tcx>, cfg: CFG<'tcx>, + + // the current set of scopes, updated as we traverse; + // see the `scope` module for more details scopes: Vec>, + + // for each scope, a span of blocks that defines it; + // we track these for use in region and borrow checking, + // but these are liable to get out of date once optimization + // begins. They are also hopefully temporary, and will be + // no longer needed when we adopt graph-based regions. + scope_auxiliary: Vec, + + // the current set of loops; see the `scope` module for more + // details loop_scopes: Vec, + + // the vector of all scopes that we have created thus far; + // we track this for debuginfo later + scope_data_vec: ScopeDataVec, + var_decls: Vec>, var_indices: FnvHashMap, temp_decls: Vec>, @@ -33,6 +51,42 @@ struct CFG<'tcx> { basic_blocks: Vec>, } +/// For each scope, we track the extent (from the HIR) and a +/// single-entry-multiple-exit subgraph that contains all the +/// statements/terminators within it. +/// +/// This information is separated out from the main `ScopeData` +/// because it is short-lived. First, the extent contains node-ids, +/// so it cannot be saved and re-loaded. Second, any optimization will mess up +/// the dominator/postdominator information. +/// +/// The intention is basically to use this information to do +/// regionck/borrowck and then throw it away once we are done. +pub struct ScopeAuxiliary { + /// extent of this scope from the MIR. + pub extent: CodeExtent, + + /// "entry point": dominator of all nodes in the scope + pub dom: Location, + + /// "exit points": mutual postdominators of all nodes in the scope + pub postdoms: Vec, +} + +pub struct Location { + /// the location is within this block + pub block: BasicBlock, + + /// the location is the start of the this statement; or, if `statement_index` + /// == num-statements, then the start of the terminator. + pub statement_index: usize, +} + +pub struct MirPlusPlus<'tcx> { + pub mir: Mir<'tcx>, + pub scope_auxiliary: Vec, +} + /////////////////////////////////////////////////////////////////////////// /// The `BlockAnd` "monad" packages up the new basic block along with a /// produced value (sometimes just unit, of course). The `unpack!` @@ -86,13 +140,15 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>, argument_extent: CodeExtent, return_ty: FnOutput<'tcx>, ast_block: &'tcx hir::Block) - -> Mir<'tcx> { + -> MirPlusPlus<'tcx> { let cfg = CFG { basic_blocks: vec![] }; let mut builder = Builder { hir: hir, cfg: cfg, scopes: vec![], + scope_data_vec: ScopeDataVec::new(), + scope_auxiliary: vec![], loop_scopes: vec![], temp_decls: vec![], var_decls: vec![], @@ -113,13 +169,17 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>, builder.cfg.terminate(block, Terminator::Goto { target: END_BLOCK }); builder.cfg.terminate(END_BLOCK, Terminator::Return); - Mir { - basic_blocks: builder.cfg.basic_blocks, - var_decls: builder.var_decls, - arg_decls: arg_decls, - temp_decls: builder.temp_decls, - return_ty: return_ty, - span: span + MirPlusPlus { + mir: Mir { + basic_blocks: builder.cfg.basic_blocks, + scopes: builder.scope_data_vec, + var_decls: builder.var_decls, + arg_decls: arg_decls, + temp_decls: builder.temp_decls, + return_ty: return_ty, + span: span + }, + scope_auxiliary: builder.scope_auxiliary, } } diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 6d411b9c07b31..6a734e1816a25 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -86,7 +86,7 @@ should go to. */ -use build::{BlockAnd, BlockAndExtension, Builder, CFG}; +use build::{BlockAnd, BlockAndExtension, Builder, CFG, ScopeAuxiliary}; use rustc::middle::region::CodeExtent; use rustc::middle::lang_items; use rustc::middle::subst::{Substs, Subst, VecPerParamSpace}; @@ -98,8 +98,11 @@ use rustc::middle::const_eval::ConstVal; use rustc_const_eval::ConstInt; pub struct Scope<'tcx> { + // the scope-id within the scope_data_vec + id: ScopeId, extent: CodeExtent, drops: Vec>, + // A scope may only have one associated free, because: // 1. We require a `free` to only be scheduled in the scope of `EXPR` in `box EXPR`; // 2. It only makes sense to have it translated into the diverge-path. @@ -208,7 +211,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { where F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd { debug!("in_scope(extent={:?}, block={:?})", extent, block); - self.push_scope(extent); + self.push_scope(extent, block); let rv = unpack!(block = f(self)); unpack!(block = self.pop_scope(extent, block)); debug!("in_scope: exiting extent={:?} block={:?}", extent, block); @@ -219,26 +222,44 @@ impl<'a,'tcx> Builder<'a,'tcx> { /// scope and call `pop_scope` afterwards. Note that these two /// calls must be paired; using `in_scope` as a convenience /// wrapper maybe preferable. - pub fn push_scope(&mut self, extent: CodeExtent) { + pub fn push_scope(&mut self, extent: CodeExtent, entry: BasicBlock) { debug!("push_scope({:?})", extent); + let parent_id = self.scopes.last().map(|s| s.id); + let id = ScopeId::new(self.scope_data_vec.vec.len()); + self.scope_data_vec.vec.push(ScopeData { + parent_scope: parent_id, + }); self.scopes.push(Scope { - extent: extent.clone(), + id: id, + extent: extent, drops: vec![], free: None }); + self.scope_auxiliary.push(ScopeAuxiliary { + extent: extent, + dom: self.cfg.current_location(entry), + postdoms: vec![] + }); } /// Pops a scope, which should have extent `extent`, adding any /// 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, block: BasicBlock) -> BlockAnd<()> { + pub fn pop_scope(&mut self, + extent: CodeExtent, + 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(); let scope = self.scopes.pop().unwrap(); assert_eq!(scope.extent, extent); - build_scope_drops(&mut self.cfg, &scope, &self.scopes[..], block) + unpack!(block = build_scope_drops(&mut self.cfg, &scope, &self.scopes, block)); + self.scope_auxiliary[scope.id.index()] + .postdoms + .push(self.cfg.current_location(block)); + block.and(()) } @@ -269,6 +290,9 @@ impl<'a,'tcx> Builder<'a,'tcx> { self.cfg.terminate(block, free); block = next; } + self.scope_auxiliary[scope.id.index()] + .postdoms + .push(self.cfg.current_location(block)); } self.cfg.terminate(block, Terminator::Goto { target: target }); } diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 05dbd63ef1a42..71037d1f08070 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -19,7 +19,7 @@ extern crate syntax; extern crate rustc_front; -use build; +use build::{self, MirPlusPlus}; use rustc::dep_graph::DepNode; use rustc::mir::repr::Mir; use hair::cx::Cx; @@ -182,8 +182,14 @@ fn build_mir<'a,'tcx:'a>(cx: Cx<'a,'tcx>, let parameter_scope = cx.tcx().region_maps.lookup_code_extent( CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body.id }); - let mut mir = build::construct(cx, span, implicit_arg_tys, arguments, - parameter_scope, fn_sig.output, body); + let MirPlusPlus { mut mir, scope_auxiliary: _ } = + build::construct(cx, + span, + implicit_arg_tys, + arguments, + parameter_scope, + fn_sig.output, + body); match cx.tcx().node_id_to_type(fn_id).sty { ty::TyFnDef(_, _, f) if f.abi == Abi::RustCall => { From 323d7f4e98c2e6039efb8c9afda405c156d21299 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 9 Mar 2016 12:36:07 -0500 Subject: [PATCH 02/24] record a scope for each `VarDecl` --- src/librustc/mir/repr.rs | 2 ++ src/librustc_mir/build/block.rs | 12 +++---- src/librustc_mir/build/expr/as_lvalue.rs | 2 +- src/librustc_mir/build/expr/as_operand.rs | 2 +- src/librustc_mir/build/expr/as_rvalue.rs | 4 +-- src/librustc_mir/build/expr/as_temp.rs | 2 +- src/librustc_mir/build/expr/into.rs | 2 +- src/librustc_mir/build/matches/mod.rs | 38 ++++++++++++----------- src/librustc_mir/build/mod.rs | 4 +-- src/librustc_mir/build/scope.rs | 13 +++++--- 10 files changed, 45 insertions(+), 36 deletions(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 6723fc72a40eb..ebf78792d72e2 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -159,6 +159,8 @@ pub struct VarDecl<'tcx> { pub mutability: Mutability, pub name: Name, pub ty: Ty<'tcx>, + pub scope: ScopeId, // scope in which variable was declared + pub span: Span, // span where variable was declared } /// A "temp" is a temporary that we place on the stack. They are diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs index ca23be7dc0618..b4c3e93b28005 100644 --- a/src/librustc_mir/build/block.rs +++ b/src/librustc_mir/build/block.rs @@ -20,7 +20,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { ast_block: &'tcx hir::Block) -> BlockAnd<()> { let Block { extent, span, stmts, expr } = self.hir.mirror(ast_block); - self.in_scope(extent, block, move |this| { + self.in_scope(extent, block, move |this, _| { // This convoluted structure is to avoid using recursion as we walk down a list // of statements. Basically, the structure we get back is something like: // @@ -42,7 +42,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { let Stmt { span: _, kind } = this.hir.mirror(stmt); match kind { StmtKind::Expr { scope, expr } => { - unpack!(block = this.in_scope(scope, block, |this| { + unpack!(block = this.in_scope(scope, block, |this, _| { let expr = this.hir.mirror(expr); let temp = this.temp(expr.ty.clone()); unpack!(block = this.into(&temp, block, expr)); @@ -51,14 +51,14 @@ impl<'a,'tcx> Builder<'a,'tcx> { })); } StmtKind::Let { remainder_scope, init_scope, pattern, initializer } => { - this.push_scope(remainder_scope, block); + let remainder_scope_id = this.push_scope(remainder_scope, block); let_extent_stack.push(remainder_scope); - unpack!(block = this.in_scope(init_scope, block, move |this| { + unpack!(block = this.in_scope(init_scope, block, move |this, _| { // FIXME #30046 ^~~~ if let Some(init) = initializer { - this.expr_into_pattern(block, remainder_scope, pattern, init) + this.expr_into_pattern(block, remainder_scope_id, pattern, init) } else { - this.declare_bindings(remainder_scope, &pattern); + this.declare_bindings(remainder_scope_id, &pattern); block.unit() } })); diff --git a/src/librustc_mir/build/expr/as_lvalue.rs b/src/librustc_mir/build/expr/as_lvalue.rs index b2c7507ed7b28..be2b70b86990e 100644 --- a/src/librustc_mir/build/expr/as_lvalue.rs +++ b/src/librustc_mir/build/expr/as_lvalue.rs @@ -37,7 +37,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { let expr_span = expr.span; match expr.kind { ExprKind::Scope { extent, value } => { - this.in_scope(extent, block, |this| this.as_lvalue(block, value)) + this.in_scope(extent, block, |this, _| this.as_lvalue(block, value)) } ExprKind::Field { lhs, name } => { let lvalue = unpack!(block = this.as_lvalue(block, lhs)); diff --git a/src/librustc_mir/build/expr/as_operand.rs b/src/librustc_mir/build/expr/as_operand.rs index 7738ebca26b47..661d01ce989cd 100644 --- a/src/librustc_mir/build/expr/as_operand.rs +++ b/src/librustc_mir/build/expr/as_operand.rs @@ -35,7 +35,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { let this = self; if let ExprKind::Scope { extent, value } = expr.kind { - return this.in_scope(extent, block, |this| this.as_operand(block, value)); + return this.in_scope(extent, block, |this, _| this.as_operand(block, value)); } let category = Category::of(&expr.kind).unwrap(); diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index 4c0e9b98d9a6e..a77a64ec3d899 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -37,7 +37,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { match expr.kind { ExprKind::Scope { extent, value } => { - this.in_scope(extent, block, |this| this.as_rvalue(block, value)) + this.in_scope(extent, block, |this, _| this.as_rvalue(block, value)) } ExprKind::InlineAsm { asm, outputs, inputs } => { let outputs = outputs.into_iter().map(|output| { @@ -76,7 +76,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { let result = this.temp(expr.ty); // to start, malloc some memory of suitable type (thus far, uninitialized): this.cfg.push_assign(block, expr_span, &result, Rvalue::Box(value.ty)); - this.in_scope(value_extents, block, |this| { + this.in_scope(value_extents, 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: diff --git a/src/librustc_mir/build/expr/as_temp.rs b/src/librustc_mir/build/expr/as_temp.rs index 27c374e1ac2c4..2041fef885d08 100644 --- a/src/librustc_mir/build/expr/as_temp.rs +++ b/src/librustc_mir/build/expr/as_temp.rs @@ -30,7 +30,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { let this = self; if let ExprKind::Scope { extent, value } = expr.kind { - return this.in_scope(extent, block, |this| this.as_temp(block, value)); + return this.in_scope(extent, block, |this, _| this.as_temp(block, value)); } let expr_ty = expr.ty.clone(); diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index a7f4a53b022e1..ce2b7dc34ebf0 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -39,7 +39,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { match expr.kind { ExprKind::Scope { extent, value } => { - 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 } => { this.ast_block(destination, block, ast_block) diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 673ff9e86c440..bc92da30caba8 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -16,7 +16,6 @@ use build::{BlockAnd, BlockAndExtension, Builder}; use rustc_data_structures::fnv::FnvHashMap; use rustc::middle::const_eval::ConstVal; -use rustc::middle::region::CodeExtent; use rustc::middle::ty::{AdtDef, Ty}; use rustc::mir::repr::*; use hair::*; @@ -42,9 +41,9 @@ impl<'a,'tcx> Builder<'a,'tcx> { // suitable extent for all of the bindings in this match. It's // easiest to do this up front because some of these arms may // be unreachable or reachable multiple times. - let var_extent = self.extent_of_innermost_scope(); + let var_scope_id = self.innermost_scope_id(); for arm in &arms { - self.declare_bindings(var_extent, &arm.patterns[0]); + self.declare_bindings(var_scope_id, &arm.patterns[0]); } let mut arm_blocks = ArmBlocks { @@ -106,7 +105,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { pub fn expr_into_pattern(&mut self, mut block: BasicBlock, - var_extent: CodeExtent, // lifetime of vars + var_scope_id: ScopeId, // lifetime of vars irrefutable_pat: Pattern<'tcx>, initializer: ExprRef<'tcx>) -> BlockAnd<()> { @@ -118,7 +117,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { var, ty, subpattern: None } => { - let index = self.declare_binding(var_extent, + let index = self.declare_binding(var_scope_id, mutability, name, var, @@ -131,19 +130,19 @@ impl<'a,'tcx> Builder<'a,'tcx> { } let lvalue = unpack!(block = self.as_lvalue(block, initializer)); self.lvalue_into_pattern(block, - var_extent, + var_scope_id, irrefutable_pat, &lvalue) } pub fn lvalue_into_pattern(&mut self, mut block: BasicBlock, - var_extent: CodeExtent, + var_scope_id: ScopeId, irrefutable_pat: Pattern<'tcx>, initializer: &Lvalue<'tcx>) -> BlockAnd<()> { // first, creating the bindings - self.declare_bindings(var_extent, &irrefutable_pat); + self.declare_bindings(var_scope_id, &irrefutable_pat); // create a dummy candidate let mut candidate = Candidate { @@ -170,29 +169,29 @@ impl<'a,'tcx> Builder<'a,'tcx> { block.unit() } - pub fn declare_bindings(&mut self, var_extent: CodeExtent, pattern: &Pattern<'tcx>) { + pub fn declare_bindings(&mut self, var_scope_id: ScopeId, pattern: &Pattern<'tcx>) { match *pattern.kind { PatternKind::Binding { mutability, name, mode: _, var, ty, ref subpattern } => { - self.declare_binding(var_extent, mutability, name, var, ty, pattern.span); + self.declare_binding(var_scope_id, mutability, name, var, ty, pattern.span); if let Some(subpattern) = subpattern.as_ref() { - self.declare_bindings(var_extent, subpattern); + self.declare_bindings(var_scope_id, subpattern); } } PatternKind::Array { ref prefix, ref slice, ref suffix } | PatternKind::Slice { ref prefix, ref slice, ref suffix } => { for subpattern in prefix.iter().chain(slice).chain(suffix) { - self.declare_bindings(var_extent, subpattern); + self.declare_bindings(var_scope_id, subpattern); } } PatternKind::Constant { .. } | PatternKind::Range { .. } | PatternKind::Wild => { } PatternKind::Deref { ref subpattern } => { - self.declare_bindings(var_extent, subpattern); + self.declare_bindings(var_scope_id, subpattern); } PatternKind::Leaf { ref subpatterns } | PatternKind::Variant { ref subpatterns, .. } => { for subpattern in subpatterns { - self.declare_bindings(var_extent, &subpattern.pattern); + self.declare_bindings(var_scope_id, &subpattern.pattern); } } } @@ -590,7 +589,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { } fn declare_binding(&mut self, - var_extent: CodeExtent, + var_scope_id: ScopeId, mutability: Mutability, name: Name, var_id: NodeId, @@ -598,17 +597,20 @@ impl<'a,'tcx> Builder<'a,'tcx> { span: Span) -> u32 { - debug!("declare_binding(var_id={:?}, name={:?}, var_ty={:?}, var_extent={:?}, span={:?})", - var_id, name, var_ty, var_extent, span); + debug!("declare_binding(var_id={:?}, name={:?}, var_ty={:?}, var_scope_id={:?}, span={:?})", + var_id, name, var_ty, var_scope_id, span); let index = self.var_decls.len(); self.var_decls.push(VarDecl::<'tcx> { + scope: var_scope_id, mutability: mutability, name: name, ty: var_ty.clone(), + span: span, }); let index = index as u32; - self.schedule_drop(span, var_extent, &Lvalue::Var(index), var_ty); + let extent = self.scope_auxiliary[var_scope_id.index()].extent; + self.schedule_drop(span, extent, &Lvalue::Var(index), var_ty); self.var_indices.insert(var_id, index); debug!("declare_binding: index={:?}", index); diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 41259f8a281de..4bb6b20a5d5b2 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -192,7 +192,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { ast_block: &'tcx hir::Block) -> BlockAnd>> { - self.in_scope(argument_extent, block, |this| { + self.in_scope(argument_extent, block, |this, argument_scope_id| { // to start, translate the argument patterns and collect the argument types. let implicits = implicit_arguments.into_iter().map(|ty| (ty, None)); let explicits = explicit_arguments.into_iter().map(|(ty, pat)| (ty, Some(pat))); @@ -205,7 +205,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { if let Some(pattern) = pattern { let pattern = this.hir.irrefutable_pat(pattern); unpack!(block = this.lvalue_into_pattern(block, - argument_extent, + argument_scope_id, pattern, &lvalue)); } diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 6a734e1816a25..1dd9ec5bae5d1 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -208,11 +208,11 @@ impl<'a,'tcx> Builder<'a,'tcx> { /// Convenience wrapper that pushes a scope and then executes `f` /// to build its contents, popping the scope afterwards. pub fn in_scope(&mut self, extent: CodeExtent, mut block: BasicBlock, f: F) -> BlockAnd - where F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd + where F: FnOnce(&mut Builder<'a, 'tcx>, ScopeId) -> BlockAnd { debug!("in_scope(extent={:?}, block={:?})", extent, block); - self.push_scope(extent, block); - let rv = unpack!(block = f(self)); + let id = self.push_scope(extent, block); + let rv = unpack!(block = f(self, id)); unpack!(block = self.pop_scope(extent, block)); debug!("in_scope: exiting extent={:?} block={:?}", extent, block); block.and(rv) @@ -222,7 +222,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { /// scope and call `pop_scope` afterwards. Note that these two /// calls must be paired; using `in_scope` as a convenience /// wrapper maybe preferable. - pub fn push_scope(&mut self, extent: CodeExtent, entry: BasicBlock) { + pub fn push_scope(&mut self, extent: CodeExtent, entry: BasicBlock) -> ScopeId { debug!("push_scope({:?})", extent); let parent_id = self.scopes.last().map(|s| s.id); let id = ScopeId::new(self.scope_data_vec.vec.len()); @@ -240,6 +240,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { dom: self.cfg.current_location(entry), postdoms: vec![] }); + id } /// Pops a scope, which should have extent `extent`, adding any @@ -321,6 +322,10 @@ impl<'a,'tcx> Builder<'a,'tcx> { }.unwrap_or_else(|| hir.span_bug(span, "no enclosing loop scope found?")) } + pub fn innermost_scope_id(&self) -> ScopeId { + self.scopes.last().map(|scope| scope.id).unwrap() + } + pub fn extent_of_innermost_scope(&self) -> CodeExtent { self.scopes.last().map(|scope| scope.extent).unwrap() } From e752d4cde359cebad6decc2e98334ff999031eba Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 9 Mar 2016 13:36:04 -0500 Subject: [PATCH 03/24] track the innermost scope for every stmt --- src/librustc/mir/repr.rs | 2 ++ src/librustc_mir/build/block.rs | 3 ++- src/librustc_mir/build/cfg.rs | 9 +++++++-- src/librustc_mir/build/expr/as_lvalue.rs | 5 +++-- src/librustc_mir/build/expr/as_rvalue.rs | 3 ++- src/librustc_mir/build/expr/as_temp.rs | 3 ++- src/librustc_mir/build/expr/into.rs | 18 ++++++++++-------- src/librustc_mir/build/matches/mod.rs | 4 +++- src/librustc_mir/build/matches/test.rs | 14 +++++++++----- src/librustc_mir/build/matches/util.rs | 3 ++- src/librustc_mir/build/misc.rs | 9 +++++++-- src/librustc_mir/build/scope.rs | 10 ++++++---- 12 files changed, 55 insertions(+), 28 deletions(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index ebf78792d72e2..7f28fe05beab0 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -458,6 +458,7 @@ impl<'tcx> Terminator<'tcx> { #[derive(Clone, RustcEncodable, RustcDecodable)] pub struct Statement<'tcx> { pub span: Span, + pub scope: ScopeId, pub kind: StatementKind<'tcx>, } @@ -474,6 +475,7 @@ impl<'tcx> Debug for Statement<'tcx> { } } } + /////////////////////////////////////////////////////////////////////////// // Lvalues diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs index b4c3e93b28005..7ada408361d05 100644 --- a/src/librustc_mir/build/block.rs +++ b/src/librustc_mir/build/block.rs @@ -71,7 +71,8 @@ impl<'a,'tcx> Builder<'a,'tcx> { unpack!(block = this.into(destination, block, expr)); } else { // FIXME(#31472) - this.cfg.push_assign_unit(block, span, destination); + let scope_id = this.innermost_scope_id(); + this.cfg.push_assign_unit(block, scope_id, span, destination); } // Finally, we pop all the let scopes before exiting out from the scope of block // itself. diff --git a/src/librustc_mir/build/cfg.rs b/src/librustc_mir/build/cfg.rs index d804fc8635a92..e85c8ccce8473 100644 --- a/src/librustc_mir/build/cfg.rs +++ b/src/librustc_mir/build/cfg.rs @@ -50,10 +50,12 @@ impl<'tcx> CFG<'tcx> { pub fn push_assign(&mut self, block: BasicBlock, + scope: ScopeId, span: Span, lvalue: &Lvalue<'tcx>, rvalue: Rvalue<'tcx>) { self.push(block, Statement { + scope: scope, span: span, kind: StatementKind::Assign(lvalue.clone(), rvalue) }); @@ -61,17 +63,20 @@ impl<'tcx> CFG<'tcx> { pub fn push_assign_constant(&mut self, block: BasicBlock, + scope: ScopeId, span: Span, temp: &Lvalue<'tcx>, constant: Constant<'tcx>) { - self.push_assign(block, span, temp, Rvalue::Use(Operand::Constant(constant))); + self.push_assign(block, scope, span, temp, + Rvalue::Use(Operand::Constant(constant))); } pub fn push_assign_unit(&mut self, block: BasicBlock, + scope: ScopeId, span: Span, lvalue: &Lvalue<'tcx>) { - self.push_assign(block, span, lvalue, Rvalue::Aggregate( + self.push_assign(block, scope, span, lvalue, Rvalue::Aggregate( AggregateKind::Tuple, vec![] )); } diff --git a/src/librustc_mir/build/expr/as_lvalue.rs b/src/librustc_mir/build/expr/as_lvalue.rs index be2b70b86990e..fba1ff6434bb9 100644 --- a/src/librustc_mir/build/expr/as_lvalue.rs +++ b/src/librustc_mir/build/expr/as_lvalue.rs @@ -34,6 +34,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { debug!("expr_as_lvalue(block={:?}, expr={:?})", block, expr); let this = self; + let scope_id = this.innermost_scope_id(); let expr_span = expr.span; match expr.kind { ExprKind::Scope { extent, value } => { @@ -58,9 +59,9 @@ impl<'a,'tcx> Builder<'a,'tcx> { // bounds check: let (len, lt) = (this.temp(usize_ty.clone()), this.temp(bool_ty)); - this.cfg.push_assign(block, expr_span, // len = len(slice) + this.cfg.push_assign(block, scope_id, expr_span, // len = len(slice) &len, Rvalue::Len(slice.clone())); - this.cfg.push_assign(block, expr_span, // lt = idx < len + this.cfg.push_assign(block, scope_id, expr_span, // lt = idx < len <, Rvalue::BinaryOp(BinOp::Lt, idx.clone(), Operand::Consume(len.clone()))); diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index a77a64ec3d899..b340d933e64c3 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -33,6 +33,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { debug!("expr_as_rvalue(block={:?}, expr={:?})", block, expr); let this = self; + let scope_id = this.innermost_scope_id(); let expr_span = expr.span; match expr.kind { @@ -75,7 +76,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { let value = this.hir.mirror(value); let result = this.temp(expr.ty); // to start, malloc some memory of suitable type (thus far, uninitialized): - this.cfg.push_assign(block, expr_span, &result, Rvalue::Box(value.ty)); + this.cfg.push_assign(block, scope_id, expr_span, &result, Rvalue::Box(value.ty)); this.in_scope(value_extents, block, |this, _| { // schedule a shallow free of that memory, lest we unwind: this.schedule_box_free(expr_span, value_extents, &result, value.ty); diff --git a/src/librustc_mir/build/expr/as_temp.rs b/src/librustc_mir/build/expr/as_temp.rs index 2041fef885d08..30a42bcd709d2 100644 --- a/src/librustc_mir/build/expr/as_temp.rs +++ b/src/librustc_mir/build/expr/as_temp.rs @@ -55,7 +55,8 @@ impl<'a,'tcx> Builder<'a,'tcx> { let expr_span = expr.span; let lvalue = unpack!(block = this.as_lvalue(block, expr)); let rvalue = Rvalue::Use(Operand::Consume(lvalue)); - this.cfg.push_assign(block, expr_span, &temp, rvalue); + let scope_id = this.innermost_scope_id(); + this.cfg.push_assign(block, scope_id, expr_span, &temp, rvalue); } _ => { unpack!(block = this.into(&temp, block, expr)); diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index ce2b7dc34ebf0..08b156c07bb41 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -36,6 +36,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { // just use the name `this` uniformly let this = self; let expr_span = expr.span; + let scope_id = this.innermost_scope_id(); match expr.kind { ExprKind::Scope { extent, value } => { @@ -63,7 +64,8 @@ impl<'a,'tcx> Builder<'a,'tcx> { } else { // Body of the `if` expression without an `else` clause must return `()`, thus // we implicitly generate a `else {}` if it is not specified. - this.cfg.push_assign_unit(else_block, expr_span, destination); + let scope_id = this.innermost_scope_id(); + this.cfg.push_assign_unit(else_block, scope_id, expr_span, destination); else_block }; @@ -104,7 +106,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { }); this.cfg.push_assign_constant( - true_block, expr_span, destination, + true_block, scope_id, expr_span, destination, Constant { span: expr_span, ty: this.hir.bool_ty(), @@ -112,7 +114,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { }); this.cfg.push_assign_constant( - false_block, expr_span, destination, + false_block, scope_id, expr_span, destination, Constant { span: expr_span, ty: this.hir.bool_ty(), @@ -178,7 +180,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { // If the loop may reach its exit_block, we assign an empty tuple to the // destination to keep the MIR well-formed. if might_break { - this.cfg.push_assign_unit(exit_block, expr_span, destination); + this.cfg.push_assign_unit(exit_block, scope_id, expr_span, destination); } exit_block.unit() } @@ -189,7 +191,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { let rhs = unpack!(block = this.as_operand(block, rhs)); let lhs = unpack!(block = this.as_lvalue(block, lhs)); unpack!(block = this.build_drop(block, lhs.clone())); - this.cfg.push_assign(block, expr_span, &lhs, Rvalue::Use(rhs)); + this.cfg.push_assign(block, scope_id, expr_span, &lhs, Rvalue::Use(rhs)); block.unit() } ExprKind::AssignOp { op, lhs, rhs } => { @@ -208,7 +210,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { // we don't have to drop prior contents or anything // because AssignOp is only legal for Copy types // (overloaded ops should be desugared into a call). - this.cfg.push_assign(block, expr_span, &lhs, + this.cfg.push_assign(block, scope_id, expr_span, &lhs, Rvalue::BinaryOp(op, Operand::Consume(lhs.clone()), rhs)); @@ -229,7 +231,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { block = match value { Some(value) => unpack!(this.into(&Lvalue::ReturnPointer, block, value)), None => { - this.cfg.push_assign_unit(block, expr_span, &Lvalue::ReturnPointer); + this.cfg.push_assign_unit(block, scope_id, expr_span, &Lvalue::ReturnPointer); block } }; @@ -293,7 +295,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { }); let rvalue = unpack!(block = this.as_rvalue(block, expr)); - this.cfg.push_assign(block, expr_span, destination, rvalue); + this.cfg.push_assign(block, scope_id, expr_span, destination, rvalue); block.unit() } } diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index bc92da30caba8..7f38324bca4aa 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -584,7 +584,9 @@ impl<'a,'tcx> Builder<'a,'tcx> { Rvalue::Ref(region, borrow_kind, binding.source), }; - self.cfg.push_assign(block, binding.span, &Lvalue::Var(var_index), rvalue); + let scope_id = self.innermost_scope_id(); + self.cfg.push_assign(block, scope_id, binding.span, + &Lvalue::Var(var_index), rvalue); } } diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 0efa24f311943..f5e7cfb2d2807 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -146,6 +146,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { lvalue: &Lvalue<'tcx>, test: &Test<'tcx>) -> Vec { + let scope_id = self.innermost_scope_id(); match test.kind { TestKind::Switch { adt_def } => { let num_enum_variants = self.hir.num_variants(adt_def); @@ -189,7 +190,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { if let ty::TyArray(_, _) = mt.ty.sty { ty = tcx.mk_imm_ref(region, tcx.mk_slice(tcx.types.u8)); let val_slice = self.temp(ty); - self.cfg.push_assign(block, test.span, &val_slice, + self.cfg.push_assign(block, scope_id, test.span, &val_slice, Rvalue::Cast(CastKind::Unsize, val, ty)); val = Operand::Consume(val_slice); } @@ -204,7 +205,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { }); let slice = self.temp(ty); - self.cfg.push_assign(block, test.span, &slice, + self.cfg.push_assign(block, scope_id, test.span, &slice, Rvalue::Cast(CastKind::Unsize, array, ty)); Operand::Consume(slice) } else { @@ -268,13 +269,14 @@ impl<'a,'tcx> Builder<'a,'tcx> { let (actual, result) = (self.temp(usize_ty), self.temp(bool_ty)); // actual = len(lvalue) - self.cfg.push_assign(block, test.span, &actual, Rvalue::Len(lvalue.clone())); + self.cfg.push_assign(block, scope_id, test.span, &actual, Rvalue::Len(lvalue.clone())); // expected = - let expected = self.push_usize(block, test.span, len); + let expected = self.push_usize(block, scope_id, test.span, len); // result = actual == expected OR result = actual < expected self.cfg.push_assign(block, + scope_id, test.span, &result, Rvalue::BinaryOp(op, @@ -305,7 +307,9 @@ impl<'a,'tcx> Builder<'a,'tcx> { let result = self.temp(bool_ty); // result = op(left, right) - self.cfg.push_assign(block, span, &result, Rvalue::BinaryOp(op, left, right)); + let scope_id = self.innermost_scope_id(); + self.cfg.push_assign(block, scope_id, span, &result, + Rvalue::BinaryOp(op, left, right)); // branch based on result let target_block = self.cfg.start_new_block(); diff --git a/src/librustc_mir/build/matches/util.rs b/src/librustc_mir/build/matches/util.rs index b46c3ffb76a1b..101d7594309a9 100644 --- a/src/librustc_mir/build/matches/util.rs +++ b/src/librustc_mir/build/matches/util.rs @@ -61,7 +61,8 @@ impl<'a,'tcx> Builder<'a,'tcx> { from_end: suffix_len, }; let temp = self.temp(slice.ty.clone()); // no need to schedule drop, temp is always copy - self.cfg.push_assign(block, slice.span, &temp, rvalue); + let scope_id = self.innermost_scope_id(); + self.cfg.push_assign(block, scope_id, slice.span, &temp, rvalue); match_pairs.push(MatchPair::new(temp, slice)); } diff --git a/src/librustc_mir/build/misc.rs b/src/librustc_mir/build/misc.rs index 13ab26c358d6a..0d1690783301b 100644 --- a/src/librustc_mir/build/misc.rs +++ b/src/librustc_mir/build/misc.rs @@ -46,11 +46,16 @@ impl<'a,'tcx> Builder<'a,'tcx> { Operand::Constant(constant) } - pub fn push_usize(&mut self, block: BasicBlock, span: Span, value: u64) -> Lvalue<'tcx> { + pub fn push_usize(&mut self, + block: BasicBlock, + scope_id: ScopeId, + span: Span, + value: u64) + -> Lvalue<'tcx> { let usize_ty = self.hir.usize_ty(); let temp = self.temp(usize_ty); self.cfg.push_assign_constant( - block, span, &temp, + block, scope_id, span, &temp, Constant { span: span, ty: self.hir.usize_ty(), diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 1dd9ec5bae5d1..61b35b94e7000 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -464,13 +464,14 @@ impl<'a,'tcx> Builder<'a,'tcx> { let (tuple, tuple_ref) = (self.temp(tup_ty), self.temp(ref_ty)); let (file, line) = self.span_to_fileline_args(span); let elems = vec![Operand::Constant(file), Operand::Constant(line)]; + let scope_id = self.innermost_scope_id(); // FIXME: We should have this as a constant, rather than a stack variable (to not pollute // icache with cold branch code), however to achieve that we either have to rely on rvalue // promotion or have some way, in MIR, to create constants. - self.cfg.push_assign(block, span, &tuple, // tuple = (file_arg, line_arg); + self.cfg.push_assign(block, scope_id, span, &tuple, // tuple = (file_arg, line_arg); Rvalue::Aggregate(AggregateKind::Tuple, elems)); // FIXME: is this region really correct here? - self.cfg.push_assign(block, span, &tuple_ref, // tuple_ref = &tuple; + self.cfg.push_assign(block, scope_id, span, &tuple_ref, // tuple_ref = &tuple; Rvalue::Ref(region, BorrowKind::Shared, tuple)); let cleanup = self.diverge_cleanup(); self.cfg.terminate(block, Terminator::Call { @@ -505,13 +506,14 @@ impl<'a,'tcx> Builder<'a,'tcx> { let elems = vec![Operand::Constant(message), Operand::Constant(file), Operand::Constant(line)]; + let scope_id = self.innermost_scope_id(); // FIXME: We should have this as a constant, rather than a stack variable (to not pollute // icache with cold branch code), however to achieve that we either have to rely on rvalue // promotion or have some way, in MIR, to create constants. - self.cfg.push_assign(block, span, &tuple, // tuple = (message_arg, file_arg, line_arg); + self.cfg.push_assign(block, scope_id, span, &tuple, // tuple = (message_arg, file_arg, line_arg); Rvalue::Aggregate(AggregateKind::Tuple, elems)); // FIXME: is this region really correct here? - self.cfg.push_assign(block, span, &tuple_ref, // tuple_ref = &tuple; + self.cfg.push_assign(block, scope_id, span, &tuple_ref, // tuple_ref = &tuple; Rvalue::Ref(region, BorrowKind::Shared, tuple)); let cleanup = self.diverge_cleanup(); self.cfg.terminate(block, Terminator::Call { From 3a16f57fbbd0930c10a5b467a5ab2109712c50f1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 10 Mar 2016 09:55:15 -0500 Subject: [PATCH 04/24] extend Terminator into a struct so it can have additional fields --- src/librustc/mir/repr.rs | 29 +++++-- src/librustc/mir/visit.rs | 38 ++++----- .../borrowck/mir/dataflow.rs | 21 +++-- .../borrowck/mir/gather_moves.rs | 78 +++++++++---------- src/librustc_mir/build/cfg.rs | 6 +- src/librustc_mir/build/expr/as_lvalue.rs | 2 +- src/librustc_mir/build/expr/into.rs | 22 +++--- src/librustc_mir/build/matches/mod.rs | 8 +- src/librustc_mir/build/matches/test.rs | 8 +- src/librustc_mir/build/mod.rs | 4 +- src/librustc_mir/build/scope.rs | 23 +++--- src/librustc_mir/graphviz.rs | 4 +- src/librustc_mir/transform/erase_regions.rs | 18 ++--- src/librustc_mir/transform/no_landing_pads.rs | 18 ++--- src/librustc_mir/transform/simplify_cfg.rs | 20 ++--- src/librustc_mir/transform/type_check.rs | 18 ++--- src/librustc_trans/trans/mir/block.rs | 19 ++--- 17 files changed, 177 insertions(+), 159 deletions(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 7f28fe05beab0..62fea3c6aa831 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -228,8 +228,13 @@ pub struct BasicBlockData<'tcx> { pub is_cleanup: bool, } +#[derive(Clone, Debug, RustcEncodable, RustcDecodable)] +pub struct Terminator<'tcx> { + pub kind: TerminatorKind<'tcx> +} + #[derive(Clone, RustcEncodable, RustcDecodable)] -pub enum Terminator<'tcx> { +pub enum TerminatorKind<'tcx> { /// block should have one successor in the graph; we jump there Goto { target: BasicBlock, @@ -299,7 +304,17 @@ pub enum Terminator<'tcx> { impl<'tcx> Terminator<'tcx> { pub fn successors(&self) -> Cow<[BasicBlock]> { - use self::Terminator::*; + self.kind.successors() + } + + pub fn successors_mut(&mut self) -> Vec<&mut BasicBlock> { + self.kind.successors_mut() + } +} + +impl<'tcx> TerminatorKind<'tcx> { + pub fn successors(&self) -> Cow<[BasicBlock]> { + use self::TerminatorKind::*; match *self { Goto { target: ref b } => slice::ref_slice(b).into_cow(), If { targets: (b1, b2), .. } => vec![b1, b2].into_cow(), @@ -320,7 +335,7 @@ impl<'tcx> Terminator<'tcx> { // FIXME: no mootable cow. I’m honestly not sure what a “cow” between `&mut [BasicBlock]` and // `Vec<&mut BasicBlock>` would look like in the first place. pub fn successors_mut(&mut self) -> Vec<&mut BasicBlock> { - use self::Terminator::*; + use self::TerminatorKind::*; match *self { Goto { target: ref mut b } => vec![b], If { targets: (ref mut b1, ref mut b2), .. } => vec![b1, b2], @@ -360,7 +375,7 @@ impl<'tcx> BasicBlockData<'tcx> { } } -impl<'tcx> Debug for Terminator<'tcx> { +impl<'tcx> Debug for TerminatorKind<'tcx> { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { self.fmt_head(fmt)?; let successors = self.successors(); @@ -387,12 +402,12 @@ impl<'tcx> Debug for Terminator<'tcx> { } } -impl<'tcx> Terminator<'tcx> { +impl<'tcx> TerminatorKind<'tcx> { /// Write the "head" part of the terminator; that is, its name and the data it uses to pick the /// successor basic block, if any. The only information not inlcuded is the list of possible /// successors, which may be rendered differently between the text and the graphviz format. pub fn fmt_head(&self, fmt: &mut W) -> fmt::Result { - use self::Terminator::*; + use self::TerminatorKind::*; match *self { Goto { .. } => write!(fmt, "goto"), If { cond: ref lv, .. } => write!(fmt, "if({:?})", lv), @@ -419,7 +434,7 @@ impl<'tcx> Terminator<'tcx> { /// Return the list of labels for the edges to the successor basic blocks. pub fn fmt_successor_labels(&self) -> Vec> { - use self::Terminator::*; + use self::TerminatorKind::*; match *self { Return | Resume => vec![], Goto { .. } => vec!["".into()], diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 36d45f0a51ec2..c7e4b825d58a5 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -138,52 +138,54 @@ macro_rules! make_mir_visitor { fn super_terminator(&mut self, block: BasicBlock, terminator: &$($mutability)* Terminator<'tcx>) { - match *terminator { - Terminator::Goto { target } => { + match terminator.kind { + TerminatorKind::Goto { target } => { self.visit_branch(block, target); } - Terminator::If { ref $($mutability)* cond, - ref $($mutability)* targets } => { + TerminatorKind::If { ref $($mutability)* cond, + ref $($mutability)* targets } => { self.visit_operand(cond); for &target in targets.as_slice() { self.visit_branch(block, target); } } - Terminator::Switch { ref $($mutability)* discr, - adt_def: _, - ref targets } => { + TerminatorKind::Switch { ref $($mutability)* discr, + adt_def: _, + ref targets } => { self.visit_lvalue(discr, LvalueContext::Inspect); for &target in targets { self.visit_branch(block, target); } } - Terminator::SwitchInt { ref $($mutability)* discr, - switch_ty: _, - values: _, - ref targets } => { + TerminatorKind::SwitchInt { ref $($mutability)* discr, + switch_ty: _, + values: _, + ref targets } => { self.visit_lvalue(discr, LvalueContext::Inspect); for &target in targets { self.visit_branch(block, target); } } - Terminator::Resume | - Terminator::Return => { + TerminatorKind::Resume | + TerminatorKind::Return => { } - Terminator::Drop { ref $($mutability)* value, target, unwind } => { + TerminatorKind::Drop { ref $($mutability)* value, + target, + unwind } => { self.visit_lvalue(value, LvalueContext::Drop); self.visit_branch(block, target); unwind.map(|t| self.visit_branch(block, t)); } - Terminator::Call { ref $($mutability)* func, - ref $($mutability)* args, - ref $($mutability)* destination, - cleanup } => { + TerminatorKind::Call { ref $($mutability)* func, + ref $($mutability)* args, + ref $($mutability)* destination, + cleanup } => { self.visit_operand(func); for arg in args { self.visit_operand(arg); diff --git a/src/librustc_borrowck/borrowck/mir/dataflow.rs b/src/librustc_borrowck/borrowck/mir/dataflow.rs index 69aaae91c49fa..3c3a60b995ff0 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow.rs @@ -410,29 +410,28 @@ impl DataflowState { bb: &repr::BasicBlockData, on_return: OnReturn) where OnReturn: Fn(&D, &mut [usize], &repr::Lvalue) { - let term = if let Some(ref term) = bb.terminator { term } else { return }; - match *term { - repr::Terminator::Return | - repr::Terminator::Resume => {} - repr::Terminator::Goto { ref target } | - repr::Terminator::Drop { ref target, value: _, unwind: None } => { + match bb.terminator().kind { + repr::TerminatorKind::Return | + repr::TerminatorKind::Resume => {} + repr::TerminatorKind::Goto { ref target } | + repr::TerminatorKind::Drop { ref target, value: _, unwind: None } => { self.propagate_bits_into_entry_set_for(in_out, changed, target); } - repr::Terminator::Drop { ref target, value: _, unwind: Some(ref unwind) } => { + repr::TerminatorKind::Drop { ref target, value: _, unwind: Some(ref unwind) } => { self.propagate_bits_into_entry_set_for(in_out, changed, target); self.propagate_bits_into_entry_set_for(in_out, changed, unwind); } - repr::Terminator::If { ref targets, .. } => { + repr::TerminatorKind::If { ref targets, .. } => { self.propagate_bits_into_entry_set_for(in_out, changed, &targets.0); self.propagate_bits_into_entry_set_for(in_out, changed, &targets.1); } - repr::Terminator::Switch { ref targets, .. } | - repr::Terminator::SwitchInt { ref targets, .. } => { + repr::TerminatorKind::Switch { ref targets, .. } | + repr::TerminatorKind::SwitchInt { ref targets, .. } => { for target in targets { self.propagate_bits_into_entry_set_for(in_out, changed, target); } } - repr::Terminator::Call { ref cleanup, ref destination, func: _, args: _ } => { + repr::TerminatorKind::Call { ref cleanup, ref destination, func: _, args: _ } => { if let Some(ref unwind) = *cleanup { self.propagate_bits_into_entry_set_for(in_out, changed, unwind); } diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs index 826284f1d78c0..0c42ac4fd8430 100644 --- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs +++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs @@ -11,7 +11,7 @@ use rustc::middle::ty; use rustc::mir::repr::{self, Mir, BasicBlock, Lvalue, Rvalue}; -use rustc::mir::repr::{StatementKind, Terminator}; +use rustc::mir::repr::{StatementKind, TerminatorKind}; use rustc::util::nodemap::FnvHashMap; use std::cell::{Cell}; @@ -577,50 +577,48 @@ fn gather_moves<'tcx>(mir: &Mir<'tcx>, tcx: &ty::TyCtxt<'tcx>) -> MoveData<'tcx> } } - if let Some(ref term) = bb_data.terminator { - match *term { - Terminator::Goto { target: _ } | Terminator::Resume => { } + match bb_data.terminator().kind { + TerminatorKind::Goto { target: _ } | TerminatorKind::Resume => { } - Terminator::Return => { - let source = Location { block: bb, - index: bb_data.statements.len() }; - let lval = &Lvalue::ReturnPointer.deref(); - bb_ctxt.on_move_out_lval(SK::Return, lval, source); - } + TerminatorKind::Return => { + let source = Location { block: bb, + index: bb_data.statements.len() }; + let lval = &Lvalue::ReturnPointer.deref(); + bb_ctxt.on_move_out_lval(SK::Return, lval, source); + } - Terminator::If { ref cond, targets: _ } => { - // The `cond` is always of (copyable) type `bool`, - // so there will never be anything to move. - let _ = cond; - } + TerminatorKind::If { ref cond, targets: _ } => { + // The `cond` is always of (copyable) type `bool`, + // so there will never be anything to move. + let _ = cond; + } - Terminator::SwitchInt { switch_ty: _, values: _, targets: _, ref discr } | - Terminator::Switch { adt_def: _, targets: _, ref discr } => { - // The `discr` is not consumed; that is instead - // encoded on specific match arms (and for - // SwitchInt`, it is always a copyable integer - // type anyway). - let _ = discr; - } + TerminatorKind::SwitchInt { switch_ty: _, values: _, targets: _, ref discr } | + TerminatorKind::Switch { adt_def: _, targets: _, ref discr } => { + // The `discr` is not consumed; that is instead + // encoded on specific match arms (and for + // SwitchInt`, it is always a copyable integer + // type anyway). + let _ = discr; + } - Terminator::Drop { value: ref lval, target: _, unwind: _ } => { - let source = Location { block: bb, - index: bb_data.statements.len() }; - bb_ctxt.on_move_out_lval(SK::Drop, lval, source); - } + TerminatorKind::Drop { value: ref lval, target: _, unwind: _ } => { + let source = Location { block: bb, + index: bb_data.statements.len() }; + bb_ctxt.on_move_out_lval(SK::Drop, lval, source); + } - Terminator::Call { ref func, ref args, ref destination, cleanup: _ } => { - let source = Location { block: bb, - index: bb_data.statements.len() }; - bb_ctxt.on_operand(SK::CallFn, func, source); - for arg in args { - bb_ctxt.on_operand(SK::CallArg, arg, source); - } - if let Some((ref destination, _bb)) = *destination { - // Create MovePath for `destination`, then - // discard returned index. - bb_ctxt.builder.move_path_for(destination); - } + TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => { + let source = Location { block: bb, + index: bb_data.statements.len() }; + bb_ctxt.on_operand(SK::CallFn, func, source); + for arg in args { + bb_ctxt.on_operand(SK::CallArg, arg, source); + } + if let Some((ref destination, _bb)) = *destination { + // Create MovePath for `destination`, then + // discard returned index. + bb_ctxt.builder.move_path_for(destination); } } } diff --git a/src/librustc_mir/build/cfg.rs b/src/librustc_mir/build/cfg.rs index e85c8ccce8473..ed99364a0f230 100644 --- a/src/librustc_mir/build/cfg.rs +++ b/src/librustc_mir/build/cfg.rs @@ -83,9 +83,11 @@ impl<'tcx> CFG<'tcx> { pub fn terminate(&mut self, block: BasicBlock, - terminator: Terminator<'tcx>) { + kind: TerminatorKind<'tcx>) { debug_assert!(self.block_data(block).terminator.is_none(), "terminate: block {:?} already has a terminator set", block); - self.block_data_mut(block).terminator = Some(terminator); + self.block_data_mut(block).terminator = Some(Terminator { + kind: kind + }); } } diff --git a/src/librustc_mir/build/expr/as_lvalue.rs b/src/librustc_mir/build/expr/as_lvalue.rs index fba1ff6434bb9..fa864625f7f32 100644 --- a/src/librustc_mir/build/expr/as_lvalue.rs +++ b/src/librustc_mir/build/expr/as_lvalue.rs @@ -68,7 +68,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { let (success, failure) = (this.cfg.start_new_block(), this.cfg.start_new_block()); this.cfg.terminate(block, - Terminator::If { + TerminatorKind::If { cond: Operand::Consume(lt), targets: (success, failure), }); diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 08b156c07bb41..0ce2494ee35ef 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -53,7 +53,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { let mut then_block = this.cfg.start_new_block(); let mut else_block = this.cfg.start_new_block(); - this.cfg.terminate(block, Terminator::If { + this.cfg.terminate(block, TerminatorKind::If { cond: operand, targets: (then_block, else_block) }); @@ -70,8 +70,8 @@ impl<'a,'tcx> Builder<'a,'tcx> { }; let join_block = this.cfg.start_new_block(); - this.cfg.terminate(then_block, Terminator::Goto { target: join_block }); - this.cfg.terminate(else_block, Terminator::Goto { target: join_block }); + this.cfg.terminate(then_block, TerminatorKind::Goto { target: join_block }); + this.cfg.terminate(else_block, TerminatorKind::Goto { target: join_block }); join_block.unit() } @@ -97,10 +97,10 @@ impl<'a,'tcx> Builder<'a,'tcx> { LogicalOp::And => (else_block, false_block), LogicalOp::Or => (true_block, else_block), }; - this.cfg.terminate(block, Terminator::If { cond: lhs, targets: blocks }); + this.cfg.terminate(block, TerminatorKind::If { cond: lhs, targets: blocks }); let rhs = unpack!(else_block = this.as_operand(else_block, rhs)); - this.cfg.terminate(else_block, Terminator::If { + this.cfg.terminate(else_block, TerminatorKind::If { cond: rhs, targets: (true_block, false_block) }); @@ -121,8 +121,8 @@ impl<'a,'tcx> Builder<'a,'tcx> { literal: this.hir.false_literal(), }); - this.cfg.terminate(true_block, Terminator::Goto { target: join_block }); - this.cfg.terminate(false_block, Terminator::Goto { target: join_block }); + this.cfg.terminate(true_block, TerminatorKind::Goto { target: join_block }); + this.cfg.terminate(false_block, TerminatorKind::Goto { target: join_block }); join_block.unit() } @@ -146,7 +146,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { let exit_block = this.cfg.start_new_block(); // start the loop - this.cfg.terminate(block, Terminator::Goto { target: loop_block }); + this.cfg.terminate(block, TerminatorKind::Goto { target: loop_block }); let might_break = this.in_loop_scope(loop_block, exit_block, move |this| { // conduct the test, if necessary @@ -159,7 +159,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { let cond = unpack!(loop_block_end = this.as_operand(loop_block, cond_expr)); body_block = this.cfg.start_new_block(); this.cfg.terminate(loop_block_end, - Terminator::If { + TerminatorKind::If { cond: cond, targets: (body_block, exit_block) }); @@ -175,7 +175,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { let tmp = this.get_unit_temp(); // Execute the body, branching back to the test. let body_block_end = unpack!(this.into(&tmp, body_block, body)); - this.cfg.terminate(body_block_end, Terminator::Goto { target: loop_block }); + this.cfg.terminate(body_block_end, TerminatorKind::Goto { target: loop_block }); }); // If the loop may reach its exit_block, we assign an empty tuple to the // destination to keep the MIR well-formed. @@ -254,7 +254,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { let success = this.cfg.start_new_block(); let cleanup = this.diverge_cleanup(); - this.cfg.terminate(block, Terminator::Call { + this.cfg.terminate(block, TerminatorKind::Call { func: fun, args: args, cleanup: cleanup, diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 7f38324bca4aa..2018d7255f5ed 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -97,7 +97,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { for (arm_index, arm_body) in arm_bodies.into_iter().enumerate() { let mut arm_block = arm_blocks.blocks[arm_index]; unpack!(arm_block = self.into(destination, arm_block, arm_body)); - self.cfg.terminate(arm_block, Terminator::Goto { target: end_block }); + self.cfg.terminate(arm_block, TerminatorKind::Goto { target: end_block }); } end_block.unit() @@ -383,7 +383,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { } else { let join_block = self.cfg.start_new_block(); for block in otherwise { - self.cfg.terminate(block, Terminator::Goto { target: join_block }); + self.cfg.terminate(block, TerminatorKind::Goto { target: join_block }); } join_block } @@ -555,11 +555,11 @@ impl<'a,'tcx> Builder<'a,'tcx> { // guard, this block is simply unreachable let cond = unpack!(block = self.as_operand(block, guard)); let otherwise = self.cfg.start_new_block(); - self.cfg.terminate(block, Terminator::If { cond: cond, + self.cfg.terminate(block, TerminatorKind::If { cond: cond, targets: (arm_block, otherwise)}); Some(otherwise) } else { - self.cfg.terminate(block, Terminator::Goto { target: arm_block }); + self.cfg.terminate(block, TerminatorKind::Goto { target: arm_block }); None } } diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index f5e7cfb2d2807..74402ccba348f 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -153,7 +153,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { let target_blocks: Vec<_> = (0..num_enum_variants).map(|_| self.cfg.start_new_block()) .collect(); - self.cfg.terminate(block, Terminator::Switch { + self.cfg.terminate(block, TerminatorKind::Switch { discr: lvalue.clone(), adt_def: adt_def, targets: target_blocks.clone() @@ -168,7 +168,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { .map(|_| self.cfg.start_new_block()) .chain(Some(otherwise)) .collect(); - self.cfg.terminate(block, Terminator::SwitchInt { + self.cfg.terminate(block, TerminatorKind::SwitchInt { discr: lvalue.clone(), switch_ty: switch_ty, values: options.clone(), @@ -286,7 +286,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { // branch based on result let target_blocks: Vec<_> = vec![self.cfg.start_new_block(), self.cfg.start_new_block()]; - self.cfg.terminate(block, Terminator::If { + self.cfg.terminate(block, TerminatorKind::If { cond: Operand::Consume(result), targets: (target_blocks[0], target_blocks[1]) }); @@ -313,7 +313,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { // branch based on result let target_block = self.cfg.start_new_block(); - self.cfg.terminate(block, Terminator::If { + self.cfg.terminate(block, TerminatorKind::If { cond: Operand::Consume(result), targets: (target_block, fail_block) }); diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 4bb6b20a5d5b2..44bbb2e3f1939 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -166,8 +166,8 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>, argument_extent, ast_block)); - builder.cfg.terminate(block, Terminator::Goto { target: END_BLOCK }); - builder.cfg.terminate(END_BLOCK, Terminator::Return); + builder.cfg.terminate(block, TerminatorKind::Goto { target: END_BLOCK }); + builder.cfg.terminate(END_BLOCK, TerminatorKind::Return); MirPlusPlus { mir: Mir { diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 61b35b94e7000..c6d24591d2ac3 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -295,7 +295,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { .postdoms .push(self.cfg.current_location(block)); } - self.cfg.terminate(block, Terminator::Goto { target: target }); + self.cfg.terminate(block, TerminatorKind::Goto { target: target }); } // Finding scopes @@ -432,7 +432,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { pub fn build_drop(&mut self, block: BasicBlock, value: Lvalue<'tcx>) -> BlockAnd<()> { let next_target = self.cfg.start_new_block(); let diverge_target = self.diverge_cleanup(); - self.cfg.terminate(block, Terminator::Drop { + self.cfg.terminate(block, TerminatorKind::Drop { value: value, target: next_target, unwind: diverge_target, @@ -474,7 +474,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { self.cfg.push_assign(block, scope_id, span, &tuple_ref, // tuple_ref = &tuple; Rvalue::Ref(region, BorrowKind::Shared, tuple)); let cleanup = self.diverge_cleanup(); - self.cfg.terminate(block, Terminator::Call { + self.cfg.terminate(block, TerminatorKind::Call { func: Operand::Constant(func), args: vec![Operand::Consume(tuple_ref), index, len], destination: None, @@ -516,7 +516,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { self.cfg.push_assign(block, scope_id, span, &tuple_ref, // tuple_ref = &tuple; Rvalue::Ref(region, BorrowKind::Shared, tuple)); let cleanup = self.diverge_cleanup(); - self.cfg.terminate(block, Terminator::Call { + self.cfg.terminate(block, TerminatorKind::Call { func: Operand::Constant(func), args: vec![Operand::Consume(tuple_ref)], cleanup: cleanup, @@ -575,7 +575,7 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>, earlier_scopes.iter().rev().flat_map(|s| s.cached_block()).next() }); let next = cfg.start_new_block(); - cfg.terminate(block, Terminator::Drop { + cfg.terminate(block, TerminatorKind::Drop { value: drop_data.value.clone(), target: next, unwind: on_diverge @@ -600,7 +600,7 @@ fn build_diverge_scope<'tcx>(tcx: &TyCtxt<'tcx>, for drop_data in scope.drops.iter_mut().rev() { if let Some(cached_block) = drop_data.cached_block { if let Some((previous_block, previous_value)) = previous { - cfg.terminate(previous_block, Terminator::Drop { + cfg.terminate(previous_block, TerminatorKind::Drop { value: previous_value, target: cached_block, unwind: None @@ -613,7 +613,7 @@ fn build_diverge_scope<'tcx>(tcx: &TyCtxt<'tcx>, let block = cfg.start_new_cleanup_block(); drop_data.cached_block = Some(block); if let Some((previous_block, previous_value)) = previous { - cfg.terminate(previous_block, Terminator::Drop { + cfg.terminate(previous_block, TerminatorKind::Drop { value: previous_value, target: block, unwind: None @@ -628,7 +628,7 @@ fn build_diverge_scope<'tcx>(tcx: &TyCtxt<'tcx>, // Prepare the end target for this chain. let mut target = target.unwrap_or_else(||{ let b = cfg.start_new_cleanup_block(); - cfg.terminate(b, Terminator::Resume); + cfg.terminate(b, TerminatorKind::Resume); b }); @@ -646,7 +646,7 @@ fn build_diverge_scope<'tcx>(tcx: &TyCtxt<'tcx>, if let Some((previous_block, previous_value)) = previous { // Finally, branch into that just-built `target` from the `previous_block`. - cfg.terminate(previous_block, Terminator::Drop { + cfg.terminate(previous_block, TerminatorKind::Drop { value: previous_value, target: target, unwind: None @@ -662,14 +662,15 @@ fn build_diverge_scope<'tcx>(tcx: &TyCtxt<'tcx>, fn build_free<'tcx>(tcx: &TyCtxt<'tcx>, unit_temp: Lvalue<'tcx>, data: &FreeData<'tcx>, - target: BasicBlock) -> Terminator<'tcx> { + target: BasicBlock) + -> TerminatorKind<'tcx> { let free_func = tcx.lang_items.require(lang_items::BoxFreeFnLangItem) .unwrap_or_else(|e| tcx.sess.fatal(&e)); let substs = tcx.mk_substs(Substs::new( VecPerParamSpace::new(vec![], vec![], vec![data.item_ty]), VecPerParamSpace::new(vec![], vec![], vec![]) )); - Terminator::Call { + TerminatorKind::Call { func: Operand::Constant(Constant { span: data.span, ty: tcx.lookup_item_type(free_func).ty.subst(tcx, substs), diff --git a/src/librustc_mir/graphviz.rs b/src/librustc_mir/graphviz.rs index 953eb724f50ec..a5e749ea6870e 100644 --- a/src/librustc_mir/graphviz.rs +++ b/src/librustc_mir/graphviz.rs @@ -83,7 +83,7 @@ pub fn write_node_label(block: BasicBlock, // Terminator head at the bottom, not including the list of successor blocks. Those will be // displayed as labels on the edges between blocks. let mut terminator_head = String::new(); - data.terminator().fmt_head(&mut terminator_head).unwrap(); + data.terminator().kind.fmt_head(&mut terminator_head).unwrap(); write!(w, r#"{}"#, dot::escape_html(&terminator_head))?; fini(w)?; @@ -104,7 +104,7 @@ fn write_node(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<( /// Write graphviz DOT edges with labels between the given basic block and all of its successors. fn write_edges(source: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> { let terminator = &mir.basic_block_data(source).terminator(); - let labels = terminator.fmt_successor_labels(); + let labels = terminator.kind.fmt_successor_labels(); for (&target, label) in terminator.successors().iter().zip(labels) { writeln!(w, r#" {} -> {} [label="{}"];"#, node(source), node(target), label)?; diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs index 9ac3749589e90..d8aa0d9b72503 100644 --- a/src/librustc_mir/transform/erase_regions.rs +++ b/src/librustc_mir/transform/erase_regions.rs @@ -58,17 +58,17 @@ impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> { } fn visit_terminator(&mut self, bb: BasicBlock, terminator: &mut Terminator<'tcx>) { - match *terminator { - Terminator::Goto { .. } | - Terminator::Resume | - Terminator::Return | - Terminator::If { .. } | - Terminator::Switch { .. } | - Terminator::Drop { .. } | - Terminator::Call { .. } => { + match terminator.kind { + TerminatorKind::Goto { .. } | + TerminatorKind::Resume | + TerminatorKind::Return | + TerminatorKind::If { .. } | + TerminatorKind::Switch { .. } | + TerminatorKind::Drop { .. } | + TerminatorKind::Call { .. } => { /* nothing to do */ }, - Terminator::SwitchInt { ref mut switch_ty, .. } => { + TerminatorKind::SwitchInt { ref mut switch_ty, .. } => { *switch_ty = self.tcx.erase_regions(switch_ty); }, } diff --git a/src/librustc_mir/transform/no_landing_pads.rs b/src/librustc_mir/transform/no_landing_pads.rs index 9caee36e44a7d..0e9a18107870e 100644 --- a/src/librustc_mir/transform/no_landing_pads.rs +++ b/src/librustc_mir/transform/no_landing_pads.rs @@ -21,19 +21,19 @@ pub struct NoLandingPads; impl<'tcx> MutVisitor<'tcx> for NoLandingPads { fn visit_terminator(&mut self, bb: BasicBlock, terminator: &mut Terminator<'tcx>) { - match *terminator { - Terminator::Goto { .. } | - Terminator::Resume | - Terminator::Return | - Terminator::If { .. } | - Terminator::Switch { .. } | - Terminator::SwitchInt { .. } => { + match terminator.kind { + TerminatorKind::Goto { .. } | + TerminatorKind::Resume | + TerminatorKind::Return | + TerminatorKind::If { .. } | + TerminatorKind::Switch { .. } | + TerminatorKind::SwitchInt { .. } => { /* nothing to do */ }, - Terminator::Drop { ref mut unwind, .. } => { + TerminatorKind::Drop { ref mut unwind, .. } => { unwind.take(); }, - Terminator::Call { ref mut cleanup, .. } => { + TerminatorKind::Call { ref mut cleanup, .. } => { cleanup.take(); }, } diff --git a/src/librustc_mir/transform/simplify_cfg.rs b/src/librustc_mir/transform/simplify_cfg.rs index 84410bdc57c0a..4e192095043eb 100644 --- a/src/librustc_mir/transform/simplify_cfg.rs +++ b/src/librustc_mir/transform/simplify_cfg.rs @@ -30,8 +30,8 @@ impl SimplifyCfg { let mut seen: Vec = Vec::with_capacity(8); while mir.basic_block_data(target).statements.is_empty() { - match mir.basic_block_data(target).terminator { - Some(Terminator::Goto { target: next }) => { + match mir.basic_block_data(target).terminator().kind { + TerminatorKind::Goto { target: next } => { if seen.contains(&next) { return None; } @@ -71,27 +71,27 @@ impl SimplifyCfg { for bb in mir.all_basic_blocks() { let basic_block = mir.basic_block_data_mut(bb); let mut terminator = basic_block.terminator_mut(); - *terminator = match *terminator { - Terminator::If { ref targets, .. } if targets.0 == targets.1 => { + terminator.kind = match terminator.kind { + TerminatorKind::If { ref targets, .. } if targets.0 == targets.1 => { changed = true; - Terminator::Goto { target: targets.0 } + TerminatorKind::Goto { target: targets.0 } } - Terminator::If { ref targets, cond: Operand::Constant(Constant { + TerminatorKind::If { ref targets, cond: Operand::Constant(Constant { literal: Literal::Value { value: ConstVal::Bool(cond) }, .. }) } => { changed = true; if cond { - Terminator::Goto { target: targets.0 } + TerminatorKind::Goto { target: targets.0 } } else { - Terminator::Goto { target: targets.1 } + TerminatorKind::Goto { target: targets.1 } } } - Terminator::SwitchInt { ref targets, .. } if targets.len() == 1 => { - Terminator::Goto { target: targets[0] } + TerminatorKind::SwitchInt { ref targets, .. } if targets.len() == 1 => { + TerminatorKind::Goto { target: targets[0] } } _ => continue } diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index d99e6ff4bf55d..c0605ebe6d4a8 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -376,15 +376,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { term: &Terminator<'tcx>) { debug!("check_terminator: {:?}", term); let tcx = self.tcx(); - match *term { - Terminator::Goto { .. } | - Terminator::Resume | - Terminator::Return | - Terminator::Drop { .. } => { + match term.kind { + TerminatorKind::Goto { .. } | + TerminatorKind::Resume | + TerminatorKind::Return | + TerminatorKind::Drop { .. } => { // no checks needed for these } - Terminator::If { ref cond, .. } => { + TerminatorKind::If { ref cond, .. } => { let cond_ty = mir.operand_ty(tcx, cond); match cond_ty.sty { ty::TyBool => {} @@ -393,7 +393,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } } - Terminator::SwitchInt { ref discr, switch_ty, .. } => { + TerminatorKind::SwitchInt { ref discr, switch_ty, .. } => { let discr_ty = mir.lvalue_ty(tcx, discr).to_ty(tcx); if let Err(terr) = self.mk_subty(self.last_span, discr_ty, switch_ty) { span_mirbug!(self, term, "bad SwitchInt ({:?} on {:?}): {:?}", @@ -406,7 +406,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } // FIXME: check the values } - Terminator::Switch { ref discr, adt_def, ref targets } => { + TerminatorKind::Switch { ref discr, adt_def, ref targets } => { let discr_ty = mir.lvalue_ty(tcx, discr).to_ty(tcx); match discr_ty.sty { ty::TyEnum(def, _) @@ -418,7 +418,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } } - Terminator::Call { ref func, ref args, ref destination, .. } => { + TerminatorKind::Call { ref func, ref args, ref destination, .. } => { let func_ty = mir.operand_ty(tcx, func); debug!("check_terminator: call, func_ty={:?}", func_ty); let func_ty = match func_ty.sty { diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs index 080547952a5ce..0fb4975453a23 100644 --- a/src/librustc_trans/trans/mir/block.rs +++ b/src/librustc_trans/trans/mir/block.rs @@ -56,8 +56,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { debug!("trans_block: terminator: {:?}", data.terminator()); - match *data.terminator() { - mir::Terminator::Resume => { + match data.terminator().kind { + mir::TerminatorKind::Resume => { if let Some(cleanup_pad) = cleanup_pad { bcx.cleanup_ret(cleanup_pad, None); } else { @@ -70,18 +70,18 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } - mir::Terminator::Goto { target } => { + mir::TerminatorKind::Goto { target } => { funclet_br(bcx, self.llblock(target)); } - mir::Terminator::If { ref cond, targets: (true_bb, false_bb) } => { + mir::TerminatorKind::If { ref cond, targets: (true_bb, false_bb) } => { let cond = self.trans_operand(&bcx, cond); let lltrue = self.llblock(true_bb); let llfalse = self.llblock(false_bb); bcx.cond_br(cond.immediate(), lltrue, llfalse); } - mir::Terminator::Switch { ref discr, ref adt_def, ref targets } => { + mir::TerminatorKind::Switch { ref discr, ref adt_def, ref targets } => { let discr_lvalue = self.trans_lvalue(&bcx, discr); let ty = discr_lvalue.ty.to_ty(bcx.tcx()); let repr = adt::represent_type(bcx.ccx(), ty); @@ -103,7 +103,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } - mir::Terminator::SwitchInt { ref discr, switch_ty, ref values, ref targets } => { + mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => { let (otherwise, targets) = targets.split_last().unwrap(); let discr = bcx.load(self.trans_lvalue(&bcx, discr).llval); let discr = bcx.with_block(|bcx| base::to_immediate(bcx, discr, switch_ty)); @@ -115,13 +115,13 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } - mir::Terminator::Return => { + mir::TerminatorKind::Return => { bcx.with_block(|bcx| { self.fcx.build_return_block(bcx, DebugLoc::None); }) } - mir::Terminator::Drop { ref value, target, unwind } => { + mir::TerminatorKind::Drop { ref value, target, unwind } => { let lvalue = self.trans_lvalue(&bcx, value); let ty = lvalue.ty.to_ty(bcx.tcx()); // Double check for necessity to drop @@ -152,7 +152,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } - mir::Terminator::Call { ref func, ref args, ref destination, ref cleanup } => { + mir::TerminatorKind::Call { ref func, ref args, ref destination, ref cleanup } => { // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar. let callee = self.trans_operand(&bcx, func); @@ -569,3 +569,4 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { self.blocks[bb.index()].llbb } } + From 9d00deee9619db6722b27630bd037ab32fc3b9ab Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 18 Mar 2016 08:52:13 -0400 Subject: [PATCH 05/24] add span/scope-id to terminator --- src/librustc/mir/repr.rs | 2 + src/librustc_mir/build/block.rs | 3 +- src/librustc_mir/build/cfg.rs | 6 +- src/librustc_mir/build/expr/as_lvalue.rs | 2 + src/librustc_mir/build/expr/into.rs | 47 ++++++++--- src/librustc_mir/build/matches/mod.rs | 36 +++++++-- src/librustc_mir/build/matches/test.rs | 27 ++++--- src/librustc_mir/build/mod.rs | 23 +++--- src/librustc_mir/build/scope.rs | 99 +++++++++++++++--------- 9 files changed, 168 insertions(+), 77 deletions(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 62fea3c6aa831..cc95d5946a801 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -230,6 +230,8 @@ pub struct BasicBlockData<'tcx> { #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub struct Terminator<'tcx> { + pub span: Span, + pub scope: ScopeId, pub kind: TerminatorKind<'tcx> } diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs index 7ada408361d05..ef48a408e79a7 100644 --- a/src/librustc_mir/build/block.rs +++ b/src/librustc_mir/build/block.rs @@ -44,9 +44,10 @@ impl<'a,'tcx> Builder<'a,'tcx> { StmtKind::Expr { scope, expr } => { unpack!(block = this.in_scope(scope, block, |this, _| { let expr = this.hir.mirror(expr); + let expr_span = expr.span; let temp = this.temp(expr.ty.clone()); unpack!(block = this.into(&temp, block, expr)); - unpack!(block = this.build_drop(block, temp)); + unpack!(block = this.build_drop(block, expr_span, temp)); block.unit() })); } diff --git a/src/librustc_mir/build/cfg.rs b/src/librustc_mir/build/cfg.rs index ed99364a0f230..4859257f291c9 100644 --- a/src/librustc_mir/build/cfg.rs +++ b/src/librustc_mir/build/cfg.rs @@ -83,11 +83,15 @@ impl<'tcx> CFG<'tcx> { pub fn terminate(&mut self, block: BasicBlock, + scope: ScopeId, + span: Span, kind: TerminatorKind<'tcx>) { debug_assert!(self.block_data(block).terminator.is_none(), "terminate: block {:?} already has a terminator set", block); self.block_data_mut(block).terminator = Some(Terminator { - kind: kind + span: span, + scope: scope, + kind: kind, }); } } diff --git a/src/librustc_mir/build/expr/as_lvalue.rs b/src/librustc_mir/build/expr/as_lvalue.rs index fa864625f7f32..0c9323f4af378 100644 --- a/src/librustc_mir/build/expr/as_lvalue.rs +++ b/src/librustc_mir/build/expr/as_lvalue.rs @@ -68,6 +68,8 @@ impl<'a,'tcx> Builder<'a,'tcx> { let (success, failure) = (this.cfg.start_new_block(), this.cfg.start_new_block()); this.cfg.terminate(block, + scope_id, + expr_span, TerminatorKind::If { cond: Operand::Consume(lt), targets: (success, failure), diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 0ce2494ee35ef..9992e904dea16 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -53,7 +53,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { let mut then_block = this.cfg.start_new_block(); let mut else_block = this.cfg.start_new_block(); - this.cfg.terminate(block, TerminatorKind::If { + this.cfg.terminate(block, scope_id, expr_span, TerminatorKind::If { cond: operand, targets: (then_block, else_block) }); @@ -70,8 +70,14 @@ impl<'a,'tcx> Builder<'a,'tcx> { }; let join_block = this.cfg.start_new_block(); - this.cfg.terminate(then_block, TerminatorKind::Goto { target: join_block }); - this.cfg.terminate(else_block, TerminatorKind::Goto { target: join_block }); + this.cfg.terminate(then_block, + scope_id, + expr_span, + TerminatorKind::Goto { target: join_block }); + this.cfg.terminate(else_block, + scope_id, + expr_span, + TerminatorKind::Goto { target: join_block }); join_block.unit() } @@ -97,10 +103,13 @@ impl<'a,'tcx> Builder<'a,'tcx> { LogicalOp::And => (else_block, false_block), LogicalOp::Or => (true_block, else_block), }; - this.cfg.terminate(block, TerminatorKind::If { cond: lhs, targets: blocks }); + this.cfg.terminate(block, + scope_id, + expr_span, + TerminatorKind::If { cond: lhs, targets: blocks }); let rhs = unpack!(else_block = this.as_operand(else_block, rhs)); - this.cfg.terminate(else_block, TerminatorKind::If { + this.cfg.terminate(else_block, scope_id, expr_span, TerminatorKind::If { cond: rhs, targets: (true_block, false_block) }); @@ -121,8 +130,14 @@ impl<'a,'tcx> Builder<'a,'tcx> { literal: this.hir.false_literal(), }); - this.cfg.terminate(true_block, TerminatorKind::Goto { target: join_block }); - this.cfg.terminate(false_block, TerminatorKind::Goto { target: join_block }); + this.cfg.terminate(true_block, + scope_id, + expr_span, + TerminatorKind::Goto { target: join_block }); + this.cfg.terminate(false_block, + scope_id, + expr_span, + TerminatorKind::Goto { target: join_block }); join_block.unit() } @@ -146,7 +161,10 @@ impl<'a,'tcx> Builder<'a,'tcx> { let exit_block = this.cfg.start_new_block(); // start the loop - this.cfg.terminate(block, TerminatorKind::Goto { target: loop_block }); + this.cfg.terminate(block, + scope_id, + expr_span, + TerminatorKind::Goto { target: loop_block }); let might_break = this.in_loop_scope(loop_block, exit_block, move |this| { // conduct the test, if necessary @@ -159,6 +177,8 @@ impl<'a,'tcx> Builder<'a,'tcx> { let cond = unpack!(loop_block_end = this.as_operand(loop_block, cond_expr)); body_block = this.cfg.start_new_block(); this.cfg.terminate(loop_block_end, + scope_id, + expr_span, TerminatorKind::If { cond: cond, targets: (body_block, exit_block) @@ -175,7 +195,10 @@ impl<'a,'tcx> Builder<'a,'tcx> { let tmp = this.get_unit_temp(); // Execute the body, branching back to the test. let body_block_end = unpack!(this.into(&tmp, body_block, body)); - this.cfg.terminate(body_block_end, TerminatorKind::Goto { target: loop_block }); + this.cfg.terminate(body_block_end, + scope_id, + expr_span, + TerminatorKind::Goto { target: loop_block }); }); // If the loop may reach its exit_block, we assign an empty tuple to the // destination to keep the MIR well-formed. @@ -188,9 +211,11 @@ impl<'a,'tcx> Builder<'a,'tcx> { // Note: we evaluate assignments right-to-left. This // is better for borrowck interaction with overloaded // operators like x[j] = x[i]. + let lhs = this.hir.mirror(lhs); + let lhs_span = lhs.span; let rhs = unpack!(block = this.as_operand(block, rhs)); let lhs = unpack!(block = this.as_lvalue(block, lhs)); - unpack!(block = this.build_drop(block, lhs.clone())); + unpack!(block = this.build_drop(block, lhs_span, lhs.clone())); this.cfg.push_assign(block, scope_id, expr_span, &lhs, Rvalue::Use(rhs)); block.unit() } @@ -254,7 +279,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { let success = this.cfg.start_new_block(); let cleanup = this.diverge_cleanup(); - this.cfg.terminate(block, TerminatorKind::Call { + this.cfg.terminate(block, scope_id, expr_span, TerminatorKind::Call { func: fun, args: args, cleanup: cleanup, diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 2018d7255f5ed..9414752fbd864 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -71,6 +71,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { }) .map(|(arm_index, pattern, guard)| { Candidate { + span: pattern.span, match_pairs: vec![MatchPair::new(discriminant_lvalue.clone(), pattern)], bindings: vec![], guard: guard, @@ -87,7 +88,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { // an empty vector to be returned here, but the algorithm is // not entirely precise if !otherwise.is_empty() { - let join_block = self.join_otherwise_blocks(otherwise); + let join_block = self.join_otherwise_blocks(span, otherwise); self.panic(join_block, "something about matches algorithm not being precise", span); } @@ -97,7 +98,10 @@ impl<'a,'tcx> Builder<'a,'tcx> { for (arm_index, arm_body) in arm_bodies.into_iter().enumerate() { let mut arm_block = arm_blocks.blocks[arm_index]; unpack!(arm_block = self.into(destination, arm_block, arm_body)); - self.cfg.terminate(arm_block, TerminatorKind::Goto { target: end_block }); + self.cfg.terminate(arm_block, + var_scope_id, + span, + TerminatorKind::Goto { target: end_block }); } end_block.unit() @@ -146,6 +150,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { // create a dummy candidate let mut candidate = Candidate { + span: irrefutable_pat.span, match_pairs: vec![MatchPair::new(initializer.clone(), &irrefutable_pat)], bindings: vec![], guard: None, @@ -206,6 +211,9 @@ struct ArmBlocks { #[derive(Clone, Debug)] pub struct Candidate<'pat, 'tcx:'pat> { + // span of the original pattern that gave rise to this candidate + span: Span, + // all of these must be satisfied... match_pairs: Vec>, @@ -370,20 +378,25 @@ impl<'a,'tcx> Builder<'a,'tcx> { } // Otherwise, let's process those remaining candidates. - let join_block = self.join_otherwise_blocks(otherwise); + let join_block = self.join_otherwise_blocks(span, otherwise); self.match_candidates(span, arm_blocks, untested_candidates, join_block) } fn join_otherwise_blocks(&mut self, + span: Span, otherwise: Vec) -> BasicBlock { + let scope_id = self.innermost_scope_id(); if otherwise.len() == 1 { otherwise[0] } else { let join_block = self.cfg.start_new_block(); for block in otherwise { - self.cfg.terminate(block, TerminatorKind::Goto { target: join_block }); + self.cfg.terminate(block, + scope_id, + span, + TerminatorKind::Goto { target: join_block }); } join_block } @@ -550,16 +563,25 @@ impl<'a,'tcx> Builder<'a,'tcx> { let arm_block = arm_blocks.blocks[candidate.arm_index]; + let scope_id = self.innermost_scope_id(); if let Some(guard) = candidate.guard { // the block to branch to if the guard fails; if there is no // guard, this block is simply unreachable + let guard = self.hir.mirror(guard); + let guard_span = guard.span; let cond = unpack!(block = self.as_operand(block, guard)); let otherwise = self.cfg.start_new_block(); - self.cfg.terminate(block, TerminatorKind::If { cond: cond, - targets: (arm_block, otherwise)}); + self.cfg.terminate(block, + scope_id, + guard_span, + TerminatorKind::If { cond: cond, + targets: (arm_block, otherwise)}); Some(otherwise) } else { - self.cfg.terminate(block, TerminatorKind::Goto { target: arm_block }); + self.cfg.terminate(block, + scope_id, + candidate.span, + TerminatorKind::Goto { target: arm_block }); None } } diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 74402ccba348f..3e374c07b46b1 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -153,7 +153,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { let target_blocks: Vec<_> = (0..num_enum_variants).map(|_| self.cfg.start_new_block()) .collect(); - self.cfg.terminate(block, TerminatorKind::Switch { + self.cfg.terminate(block, scope_id, test.span, TerminatorKind::Switch { discr: lvalue.clone(), adt_def: adt_def, targets: target_blocks.clone() @@ -168,12 +168,15 @@ impl<'a,'tcx> Builder<'a,'tcx> { .map(|_| self.cfg.start_new_block()) .chain(Some(otherwise)) .collect(); - self.cfg.terminate(block, TerminatorKind::SwitchInt { - discr: lvalue.clone(), - switch_ty: switch_ty, - values: options.clone(), - targets: targets.clone(), - }); + self.cfg.terminate(block, + scope_id, + test.span, + TerminatorKind::SwitchInt { + discr: lvalue.clone(), + switch_ty: switch_ty, + values: options.clone(), + targets: targets.clone(), + }); targets } @@ -226,7 +229,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { let eq_result = self.temp(bool_ty); let eq_block = self.cfg.start_new_block(); let cleanup = self.diverge_cleanup(); - self.cfg.terminate(block, Terminator::Call { + self.cfg.terminate(block, scope_id, test.span, TerminatorKind::Call { func: Operand::Constant(Constant { span: test.span, ty: mty, @@ -239,7 +242,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { // check the result let block = self.cfg.start_new_block(); - self.cfg.terminate(eq_block, Terminator::If { + self.cfg.terminate(eq_block, scope_id, test.span, TerminatorKind::If { cond: Operand::Consume(eq_result), targets: (block, fail), }); @@ -286,7 +289,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { // branch based on result let target_blocks: Vec<_> = vec![self.cfg.start_new_block(), self.cfg.start_new_block()]; - self.cfg.terminate(block, TerminatorKind::If { + self.cfg.terminate(block, scope_id, test.span, TerminatorKind::If { cond: Operand::Consume(result), targets: (target_blocks[0], target_blocks[1]) }); @@ -313,7 +316,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { // branch based on result let target_block = self.cfg.start_new_block(); - self.cfg.terminate(block, TerminatorKind::If { + self.cfg.terminate(block, scope_id, span, TerminatorKind::If { cond: Operand::Consume(result), targets: (target_block, fail_block) }); @@ -466,6 +469,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { .map(|(_, mp)| mp.clone()) .collect(); Candidate { + span: candidate.span, match_pairs: other_match_pairs, bindings: candidate.bindings.clone(), guard: candidate.guard.clone(), @@ -507,6 +511,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { let all_match_pairs = consequent_match_pairs.chain(other_match_pairs).collect(); Candidate { + span: candidate.span, match_pairs: all_match_pairs, bindings: candidate.bindings.clone(), guard: candidate.guard.clone(), diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 44bbb2e3f1939..416c078f4a95a 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -160,14 +160,17 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>, assert_eq!(builder.cfg.start_new_block(), END_BLOCK); let mut block = START_BLOCK; - let arg_decls = unpack!(block = builder.args_and_body(block, - implicit_arguments, - explicit_arguments, - argument_extent, - ast_block)); - - builder.cfg.terminate(block, TerminatorKind::Goto { target: END_BLOCK }); - builder.cfg.terminate(END_BLOCK, TerminatorKind::Return); + let (arg_decls, arg_scope_id) = + unpack!(block = builder.args_and_body(block, + implicit_arguments, + explicit_arguments, + argument_extent, + ast_block)); + + builder.cfg.terminate(block, arg_scope_id, span, + TerminatorKind::Goto { target: END_BLOCK }); + builder.cfg.terminate(END_BLOCK, arg_scope_id, span, + TerminatorKind::Return); MirPlusPlus { mir: Mir { @@ -190,7 +193,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { explicit_arguments: Vec<(Ty<'tcx>, &'tcx hir::Pat)>, argument_extent: CodeExtent, ast_block: &'tcx hir::Block) - -> BlockAnd>> + -> BlockAnd<(Vec>, ScopeId)> { self.in_scope(argument_extent, block, |this, argument_scope_id| { // to start, translate the argument patterns and collect the argument types. @@ -219,7 +222,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { // start the first basic block and translate the body unpack!(block = this.ast_block(&Lvalue::ReturnPointer, block, ast_block)); - block.and(arg_decls) + block.and((arg_decls, argument_scope_id)) }) } diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index c6d24591d2ac3..e7066ff083e24 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -116,6 +116,7 @@ pub struct Scope<'tcx> { } struct DropData<'tcx> { + span: Span, value: Lvalue<'tcx>, // NB: per-drop “cache” is necessary for the build_scope_drops function below. /// The cached block for the cleanups-on-diverge path. This block contains code to run the @@ -288,14 +289,19 @@ impl<'a,'tcx> Builder<'a,'tcx> { if let Some(ref free_data) = scope.free { let next = self.cfg.start_new_block(); let free = build_free(self.hir.tcx(), tmp.clone(), free_data, next); - self.cfg.terminate(block, free); + self.cfg.terminate(block, scope.id, span, free); block = next; } self.scope_auxiliary[scope.id.index()] .postdoms .push(self.cfg.current_location(block)); } - self.cfg.terminate(block, TerminatorKind::Goto { target: target }); + + let scope_id = self.innermost_scope_id(); + self.cfg.terminate(block, + scope_id, + span, + TerminatorKind::Goto { target: target }); } // Finding scopes @@ -351,6 +357,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { // No need to invalidate any caches here. The just-scheduled drop will branch into // the drop that comes before it in the vector. scope.drops.push(DropData { + span: span, value: lvalue.clone(), cached_block: None }); @@ -429,14 +436,22 @@ impl<'a,'tcx> Builder<'a,'tcx> { } /// Utility function for *non*-scope code to build their own drops - pub fn build_drop(&mut self, block: BasicBlock, value: Lvalue<'tcx>) -> BlockAnd<()> { + pub fn build_drop(&mut self, + block: BasicBlock, + span: Span, + value: Lvalue<'tcx>) + -> BlockAnd<()> { + let scope_id = self.innermost_scope_id(); let next_target = self.cfg.start_new_block(); let diverge_target = self.diverge_cleanup(); - self.cfg.terminate(block, TerminatorKind::Drop { - value: value, - target: next_target, - unwind: diverge_target, - }); + self.cfg.terminate(block, + scope_id, + span, + TerminatorKind::Drop { + value: value, + target: next_target, + unwind: diverge_target, + }); next_target.unit() } @@ -445,10 +460,10 @@ impl<'a,'tcx> Builder<'a,'tcx> { // ========= // FIXME: should be moved into their own module pub fn panic_bounds_check(&mut self, - block: BasicBlock, - index: Operand<'tcx>, - len: Operand<'tcx>, - span: Span) { + block: BasicBlock, + index: Operand<'tcx>, + len: Operand<'tcx>, + span: Span) { // fn(&(filename: &'static str, line: u32), index: usize, length: usize) -> ! let region = ty::ReStatic; // FIXME(mir-borrowck): use a better region? let func = self.lang_function(lang_items::PanicBoundsCheckFnLangItem); @@ -474,7 +489,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { self.cfg.push_assign(block, scope_id, span, &tuple_ref, // tuple_ref = &tuple; Rvalue::Ref(region, BorrowKind::Shared, tuple)); let cleanup = self.diverge_cleanup(); - self.cfg.terminate(block, TerminatorKind::Call { + self.cfg.terminate(block, scope_id, span, TerminatorKind::Call { func: Operand::Constant(func), args: vec![Operand::Consume(tuple_ref), index, len], destination: None, @@ -516,7 +531,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { self.cfg.push_assign(block, scope_id, span, &tuple_ref, // tuple_ref = &tuple; Rvalue::Ref(region, BorrowKind::Shared, tuple)); let cleanup = self.diverge_cleanup(); - self.cfg.terminate(block, TerminatorKind::Call { + self.cfg.terminate(block, scope_id, span, TerminatorKind::Call { func: Operand::Constant(func), args: vec![Operand::Consume(tuple_ref)], cleanup: cleanup, @@ -575,7 +590,7 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>, earlier_scopes.iter().rev().flat_map(|s| s.cached_block()).next() }); let next = cfg.start_new_block(); - cfg.terminate(block, TerminatorKind::Drop { + cfg.terminate(block, scope.id, drop_data.span, TerminatorKind::Drop { value: drop_data.value.clone(), target: next, unwind: on_diverge @@ -599,12 +614,15 @@ fn build_diverge_scope<'tcx>(tcx: &TyCtxt<'tcx>, let mut last_drop_block = None; for drop_data in scope.drops.iter_mut().rev() { if let Some(cached_block) = drop_data.cached_block { - if let Some((previous_block, previous_value)) = previous { - cfg.terminate(previous_block, TerminatorKind::Drop { - value: previous_value, - target: cached_block, - unwind: None - }); + if let Some((previous_block, previous_span, previous_value)) = previous { + cfg.terminate(previous_block, + scope.id, + previous_span, + TerminatorKind::Drop { + value: previous_value, + target: cached_block, + unwind: None + }); return last_drop_block.unwrap(); } else { return cached_block; @@ -612,23 +630,26 @@ fn build_diverge_scope<'tcx>(tcx: &TyCtxt<'tcx>, } else { let block = cfg.start_new_cleanup_block(); drop_data.cached_block = Some(block); - if let Some((previous_block, previous_value)) = previous { - cfg.terminate(previous_block, TerminatorKind::Drop { - value: previous_value, - target: block, - unwind: None - }); + if let Some((previous_block, previous_span, previous_value)) = previous { + cfg.terminate(previous_block, + scope.id, + previous_span, + TerminatorKind::Drop { + value: previous_value, + target: block, + unwind: None + }); } else { last_drop_block = Some(block); } - previous = Some((block, drop_data.value.clone())); + previous = Some((block, drop_data.span, drop_data.value.clone())); } } // Prepare the end target for this chain. let mut target = target.unwrap_or_else(||{ let b = cfg.start_new_cleanup_block(); - cfg.terminate(b, TerminatorKind::Resume); + cfg.terminate(b, scope.id, DUMMY_SP, TerminatorKind::Resume); // TODO b }); @@ -638,19 +659,25 @@ fn build_diverge_scope<'tcx>(tcx: &TyCtxt<'tcx>, cached_block } else { let into = cfg.start_new_cleanup_block(); - cfg.terminate(into, build_free(tcx, unit_temp, free_data, target)); + cfg.terminate(into, + scope.id, + free_data.span, + build_free(tcx, unit_temp, free_data, target)); free_data.cached_block = Some(into); into } }; - if let Some((previous_block, previous_value)) = previous { + if let Some((previous_block, previous_span, previous_value)) = previous { // Finally, branch into that just-built `target` from the `previous_block`. - cfg.terminate(previous_block, TerminatorKind::Drop { - value: previous_value, - target: target, - unwind: None - }); + cfg.terminate(previous_block, + scope.id, + previous_span, + TerminatorKind::Drop { + value: previous_value, + target: target, + unwind: None + }); last_drop_block.unwrap() } else { // If `previous.is_none()`, there were no drops in this scope – we return the From caac0b969fd888014c3ff80238d8a00c6eae4feb Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 19 Mar 2016 07:13:54 -0400 Subject: [PATCH 06/24] reformat mir text pretty printer --- src/librustc_mir/pretty.rs | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/librustc_mir/pretty.rs b/src/librustc_mir/pretty.rs index b8bb1b4ee1157..0f1891bf37bfa 100644 --- a/src/librustc_mir/pretty.rs +++ b/src/librustc_mir/pretty.rs @@ -16,21 +16,33 @@ use syntax::ast::NodeId; const INDENT: &'static str = " "; /// Write out a human-readable textual representation for the given MIR. -pub fn write_mir_pretty<'a, 't, W, I>(tcx: &ty::TyCtxt<'t>, iter: I, w: &mut W) -> io::Result<()> -where W: Write, I: Iterator)> { - for (&nodeid, mir) in iter { - write_mir_intro(tcx, nodeid, mir, w)?; - // Nodes - for block in mir.all_basic_blocks() { - write_basic_block(block, mir, w)?; - } - writeln!(w, "}}")? +pub fn write_mir_pretty<'a, 'tcx, I>(tcx: &ty::TyCtxt<'tcx>, + iter: I, + w: &mut Write) + -> io::Result<()> + where I: Iterator)>, 'tcx: 'a +{ + for (&node_id, mir) in iter { + write_mir_fn(tcx, node_id, mir, w)?; + } + Ok(()) +} + +pub fn write_mir_fn<'tcx>(tcx: &ty::TyCtxt<'tcx>, + node_id: NodeId, + mir: &Mir<'tcx>, + w: &mut Write) + -> io::Result<()> { + write_mir_intro(tcx, node_id, mir, w)?; + for block in mir.all_basic_blocks() { + write_basic_block(block, mir, w)?; } + writeln!(w, "}}")?; Ok(()) } /// Write out a human-readable textual representation for the given basic block. -fn write_basic_block(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> { +fn write_basic_block(block: BasicBlock, mir: &Mir, w: &mut Write) -> io::Result<()> { let data = mir.basic_block_data(block); // Basic block label at the top. @@ -49,9 +61,8 @@ fn write_basic_block(block: BasicBlock, mir: &Mir, w: &mut W) -> io::R /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its /// local variables (both user-defined bindings and compiler temporaries). -fn write_mir_intro(tcx: &ty::TyCtxt, nid: NodeId, mir: &Mir, w: &mut W) --> io::Result<()> { - +fn write_mir_intro(tcx: &ty::TyCtxt, nid: NodeId, mir: &Mir, w: &mut Write) + -> io::Result<()> { write!(w, "fn {}(", tcx.map.path_to_string(nid))?; // fn argument types. From d32bde3311a035f2a0d7c26cf3170cf98860d701 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 22 Mar 2016 10:08:44 -0400 Subject: [PATCH 07/24] augment MIR pretty printer to print scopes --- src/librustc/mir/repr.rs | 4 +- src/librustc_mir/build/mod.rs | 1 + src/librustc_mir/pretty.rs | 98 +++++++++++++++++++++++++++++++---- 3 files changed, 91 insertions(+), 12 deletions(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index cc95d5946a801..a1688e1464c5a 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -197,7 +197,7 @@ pub struct ArgDecl<'tcx> { /// list of the `Mir`. /// /// (We use a `u32` internally just to save memory.) -#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] pub struct BasicBlock(u32); impl BasicBlock { @@ -668,7 +668,7 @@ impl IndexMut for ScopeDataVec { } } -#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, RustcEncodable, RustcDecodable)] pub struct ScopeId(u32); impl ScopeId { diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 416c078f4a95a..f2def53d4ead0 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -73,6 +73,7 @@ pub struct ScopeAuxiliary { pub postdoms: Vec, } +#[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct Location { /// the location is within this block pub block: BasicBlock, diff --git a/src/librustc_mir/pretty.rs b/src/librustc_mir/pretty.rs index 0f1891bf37bfa..813766d60c6a1 100644 --- a/src/librustc_mir/pretty.rs +++ b/src/librustc_mir/pretty.rs @@ -8,60 +8,138 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use build::Location; use rustc::mir::repr::*; -use rustc::middle::ty; +use rustc::middle::ty::{self, TyCtxt}; +use rustc_data_structures::fnv::FnvHashMap; use std::io::{self, Write}; use syntax::ast::NodeId; +use syntax::codemap::Span; const INDENT: &'static str = " "; /// Write out a human-readable textual representation for the given MIR. -pub fn write_mir_pretty<'a, 'tcx, I>(tcx: &ty::TyCtxt<'tcx>, +pub fn write_mir_pretty<'a, 'tcx, I>(tcx: &TyCtxt<'tcx>, iter: I, w: &mut Write) -> io::Result<()> where I: Iterator)>, 'tcx: 'a { + let no_annotations = FnvHashMap(); for (&node_id, mir) in iter { - write_mir_fn(tcx, node_id, mir, w)?; + write_mir_fn(tcx, node_id, mir, w, &no_annotations)?; } Ok(()) } -pub fn write_mir_fn<'tcx>(tcx: &ty::TyCtxt<'tcx>, +pub enum Annotation { + EnterScope(ScopeId), + ExitScope(ScopeId), +} + +pub fn write_mir_fn<'tcx>(tcx: &TyCtxt<'tcx>, node_id: NodeId, mir: &Mir<'tcx>, - w: &mut Write) + w: &mut Write, + annotations: &FnvHashMap>) -> io::Result<()> { write_mir_intro(tcx, node_id, mir, w)?; for block in mir.all_basic_blocks() { - write_basic_block(block, mir, w)?; + write_basic_block(tcx, block, mir, w, annotations)?; + } + + // construct a scope tree + let mut scope_tree: FnvHashMap, Vec> = FnvHashMap(); + for (index, scope_data) in mir.scopes.vec.iter().enumerate() { + scope_tree.entry(scope_data.parent_scope) + .or_insert(vec![]) + .push(ScopeId::new(index)); } + write_scope_tree(tcx, mir, &scope_tree, w, None, 1)?; + writeln!(w, "}}")?; Ok(()) } /// Write out a human-readable textual representation for the given basic block. -fn write_basic_block(block: BasicBlock, mir: &Mir, w: &mut Write) -> io::Result<()> { +fn write_basic_block(tcx: &TyCtxt, + block: BasicBlock, + mir: &Mir, + w: &mut Write, + annotations: &FnvHashMap>) + -> io::Result<()> { let data = mir.basic_block_data(block); // Basic block label at the top. writeln!(w, "\n{}{:?}: {{", INDENT, block)?; // List of statements in the middle. + let mut current_location = Location { block: block, statement_index: 0 }; for statement in &data.statements { - writeln!(w, "{0}{0}{1:?};", INDENT, statement)?; + if let Some(ref annotations) = annotations.get(¤t_location) { + for annotation in annotations.iter() { + match *annotation { + Annotation::EnterScope(id) => + writeln!(w, "{0}{0}// Enter Scope({1})", + INDENT, id.index())?, + Annotation::ExitScope(id) => + writeln!(w, "{0}{0}// Exit Scope({1})", + INDENT, id.index())?, + } + } + } + + writeln!(w, "{0}{0}{1:?}; // {2}", + INDENT, + statement, + comment(tcx, statement.scope, statement.span))?; + + current_location.statement_index += 1; } // Terminator at the bottom. - writeln!(w, "{0}{0}{1:?};", INDENT, data.terminator())?; + writeln!(w, "{0}{0}{1:?}; // {2}", + INDENT, + data.terminator(), + comment(tcx, data.terminator().scope, data.terminator().span))?; writeln!(w, "{}}}", INDENT) } +fn comment(tcx: &TyCtxt, + scope: ScopeId, + span: Span) + -> String { + format!("Scope({}) at {}", scope.index(), tcx.sess.codemap().span_to_string(span)) +} + +fn write_scope_tree(tcx: &TyCtxt, + mir: &Mir, + scope_tree: &FnvHashMap, Vec>, + w: &mut Write, + parent: Option, + depth: usize) + -> io::Result<()> { + for &child in scope_tree.get(&parent).unwrap_or(&vec![]) { + let indent = depth * INDENT.len(); + let data = &mir.scopes[child]; + assert_eq!(data.parent_scope, parent); + writeln!(w, "{0:1$}Scope({2}) {{", "", indent, child.index())?; + let indent = indent + INDENT.len(); + if let Some(parent) = parent { + writeln!(w, "{0:1$}Parent: Scope({2})", "", indent, parent.index())?; + } + writeln!(w, "{0:1$}Extent: {2:?}", + "", indent, + tcx.region_maps.code_extent_data(data.extent))?; + write_scope_tree(tcx, mir, scope_tree, w, Some(child), depth + 1)?; + } + Ok(()) +} + /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its /// local variables (both user-defined bindings and compiler temporaries). -fn write_mir_intro(tcx: &ty::TyCtxt, nid: NodeId, mir: &Mir, w: &mut Write) +fn write_mir_intro(tcx: &TyCtxt, nid: NodeId, mir: &Mir, w: &mut Write) -> io::Result<()> { write!(w, "fn {}(", tcx.map.path_to_string(nid))?; From 0d93989cf5dd479a097a4d58984a482920982aa5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 22 Mar 2016 11:52:34 -0400 Subject: [PATCH 08/24] adjust pretty printer to print scopes / auxiliary --- src/librustc_mir/pretty.rs | 48 ++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/src/librustc_mir/pretty.rs b/src/librustc_mir/pretty.rs index 813766d60c6a1..650f2d5bcb802 100644 --- a/src/librustc_mir/pretty.rs +++ b/src/librustc_mir/pretty.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use build::Location; +use build::{Location, ScopeAuxiliary}; use rustc::mir::repr::*; use rustc::middle::ty::{self, TyCtxt}; use rustc_data_structures::fnv::FnvHashMap; @@ -25,14 +25,13 @@ pub fn write_mir_pretty<'a, 'tcx, I>(tcx: &TyCtxt<'tcx>, -> io::Result<()> where I: Iterator)>, 'tcx: 'a { - let no_annotations = FnvHashMap(); for (&node_id, mir) in iter { - write_mir_fn(tcx, node_id, mir, w, &no_annotations)?; + write_mir_fn(tcx, node_id, mir, w, None)?; } Ok(()) } -pub enum Annotation { +enum Annotation { EnterScope(ScopeId), ExitScope(ScopeId), } @@ -41,21 +40,39 @@ pub fn write_mir_fn<'tcx>(tcx: &TyCtxt<'tcx>, node_id: NodeId, mir: &Mir<'tcx>, w: &mut Write, - annotations: &FnvHashMap>) + auxiliary: Option<&Vec>) -> io::Result<()> { + // compute scope/entry exit annotations + let mut annotations = FnvHashMap(); + if let Some(auxiliary) = auxiliary { + for (index, auxiliary) in auxiliary.iter().enumerate() { + let scope_id = ScopeId::new(index); + + annotations.entry(auxiliary.dom) + .or_insert(vec![]) + .push(Annotation::EnterScope(scope_id)); + + for &loc in &auxiliary.postdoms { + annotations.entry(loc) + .or_insert(vec![]) + .push(Annotation::ExitScope(scope_id)); + } + } + } + write_mir_intro(tcx, node_id, mir, w)?; for block in mir.all_basic_blocks() { - write_basic_block(tcx, block, mir, w, annotations)?; + write_basic_block(tcx, block, mir, w, &annotations)?; } - // construct a scope tree + // construct a scope tree and write it out let mut scope_tree: FnvHashMap, Vec> = FnvHashMap(); for (index, scope_data) in mir.scopes.vec.iter().enumerate() { scope_tree.entry(scope_data.parent_scope) .or_insert(vec![]) .push(ScopeId::new(index)); } - write_scope_tree(tcx, mir, &scope_tree, w, None, 1)?; + write_scope_tree(tcx, mir, auxiliary, &scope_tree, w, None, 1)?; writeln!(w, "}}")?; Ok(()) @@ -115,6 +132,7 @@ fn comment(tcx: &TyCtxt, fn write_scope_tree(tcx: &TyCtxt, mir: &Mir, + auxiliary: Option<&Vec>, scope_tree: &FnvHashMap, Vec>, w: &mut Write, parent: Option, @@ -125,14 +143,20 @@ fn write_scope_tree(tcx: &TyCtxt, let data = &mir.scopes[child]; assert_eq!(data.parent_scope, parent); writeln!(w, "{0:1$}Scope({2}) {{", "", indent, child.index())?; + let indent = indent + INDENT.len(); if let Some(parent) = parent { writeln!(w, "{0:1$}Parent: Scope({2})", "", indent, parent.index())?; } - writeln!(w, "{0:1$}Extent: {2:?}", - "", indent, - tcx.region_maps.code_extent_data(data.extent))?; - write_scope_tree(tcx, mir, scope_tree, w, Some(child), depth + 1)?; + + if let Some(auxiliary) = auxiliary { + let extent = auxiliary[child.index()].extent; + let data = tcx.region_maps.code_extent_data(extent); + writeln!(w, "{0:1$}Extent: {2:?}", "", indent, data)?; + } + + write_scope_tree(tcx, mir, auxiliary, scope_tree, w, + Some(child), depth + 1)?; } Ok(()) } From a61c1759c7297f09955409fffaa162c7a21f89a7 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 22 Mar 2016 16:05:28 -0400 Subject: [PATCH 09/24] allow dumping intermediate IR with -Z dump-mir --- src/librustc/session/config.rs | 2 + src/librustc_mir/build/mod.rs | 12 +++++ src/librustc_mir/mir_map.rs | 10 +++- src/librustc_mir/pretty.rs | 53 +++++++++++++++++++++- src/librustc_mir/transform/simplify_cfg.rs | 3 ++ 5 files changed, 78 insertions(+), 2 deletions(-) diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 17f70b2d8dc66..bf532d9ccf9ed 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -663,6 +663,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "print the result of the translation item collection pass"), mir_opt_level: Option = (None, parse_opt_uint, "set the MIR optimization level (0-3)"), + dump_mir: Option = (None, parse_opt_string, + "dump MIR state at various points in translation"), orbit: bool = (false, parse_bool, "get MIR where it belongs - everywhere; most importantly, in orbit"), } diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index f2def53d4ead0..2da2a15cbd709 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -173,6 +173,18 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>, builder.cfg.terminate(END_BLOCK, arg_scope_id, span, TerminatorKind::Return); + assert!( + builder.cfg.basic_blocks + .iter() + .enumerate() + .all(|(index, block)| { + if block.terminator.is_none() { + panic!("no terminator on block {:?} in {:?}", + index, argument_extent) + } + true + })); + MirPlusPlus { mir: Mir { basic_blocks: builder.cfg.basic_blocks, diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 71037d1f08070..242d2506c82f1 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -22,6 +22,7 @@ extern crate rustc_front; use build::{self, MirPlusPlus}; use rustc::dep_graph::DepNode; use rustc::mir::repr::Mir; +use pretty; use hair::cx::Cx; use rustc::mir::mir_map::MirMap; @@ -182,7 +183,7 @@ fn build_mir<'a,'tcx:'a>(cx: Cx<'a,'tcx>, let parameter_scope = cx.tcx().region_maps.lookup_code_extent( CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body.id }); - let MirPlusPlus { mut mir, scope_auxiliary: _ } = + let MirPlusPlus { mut mir, scope_auxiliary } = build::construct(cx, span, implicit_arg_tys, @@ -201,6 +202,13 @@ fn build_mir<'a,'tcx:'a>(cx: Cx<'a,'tcx>, _ => {} } + pretty::dump_mir(cx.tcx(), + "mir_map", + &0, + fn_id, + &mir, + Some(&scope_auxiliary)); + Ok(mir) } diff --git a/src/librustc_mir/pretty.rs b/src/librustc_mir/pretty.rs index 650f2d5bcb802..d8cfd8a88cf9b 100644 --- a/src/librustc_mir/pretty.rs +++ b/src/librustc_mir/pretty.rs @@ -12,12 +12,63 @@ use build::{Location, ScopeAuxiliary}; use rustc::mir::repr::*; use rustc::middle::ty::{self, TyCtxt}; use rustc_data_structures::fnv::FnvHashMap; +use std::fmt::Display; +use std::fs; use std::io::{self, Write}; use syntax::ast::NodeId; use syntax::codemap::Span; const INDENT: &'static str = " "; +/// If the session is properly configured, dumps a human-readable +/// representation of the mir into: +/// +/// ``` +/// rustc.node.. +/// ``` +/// +/// Output from this function is controlled by passing `-Z dump-mir=`, +/// where `` takes the following forms: +/// +/// - `all` -- dump MIR for all fns, all passes, all everything +/// - `substring1&substring2,...` -- `&`-separated list of substrings +/// that can appear in the pass-name or the `item_path_str` for the given +/// node-id. If any one of the substrings match, the data is dumped out. +pub fn dump_mir<'a, 'tcx>(tcx: &TyCtxt<'tcx>, + pass_name: &str, + disambiguator: &Display, + node_id: NodeId, + mir: &Mir<'tcx>, + auxiliary: Option<&Vec>) { + let filters = match tcx.sess.opts.debugging_opts.dump_mir { + None => return, + Some(ref filters) => filters, + }; + let node_path = tcx.item_path_str(tcx.map.local_def_id(node_id)); + let is_matched = + filters.split("&") + .any(|filter| { + filter == "all" || + pass_name.contains(filter) || + node_path.contains(filter) + }); + if !is_matched { + return; + } + + let file_name = format!("rustc.node{}.{}.{}.mir", + node_id, pass_name, disambiguator); + let _ = fs::File::create(&file_name).and_then(|mut file| { + try!(writeln!(file, "// MIR for `{}`", node_path)); + try!(writeln!(file, "// node_id = {}", node_id)); + try!(writeln!(file, "// pass_name = {}", pass_name)); + try!(writeln!(file, "// disambiguator = {}", disambiguator)); + try!(writeln!(file, "")); + try!(write_mir_fn(tcx, node_id, mir, &mut file, auxiliary)); + Ok(()) + }); +} + /// Write out a human-readable textual representation for the given MIR. pub fn write_mir_pretty<'a, 'tcx, I>(tcx: &TyCtxt<'tcx>, iter: I, @@ -117,7 +168,7 @@ fn write_basic_block(tcx: &TyCtxt, // Terminator at the bottom. writeln!(w, "{0}{0}{1:?}; // {2}", INDENT, - data.terminator(), + data.terminator().kind, comment(tcx, data.terminator().scope, data.terminator().span))?; writeln!(w, "{}}}", INDENT) diff --git a/src/librustc_mir/transform/simplify_cfg.rs b/src/librustc_mir/transform/simplify_cfg.rs index 4e192095043eb..d1f9a5bd259aa 100644 --- a/src/librustc_mir/transform/simplify_cfg.rs +++ b/src/librustc_mir/transform/simplify_cfg.rs @@ -30,6 +30,7 @@ impl SimplifyCfg { let mut seen: Vec = Vec::with_capacity(8); while mir.basic_block_data(target).statements.is_empty() { + debug!("final_target: target={:?}", target); match mir.basic_block_data(target).terminator().kind { TerminatorKind::Goto { target: next } => { if seen.contains(&next) { @@ -51,6 +52,8 @@ impl SimplifyCfg { let mut terminator = mir.basic_block_data_mut(bb).terminator.take() .expect("invalid terminator state"); + debug!("remove_goto_chains: bb={:?} terminator={:?}", bb, terminator); + for target in terminator.successors_mut() { let new_target = match final_target(mir, *target) { Some(new_target) => new_target, From f976e222e97fdcb8ab66634c8910ce524f6804c9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 22 Mar 2016 16:19:44 -0400 Subject: [PATCH 10/24] fix bug in `simplify_cfg` with inf. loops --- src/librustc_mir/transform/simplify_cfg.rs | 26 +++++++++++++++------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/librustc_mir/transform/simplify_cfg.rs b/src/librustc_mir/transform/simplify_cfg.rs index d1f9a5bd259aa..21b1d022fda60 100644 --- a/src/librustc_mir/transform/simplify_cfg.rs +++ b/src/librustc_mir/transform/simplify_cfg.rs @@ -12,6 +12,7 @@ use rustc::middle::const_eval::ConstVal; use rustc::middle::ty::TyCtxt; use rustc::mir::repr::*; use rustc::mir::transform::{MirPass, Pass}; +use pretty; use syntax::ast::NodeId; use super::remove_dead_blocks::RemoveDeadBlocks; @@ -30,16 +31,22 @@ impl SimplifyCfg { let mut seen: Vec = Vec::with_capacity(8); while mir.basic_block_data(target).statements.is_empty() { - debug!("final_target: target={:?}", target); - match mir.basic_block_data(target).terminator().kind { - TerminatorKind::Goto { target: next } => { - if seen.contains(&next) { - return None; + // NB -- terminator may have been swapped with `None` + // below, in which case we have a cycle and just want + // to stop + if let Some(ref terminator) = mir.basic_block_data(target).terminator { + match terminator.kind { + TerminatorKind::Goto { target: next } => { + if seen.contains(&next) { + return None; + } + seen.push(next); + target = next; } - seen.push(next); - target = next; + _ => break } - _ => break + } else { + break } } @@ -106,8 +113,11 @@ impl SimplifyCfg { impl<'tcx> MirPass<'tcx> for SimplifyCfg { fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, id: NodeId, mir: &mut Mir<'tcx>) { + let mut counter = 0; let mut changed = true; while changed { + pretty::dump_mir(tcx, "simplify_cfg", &counter, id, mir, None); + counter += 1; changed = self.simplify_branches(mir); changed |= self.remove_goto_chains(mir); RemoveDeadBlocks.run_pass(tcx, id, mir); From cb04e495dc7a34cc708246dad7bb8b844216fd3e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 22 Mar 2016 20:39:29 -0400 Subject: [PATCH 11/24] rewrite drop code This was triggered by me wanting to address a use of DUMMY_SP, but actually I'm not sure what would be a better span -- I guess the span for the function as a whole. --- src/librustc_mir/build/expr/into.rs | 3 +- src/librustc_mir/build/matches/test.rs | 3 +- src/librustc_mir/build/scope.rs | 162 ++++++++++++------------- 3 files changed, 85 insertions(+), 83 deletions(-) diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 9992e904dea16..4c9e2c0c5fa6d 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -256,7 +256,8 @@ impl<'a,'tcx> Builder<'a,'tcx> { block = match value { Some(value) => unpack!(this.into(&Lvalue::ReturnPointer, block, value)), None => { - this.cfg.push_assign_unit(block, scope_id, expr_span, &Lvalue::ReturnPointer); + this.cfg.push_assign_unit(block, scope_id, + expr_span, &Lvalue::ReturnPointer); block } }; diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 3e374c07b46b1..3211e5849a06c 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -272,7 +272,8 @@ impl<'a,'tcx> Builder<'a,'tcx> { let (actual, result) = (self.temp(usize_ty), self.temp(bool_ty)); // actual = len(lvalue) - self.cfg.push_assign(block, scope_id, test.span, &actual, Rvalue::Len(lvalue.clone())); + self.cfg.push_assign(block, scope_id, test.span, + &actual, Rvalue::Len(lvalue.clone())); // expected = let expected = self.push_usize(block, scope_id, test.span, len); diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index e7066ff083e24..5aeaef06b8910 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -113,12 +113,20 @@ pub struct Scope<'tcx> { // This is expected to go away once `box EXPR` becomes a sugar for placement protocol and gets // desugared in some earlier stage. free: Option>, + + /// The cached block for the cleanups-on-diverge path. This block + /// contains a block that will just do a RESUME to an appropriate + /// place. This block does not execute any of the drops or free: + /// each of those has their own cached-blocks, which will branch + /// to this point. + cached_block: Option } struct DropData<'tcx> { span: Span, value: Lvalue<'tcx>, // NB: per-drop “cache” is necessary for the build_scope_drops function below. + /// The cached block for the cleanups-on-diverge path. This block contains code to run the /// current drop and all the preceding drops (i.e. those having lower index in Drop’s /// Scope drop array) @@ -127,10 +135,13 @@ struct DropData<'tcx> { struct FreeData<'tcx> { span: Span, + /// Lvalue containing the allocated box. value: Lvalue<'tcx>, + /// type of item for which the box was allocated for (i.e. the T in Box). item_ty: Ty<'tcx>, + /// The cached block containing code to run the free. The block will also execute all the drops /// in the scope. cached_block: Option @@ -155,6 +166,7 @@ impl<'tcx> Scope<'tcx> { /// Should always be run for all inner scopes when a drop is pushed into some scope enclosing a /// larger extent of code. fn invalidate_cache(&mut self) { + self.cached_block = None; for dropdata in &mut self.drops { dropdata.cached_block = None; } @@ -234,7 +246,8 @@ impl<'a,'tcx> Builder<'a,'tcx> { id: id, extent: extent, drops: vec![], - free: None + free: None, + cached_block: None, }); self.scope_auxiliary.push(ScopeAuxiliary { extent: extent, @@ -288,7 +301,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { block)); if let Some(ref free_data) = scope.free { let next = self.cfg.start_new_block(); - let free = build_free(self.hir.tcx(), tmp.clone(), free_data, next); + let free = build_free(self.hir.tcx(), &tmp, free_data, next); self.cfg.terminate(block, scope.id, span, free); block = next; } @@ -419,20 +432,16 @@ impl<'a,'tcx> Builder<'a,'tcx> { } let unit_temp = self.get_unit_temp(); let Builder { ref mut hir, ref mut cfg, ref mut scopes, .. } = *self; - let mut next_block = None; // Given an array of scopes, we generate these from the outermost scope to the innermost // one. Thus for array [S0, S1, S2] with corresponding cleanup blocks [B0, B1, B2], we will // generate B0 <- B1 <- B2 in left-to-right order. Control flow of the generated blocks // always ends up at a block with the Resume terminator. - for scope in scopes.iter_mut().filter(|s| !s.drops.is_empty() || s.free.is_some()) { - next_block = Some(build_diverge_scope(hir.tcx(), - cfg, - unit_temp.clone(), - scope, - next_block)); + if scopes.iter().any(|scope| !scope.drops.is_empty() || scope.free.is_some()) { + Some(build_diverge_scope(hir.tcx(), cfg, &unit_temp, scopes)) + } else { + None } - scopes.iter().rev().flat_map(|x| x.cached_block()).next() } /// Utility function for *non*-scope code to build their own drops @@ -525,8 +534,9 @@ impl<'a,'tcx> Builder<'a,'tcx> { // FIXME: We should have this as a constant, rather than a stack variable (to not pollute // icache with cold branch code), however to achieve that we either have to rely on rvalue // promotion or have some way, in MIR, to create constants. - self.cfg.push_assign(block, scope_id, span, &tuple, // tuple = (message_arg, file_arg, line_arg); + self.cfg.push_assign(block, scope_id, span, &tuple, // [1] Rvalue::Aggregate(AggregateKind::Tuple, elems)); + // [1] tuple = (message_arg, file_arg, line_arg); // FIXME: is this region really correct here? self.cfg.push_assign(block, scope_id, span, &tuple_ref, // tuple_ref = &tuple; Rvalue::Ref(region, BorrowKind::Shared, tuple)); @@ -602,58 +612,42 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>, fn build_diverge_scope<'tcx>(tcx: &TyCtxt<'tcx>, cfg: &mut CFG<'tcx>, - unit_temp: Lvalue<'tcx>, - scope: &mut Scope<'tcx>, - target: Option) - -> BasicBlock { - debug_assert!(!scope.drops.is_empty() || scope.free.is_some()); - - // First, we build the drops, iterating the drops array in reverse. We do that so that as soon - // as we find a `cached_block`, we know that we’re finished and don’t need to do anything else. - let mut previous = None; - let mut last_drop_block = None; - for drop_data in scope.drops.iter_mut().rev() { - if let Some(cached_block) = drop_data.cached_block { - if let Some((previous_block, previous_span, previous_value)) = previous { - cfg.terminate(previous_block, - scope.id, - previous_span, - TerminatorKind::Drop { - value: previous_value, - target: cached_block, - unwind: None - }); - return last_drop_block.unwrap(); - } else { - return cached_block; - } - } else { - let block = cfg.start_new_cleanup_block(); - drop_data.cached_block = Some(block); - if let Some((previous_block, previous_span, previous_value)) = previous { - cfg.terminate(previous_block, - scope.id, - previous_span, - TerminatorKind::Drop { - value: previous_value, - target: block, - unwind: None - }); - } else { - last_drop_block = Some(block); - } - previous = Some((block, drop_data.span, drop_data.value.clone())); - } - } - - // Prepare the end target for this chain. - let mut target = target.unwrap_or_else(||{ - let b = cfg.start_new_cleanup_block(); - cfg.terminate(b, scope.id, DUMMY_SP, TerminatorKind::Resume); // TODO - b - }); + unit_temp: &Lvalue<'tcx>, + scopes: &mut [Scope<'tcx>]) + -> BasicBlock +{ + assert!(scopes.len() >= 1); + + // Build up the drops in **reverse** order. The end result will + // look like: + // + // [drops[n]] -...-> [drops[0]] -> [Free] -> [scopes[..n-1]] + // | | + // +------------------------------------+ + // code for scopes[n] + // + // The code in this function reads from right to left. At each + // point, we check for cached blocks representing the + // remainder. If everything is cached, we'll just walk right to + // left reading the cached results but never created anything. + + // To start, translate scopes[1..]. + let (scope, earlier_scopes) = scopes.split_last_mut().unwrap(); + let mut target = if let Some(cached_block) = scope.cached_block { + cached_block + } else if earlier_scopes.is_empty() { + // Diverging from the root scope creates a RESUME terminator. + // FIXME what span to use here? + let resumeblk = cfg.start_new_cleanup_block(); + cfg.terminate(resumeblk, scope.id, DUMMY_SP, TerminatorKind::Resume); + resumeblk + } else { + // Diverging from any other scope chains up to the previous scope. + build_diverge_scope(tcx, cfg, unit_temp, earlier_scopes) + }; + scope.cached_block = Some(target); - // Then, build the free branching into the prepared target. + // Next, build up any free. if let Some(ref mut free_data) = scope.free { target = if let Some(cached_block) = free_data.cached_block { cached_block @@ -665,29 +659,35 @@ fn build_diverge_scope<'tcx>(tcx: &TyCtxt<'tcx>, build_free(tcx, unit_temp, free_data, target)); free_data.cached_block = Some(into); into - } - }; + }; + } - if let Some((previous_block, previous_span, previous_value)) = previous { - // Finally, branch into that just-built `target` from the `previous_block`. - cfg.terminate(previous_block, - scope.id, - previous_span, - TerminatorKind::Drop { - value: previous_value, - target: target, - unwind: None - }); - last_drop_block.unwrap() - } else { - // If `previous.is_none()`, there were no drops in this scope – we return the - // target. - target + // 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 { + target = if let Some(cached_block) = drop_data.cached_block { + cached_block + } else { + let block = cfg.start_new_cleanup_block(); + cfg.terminate(block, + scope.id, + drop_data.span, + TerminatorKind::Drop { + value: drop_data.value.clone(), + target: target, + unwind: None + }); + drop_data.cached_block = Some(block); + block + }; } + + target } fn build_free<'tcx>(tcx: &TyCtxt<'tcx>, - unit_temp: Lvalue<'tcx>, + unit_temp: &Lvalue<'tcx>, data: &FreeData<'tcx>, target: BasicBlock) -> TerminatorKind<'tcx> { @@ -707,7 +707,7 @@ fn build_free<'tcx>(tcx: &TyCtxt<'tcx>, } }), args: vec![Operand::Consume(data.value.clone())], - destination: Some((unit_temp, target)), + destination: Some((unit_temp.clone(), target)), cleanup: None } } From f66fd8972f16f7a38f57c9953f1f6972a917c905 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 22 Mar 2016 20:41:07 -0400 Subject: [PATCH 12/24] replace DUMMY_SP on resume with span from fn --- src/librustc_mir/build/mod.rs | 3 +++ src/librustc_mir/build/scope.rs | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 2da2a15cbd709..d52d750b12c5b 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -22,6 +22,8 @@ pub struct Builder<'a, 'tcx: 'a> { hir: Cx<'a, 'tcx>, cfg: CFG<'tcx>, + fn_span: Span, + // the current set of scopes, updated as we traverse; // see the `scope` module for more details scopes: Vec>, @@ -147,6 +149,7 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>, let mut builder = Builder { hir: hir, cfg: cfg, + fn_span: span, scopes: vec![], scope_data_vec: ScopeDataVec::new(), scope_auxiliary: vec![], diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 5aeaef06b8910..cf09333d4acfb 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -438,7 +438,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { // generate B0 <- B1 <- B2 in left-to-right order. Control flow of the generated blocks // always ends up at a block with the Resume terminator. if scopes.iter().any(|scope| !scope.drops.is_empty() || scope.free.is_some()) { - Some(build_diverge_scope(hir.tcx(), cfg, &unit_temp, scopes)) + Some(build_diverge_scope(hir.tcx(), self.fn_span, cfg, &unit_temp, scopes)) } else { None } @@ -611,6 +611,7 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>, } fn build_diverge_scope<'tcx>(tcx: &TyCtxt<'tcx>, + fn_span: Span, cfg: &mut CFG<'tcx>, unit_temp: &Lvalue<'tcx>, scopes: &mut [Scope<'tcx>]) @@ -639,11 +640,11 @@ fn build_diverge_scope<'tcx>(tcx: &TyCtxt<'tcx>, // Diverging from the root scope creates a RESUME terminator. // FIXME what span to use here? let resumeblk = cfg.start_new_cleanup_block(); - cfg.terminate(resumeblk, scope.id, DUMMY_SP, TerminatorKind::Resume); + cfg.terminate(resumeblk, scope.id, fn_span, TerminatorKind::Resume); resumeblk } else { // Diverging from any other scope chains up to the previous scope. - build_diverge_scope(tcx, cfg, unit_temp, earlier_scopes) + build_diverge_scope(tcx, fn_span, cfg, unit_temp, earlier_scopes) }; scope.cached_block = Some(target); From 14a5657a9a22580aecdbcd72fdea35e4c30f36a5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 23 Mar 2016 04:19:16 -0400 Subject: [PATCH 13/24] Rename `MirPlusPlus` to `MirAndScopeAuxiliary` --- src/librustc_mir/build/mod.rs | 6 +++--- src/librustc_mir/mir_map.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index d52d750b12c5b..58b2b92b7960a 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -85,7 +85,7 @@ pub struct Location { pub statement_index: usize, } -pub struct MirPlusPlus<'tcx> { +pub struct MirAndScopeAuxiliary<'tcx> { pub mir: Mir<'tcx>, pub scope_auxiliary: Vec, } @@ -143,7 +143,7 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>, argument_extent: CodeExtent, return_ty: FnOutput<'tcx>, ast_block: &'tcx hir::Block) - -> MirPlusPlus<'tcx> { + -> MirAndScopeAuxiliary<'tcx> { let cfg = CFG { basic_blocks: vec![] }; let mut builder = Builder { @@ -188,7 +188,7 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>, true })); - MirPlusPlus { + MirAndScopeAuxiliary { mir: Mir { basic_blocks: builder.cfg.basic_blocks, scopes: builder.scope_data_vec, diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 242d2506c82f1..276f7d0f46a75 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -19,7 +19,7 @@ extern crate syntax; extern crate rustc_front; -use build::{self, MirPlusPlus}; +use build::{self, MirAndScopeAuxiliary}; use rustc::dep_graph::DepNode; use rustc::mir::repr::Mir; use pretty; @@ -183,7 +183,7 @@ fn build_mir<'a,'tcx:'a>(cx: Cx<'a,'tcx>, let parameter_scope = cx.tcx().region_maps.lookup_code_extent( CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body.id }); - let MirPlusPlus { mut mir, scope_auxiliary } = + let MirAndScopeAuxiliary { mut mir, scope_auxiliary } = build::construct(cx, span, implicit_arg_tys, From a2bab6f3e1f8b830b311e30a5a154a351a29e001 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 23 Mar 2016 04:21:02 -0400 Subject: [PATCH 14/24] Address nit: use doc-comments for fields of VarDecl --- src/librustc/mir/repr.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index a1688e1464c5a..2931af2936f51 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -156,11 +156,21 @@ pub enum BorrowKind { /// decl, a let, etc. #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub struct VarDecl<'tcx> { + /// `let mut x` vs `let x` pub mutability: Mutability, + + /// name that user gave the variable; not that, internally, + /// mir references variables by index pub name: Name, + + /// type inferred for this variable (`let x: ty = ...`) pub ty: Ty<'tcx>, - pub scope: ScopeId, // scope in which variable was declared - pub span: Span, // span where variable was declared + + /// scope in which variable was declared + pub scope: ScopeId, + + /// span where variable was declared + pub span: Span, } /// A "temp" is a temporary that we place on the stack. They are From c1a53a60e775b3eefd315ed240db6a7589266920 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 23 Mar 2016 04:24:42 -0400 Subject: [PATCH 15/24] Address nit: doc-comments on fields --- src/librustc_mir/build/scope.rs | 36 ++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index cf09333d4acfb..bfc1028afbe6d 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -98,20 +98,24 @@ use rustc::middle::const_eval::ConstVal; use rustc_const_eval::ConstInt; pub struct Scope<'tcx> { - // the scope-id within the scope_data_vec + /// the scope-id within the scope_data_vec id: ScopeId, extent: CodeExtent, drops: Vec>, - // A scope may only have one associated free, because: - // 1. We require a `free` to only be scheduled in the scope of `EXPR` in `box EXPR`; - // 2. It only makes sense to have it translated into the diverge-path. - // - // This kind of drop will be run *after* all the regular drops scheduled onto this scope, - // because drops may have dependencies on the allocated memory. - // - // This is expected to go away once `box EXPR` becomes a sugar for placement protocol and gets - // desugared in some earlier stage. + /// A scope may only have one associated free, because: + /// + /// 1. We require a `free` to only be scheduled in the scope of + /// `EXPR` in `box EXPR`; + /// 2. It only makes sense to have it translated into the diverge-path. + /// + /// This kind of drop will be run *after* all the regular drops + /// scheduled onto this scope, because drops may have dependencies + /// on the allocated memory. + /// + /// This is expected to go away once `box EXPR` becomes a sugar + /// for placement protocol and gets desugared in some earlier + /// stage. free: Option>, /// The cached block for the cleanups-on-diverge path. This block @@ -123,17 +127,21 @@ pub struct Scope<'tcx> { } struct DropData<'tcx> { + /// span where drop obligation was incurred (typically where lvalue was declared) span: Span, + + /// lvalue to drop value: Lvalue<'tcx>, - // NB: per-drop “cache” is necessary for the build_scope_drops function below. - /// The cached block for the cleanups-on-diverge path. This block contains code to run the - /// current drop and all the preceding drops (i.e. those having lower index in Drop’s - /// Scope drop array) + /// The cached block for the cleanups-on-diverge path. This block + /// contains code to run the current drop and all the preceding + /// drops (i.e. those having lower index in Drop’s Scope drop + /// array) cached_block: Option } struct FreeData<'tcx> { + /// span where free obligation was incurred span: Span, /// Lvalue containing the allocated box. From b3d2059b08b33edaa82e200bb433b0324f4bc5e5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 23 Mar 2016 04:25:09 -0400 Subject: [PATCH 16/24] Address nit: block.unit() --- src/librustc_mir/build/scope.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index bfc1028afbe6d..baacc8ca9a9a0 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -282,7 +282,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { self.scope_auxiliary[scope.id.index()] .postdoms .push(self.cfg.current_location(block)); - block.and(()) + block.unit() } From 70d0123082913e244739d64692eec7063b9c79c5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 23 Mar 2016 04:59:44 -0400 Subject: [PATCH 17/24] Address nit: Remove `ScopedDataVec` newtype --- src/librustc/mir/repr.rs | 21 +++++---------------- src/librustc_mir/build/mod.rs | 6 +++--- src/librustc_mir/build/scope.rs | 6 +++--- src/librustc_mir/pretty.rs | 2 +- src/librustc_trans/trans/mir/block.rs | 1 - 5 files changed, 12 insertions(+), 24 deletions(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 2931af2936f51..4022d762aec34 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -34,7 +34,7 @@ pub struct Mir<'tcx> { /// List of lexical scopes; these are referenced by statements and /// used (eventually) for debuginfo. Indexed by a `ScopeId`. - pub scopes: ScopeDataVec, + pub scopes: Vec, /// Return type of the function. pub return_ty: FnOutput<'tcx>, @@ -651,30 +651,19 @@ impl<'tcx> Debug for Lvalue<'tcx> { /////////////////////////////////////////////////////////////////////////// // Scopes -#[derive(Clone, Debug, RustcEncodable, RustcDecodable)] -pub struct ScopeDataVec { - pub vec: Vec -} - -impl ScopeDataVec { - pub fn new() -> Self { - ScopeDataVec { vec: Vec::new() } - } -} - -impl Index for ScopeDataVec { +impl Index for Vec { type Output = ScopeData; #[inline] fn index(&self, index: ScopeId) -> &ScopeData { - &self.vec[index.index()] + &self[index.index()] } } -impl IndexMut for ScopeDataVec { +impl IndexMut for Vec { #[inline] fn index_mut(&mut self, index: ScopeId) -> &mut ScopeData { - &mut self.vec[index.index()] + &mut self[index.index()] } } diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 58b2b92b7960a..056d74ffe1f13 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -41,7 +41,7 @@ pub struct Builder<'a, 'tcx: 'a> { // the vector of all scopes that we have created thus far; // we track this for debuginfo later - scope_data_vec: ScopeDataVec, + scope_datas: Vec, var_decls: Vec>, var_indices: FnvHashMap, @@ -151,7 +151,7 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>, cfg: cfg, fn_span: span, scopes: vec![], - scope_data_vec: ScopeDataVec::new(), + scope_datas: vec![], scope_auxiliary: vec![], loop_scopes: vec![], temp_decls: vec![], @@ -191,7 +191,7 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>, MirAndScopeAuxiliary { mir: Mir { basic_blocks: builder.cfg.basic_blocks, - scopes: builder.scope_data_vec, + scopes: builder.scope_datas, var_decls: builder.var_decls, arg_decls: arg_decls, temp_decls: builder.temp_decls, diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index baacc8ca9a9a0..54830e6c2257d 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -98,7 +98,7 @@ use rustc::middle::const_eval::ConstVal; use rustc_const_eval::ConstInt; pub struct Scope<'tcx> { - /// the scope-id within the scope_data_vec + /// the scope-id within the scope_datas id: ScopeId, extent: CodeExtent, drops: Vec>, @@ -246,8 +246,8 @@ impl<'a,'tcx> Builder<'a,'tcx> { pub fn push_scope(&mut self, extent: CodeExtent, entry: BasicBlock) -> ScopeId { debug!("push_scope({:?})", extent); let parent_id = self.scopes.last().map(|s| s.id); - let id = ScopeId::new(self.scope_data_vec.vec.len()); - self.scope_data_vec.vec.push(ScopeData { + let id = ScopeId::new(self.scope_datas.len()); + self.scope_datas.push(ScopeData { parent_scope: parent_id, }); self.scopes.push(Scope { diff --git a/src/librustc_mir/pretty.rs b/src/librustc_mir/pretty.rs index d8cfd8a88cf9b..05fe255c64144 100644 --- a/src/librustc_mir/pretty.rs +++ b/src/librustc_mir/pretty.rs @@ -118,7 +118,7 @@ pub fn write_mir_fn<'tcx>(tcx: &TyCtxt<'tcx>, // construct a scope tree and write it out let mut scope_tree: FnvHashMap, Vec> = FnvHashMap(); - for (index, scope_data) in mir.scopes.vec.iter().enumerate() { + for (index, scope_data) in mir.scopes.iter().enumerate() { scope_tree.entry(scope_data.parent_scope) .or_insert(vec![]) .push(ScopeId::new(index)); diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs index 0fb4975453a23..7abaeb44c1c29 100644 --- a/src/librustc_trans/trans/mir/block.rs +++ b/src/librustc_trans/trans/mir/block.rs @@ -569,4 +569,3 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { self.blocks[bb.index()].llbb } } - From c36707a284be6b57990d6f5a2f84bb16655cfb81 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 23 Mar 2016 05:01:30 -0400 Subject: [PATCH 18/24] Add `ScopeAuxiliaryVec`, return MIR+aux via tuple It's nice to be able to index with a scope-id, but coherence rules prevent us from implementing `Index` for `Vec`, and I'd prefer that `ScopeAuxiliary` remain in librustc_mir, just for compilation time reasons. --- src/librustc_mir/build/matches/mod.rs | 2 +- src/librustc_mir/build/mod.rs | 37 +++++++++++++++++++-------- src/librustc_mir/build/scope.rs | 6 ++--- src/librustc_mir/mir_map.rs | 4 +-- src/librustc_mir/pretty.rs | 12 ++++----- 5 files changed, 38 insertions(+), 23 deletions(-) diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 9414752fbd864..b1286e935b684 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -633,7 +633,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { span: span, }); let index = index as u32; - let extent = self.scope_auxiliary[var_scope_id.index()].extent; + let extent = self.scope_auxiliary[var_scope_id].extent; self.schedule_drop(span, extent, &Lvalue::Var(index), var_ty); self.var_indices.insert(var_id, index); diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 056d74ffe1f13..c00acae7dc475 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -14,7 +14,7 @@ use rustc::middle::ty::{FnOutput, Ty}; use rustc::mir::repr::*; use rustc_data_structures::fnv::FnvHashMap; use rustc_front::hir; - +use std::ops::{Index, IndexMut}; use syntax::ast; use syntax::codemap::Span; @@ -33,7 +33,7 @@ pub struct Builder<'a, 'tcx: 'a> { // but these are liable to get out of date once optimization // begins. They are also hopefully temporary, and will be // no longer needed when we adopt graph-based regions. - scope_auxiliary: Vec, + scope_auxiliary: ScopeAuxiliaryVec, // the current set of loops; see the `scope` module for more // details @@ -85,9 +85,24 @@ pub struct Location { pub statement_index: usize, } -pub struct MirAndScopeAuxiliary<'tcx> { - pub mir: Mir<'tcx>, - pub scope_auxiliary: Vec, +pub struct ScopeAuxiliaryVec { + pub vec: Vec +} + +impl Index for ScopeAuxiliaryVec { + type Output = ScopeAuxiliary; + + #[inline] + fn index(&self, index: ScopeId) -> &ScopeAuxiliary { + &self.vec[index.index()] + } +} + +impl IndexMut for ScopeAuxiliaryVec { + #[inline] + fn index_mut(&mut self, index: ScopeId) -> &mut ScopeAuxiliary { + &mut self.vec[index.index()] + } } /////////////////////////////////////////////////////////////////////////// @@ -143,7 +158,7 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>, argument_extent: CodeExtent, return_ty: FnOutput<'tcx>, ast_block: &'tcx hir::Block) - -> MirAndScopeAuxiliary<'tcx> { + -> (Mir<'tcx>, ScopeAuxiliaryVec) { let cfg = CFG { basic_blocks: vec![] }; let mut builder = Builder { @@ -152,7 +167,7 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>, fn_span: span, scopes: vec![], scope_datas: vec![], - scope_auxiliary: vec![], + scope_auxiliary: ScopeAuxiliaryVec { vec: vec![] }, loop_scopes: vec![], temp_decls: vec![], var_decls: vec![], @@ -188,8 +203,8 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>, true })); - MirAndScopeAuxiliary { - mir: Mir { + ( + Mir { basic_blocks: builder.cfg.basic_blocks, scopes: builder.scope_datas, var_decls: builder.var_decls, @@ -198,8 +213,8 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>, return_ty: return_ty, span: span }, - scope_auxiliary: builder.scope_auxiliary, - } + builder.scope_auxiliary, + ) } impl<'a,'tcx> Builder<'a,'tcx> { diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 54830e6c2257d..00d517cc255a7 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -257,7 +257,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { free: None, cached_block: None, }); - self.scope_auxiliary.push(ScopeAuxiliary { + self.scope_auxiliary.vec.push(ScopeAuxiliary { extent: extent, dom: self.cfg.current_location(entry), postdoms: vec![] @@ -279,7 +279,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { let scope = self.scopes.pop().unwrap(); assert_eq!(scope.extent, extent); unpack!(block = build_scope_drops(&mut self.cfg, &scope, &self.scopes, block)); - self.scope_auxiliary[scope.id.index()] + self.scope_auxiliary[scope.id] .postdoms .push(self.cfg.current_location(block)); block.unit() @@ -313,7 +313,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { self.cfg.terminate(block, scope.id, span, free); block = next; } - self.scope_auxiliary[scope.id.index()] + self.scope_auxiliary[scope.id] .postdoms .push(self.cfg.current_location(block)); } diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 276f7d0f46a75..006c55633f85b 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -19,7 +19,7 @@ extern crate syntax; extern crate rustc_front; -use build::{self, MirAndScopeAuxiliary}; +use build; use rustc::dep_graph::DepNode; use rustc::mir::repr::Mir; use pretty; @@ -183,7 +183,7 @@ fn build_mir<'a,'tcx:'a>(cx: Cx<'a,'tcx>, let parameter_scope = cx.tcx().region_maps.lookup_code_extent( CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body.id }); - let MirAndScopeAuxiliary { mut mir, scope_auxiliary } = + let (mut mir, scope_auxiliary) = build::construct(cx, span, implicit_arg_tys, diff --git a/src/librustc_mir/pretty.rs b/src/librustc_mir/pretty.rs index 05fe255c64144..834897f8eaee0 100644 --- a/src/librustc_mir/pretty.rs +++ b/src/librustc_mir/pretty.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use build::{Location, ScopeAuxiliary}; +use build::{Location, ScopeAuxiliaryVec}; use rustc::mir::repr::*; use rustc::middle::ty::{self, TyCtxt}; use rustc_data_structures::fnv::FnvHashMap; @@ -39,7 +39,7 @@ pub fn dump_mir<'a, 'tcx>(tcx: &TyCtxt<'tcx>, disambiguator: &Display, node_id: NodeId, mir: &Mir<'tcx>, - auxiliary: Option<&Vec>) { + auxiliary: Option<&ScopeAuxiliaryVec>) { let filters = match tcx.sess.opts.debugging_opts.dump_mir { None => return, Some(ref filters) => filters, @@ -91,12 +91,12 @@ pub fn write_mir_fn<'tcx>(tcx: &TyCtxt<'tcx>, node_id: NodeId, mir: &Mir<'tcx>, w: &mut Write, - auxiliary: Option<&Vec>) + auxiliary: Option<&ScopeAuxiliaryVec>) -> io::Result<()> { // compute scope/entry exit annotations let mut annotations = FnvHashMap(); if let Some(auxiliary) = auxiliary { - for (index, auxiliary) in auxiliary.iter().enumerate() { + for (index, auxiliary) in auxiliary.vec.iter().enumerate() { let scope_id = ScopeId::new(index); annotations.entry(auxiliary.dom) @@ -183,7 +183,7 @@ fn comment(tcx: &TyCtxt, fn write_scope_tree(tcx: &TyCtxt, mir: &Mir, - auxiliary: Option<&Vec>, + auxiliary: Option<&ScopeAuxiliaryVec>, scope_tree: &FnvHashMap, Vec>, w: &mut Write, parent: Option, @@ -201,7 +201,7 @@ fn write_scope_tree(tcx: &TyCtxt, } if let Some(auxiliary) = auxiliary { - let extent = auxiliary[child.index()].extent; + let extent = auxiliary[child].extent; let data = tcx.region_maps.code_extent_data(extent); writeln!(w, "{0:1$}Extent: {2:?}", "", indent, data)?; } From 2b96cfb14375def79676a969ded1b00a44aa74c4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 23 Mar 2016 05:18:49 -0400 Subject: [PATCH 19/24] add comments on remaining fields --- src/librustc_mir/build/scope.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 00d517cc255a7..cc9a4c4e7144b 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -100,7 +100,15 @@ use rustc_const_eval::ConstInt; pub struct Scope<'tcx> { /// the scope-id within the scope_datas id: ScopeId, + + /// the extent of this scope within source code; also stored in + /// `ScopeAuxiliary`, but kept here for convenience extent: CodeExtent, + + /// set of lvalues to drop when exiting this scope. This starts + /// out empty but grows as variables are declared during the + /// building process. This is a stack, so we always drop from the + /// end of the vector (top of the stack) first. drops: Vec>, /// A scope may only have one associated free, because: From 1c0fa3431052e8b6d944891dc20a1dd614bedd13 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 23 Mar 2016 05:57:52 -0400 Subject: [PATCH 20/24] Update borrowck to use `repr::*` instead of a mix We should probably settle on some conventions here. In MIR code, I have generally been importing `*`, but perhaps borrowck does not want to do that. --- .../borrowck/mir/gather_moves.rs | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs index 0c42ac4fd8430..46eb3d3ca03e5 100644 --- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs +++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs @@ -9,9 +9,8 @@ // except according to those terms. -use rustc::middle::ty; -use rustc::mir::repr::{self, Mir, BasicBlock, Lvalue, Rvalue}; -use rustc::mir::repr::{StatementKind, TerminatorKind}; +use rustc::middle::ty::TyCtxt; +use rustc::mir::repr::*; use rustc::util::nodemap::FnvHashMap; use std::cell::{Cell}; @@ -361,7 +360,7 @@ impl<'tcx> MovePathLookup<'tcx> { } fn lookup_proj(&mut self, - proj: &repr::LvalueProjection<'tcx>, + proj: &LvalueProjection<'tcx>, base: MovePathIndex) -> Lookup { let MovePathLookup { ref mut projections, ref mut next_index, .. } = *self; @@ -484,7 +483,7 @@ impl<'a, 'tcx> MovePathDataBuilder<'a, 'tcx> { } impl<'tcx> MoveData<'tcx> { - pub fn gather_moves(mir: &Mir<'tcx>, tcx: &ty::TyCtxt<'tcx>) -> Self { + pub fn gather_moves(mir: &Mir<'tcx>, tcx: &TyCtxt<'tcx>) -> Self { gather_moves(mir, tcx) } } @@ -495,7 +494,7 @@ enum StmtKind { Aggregate, Drop, CallFn, CallArg, Return, } -fn gather_moves<'tcx>(mir: &Mir<'tcx>, tcx: &ty::TyCtxt<'tcx>) -> MoveData<'tcx> { +fn gather_moves<'tcx>(mir: &Mir<'tcx>, tcx: &TyCtxt<'tcx>) -> MoveData<'tcx> { use self::StmtKind as SK; let bbs = mir.all_basic_blocks(); @@ -554,9 +553,9 @@ fn gather_moves<'tcx>(mir: &Mir<'tcx>, tcx: &ty::TyCtxt<'tcx>) -> MoveData<'tcx> Rvalue::Box(ref _ty) => { // this is creating uninitialized // memory that needs to be initialized. - let deref_lval = Lvalue::Projection(Box::new( repr::Projection { + let deref_lval = Lvalue::Projection(Box::new(Projection { base: lval.clone(), - elem: repr::ProjectionElem::Deref, + elem: ProjectionElem::Deref, })); bb_ctxt.on_move_out_lval(SK::Box, &deref_lval, source); } @@ -668,7 +667,7 @@ fn gather_moves<'tcx>(mir: &Mir<'tcx>, tcx: &ty::TyCtxt<'tcx>) -> MoveData<'tcx> } struct BlockContext<'b, 'a: 'b, 'tcx: 'a> { - tcx: &'b ty::TyCtxt<'tcx>, + tcx: &'b TyCtxt<'tcx>, moves: &'b mut Vec, builder: MovePathDataBuilder<'a, 'tcx>, path_map: &'b mut Vec>, @@ -678,7 +677,7 @@ struct BlockContext<'b, 'a: 'b, 'tcx: 'a> { impl<'b, 'a: 'b, 'tcx: 'a> BlockContext<'b, 'a, 'tcx> { fn on_move_out_lval(&mut self, stmt_kind: StmtKind, - lval: &repr::Lvalue<'tcx>, + lval: &Lvalue<'tcx>, source: Location) { let tcx = self.tcx; let lval_ty = self.builder.mir.lvalue_ty(tcx, lval); @@ -724,10 +723,10 @@ impl<'b, 'a: 'b, 'tcx: 'a> BlockContext<'b, 'a, 'tcx> { self.loc_map_bb[i].push(index); } - fn on_operand(&mut self, stmt_kind: StmtKind, operand: &repr::Operand<'tcx>, source: Location) { + fn on_operand(&mut self, stmt_kind: StmtKind, operand: &Operand<'tcx>, source: Location) { match *operand { - repr::Operand::Constant(..) => {} // not-a-move - repr::Operand::Consume(ref lval) => { // a move + Operand::Constant(..) => {} // not-a-move + Operand::Consume(ref lval) => { // a move self.on_move_out_lval(stmt_kind, lval, source); } } From a276e755e7e272e7bbd95eb3f82f2fcc946825a5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 23 Mar 2016 12:26:37 -0400 Subject: [PATCH 21/24] introduce "call-site-scope" as the outermost scope also, when exiting a scope, assign the final goto terminator with the target scope's id --- src/librustc_mir/build/expr/into.rs | 2 +- src/librustc_mir/build/mod.rs | 109 ++++++++++++++++------------ src/librustc_mir/build/scope.rs | 23 ++++-- src/librustc_mir/mir_map.rs | 7 +- 4 files changed, 84 insertions(+), 57 deletions(-) diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 4c9e2c0c5fa6d..30c039cdde0c3 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -261,7 +261,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { block } }; - let extent = this.extent_of_outermost_scope(); + let extent = this.extent_of_return_scope(); this.exit_scope(expr_span, extent, block, END_BLOCK); this.cfg.start_new_block().unit() } diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index c00acae7dc475..a88b138a44dde 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -9,7 +9,7 @@ // except according to those terms. use hair::cx::Cx; -use rustc::middle::region::CodeExtent; +use rustc::middle::region::{CodeExtent, CodeExtentData}; use rustc::middle::ty::{FnOutput, Ty}; use rustc::mir::repr::*; use rustc_data_structures::fnv::FnvHashMap; @@ -153,12 +153,14 @@ macro_rules! unpack { pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>, span: Span, + fn_id: ast::NodeId, + body_id: ast::NodeId, implicit_arguments: Vec>, explicit_arguments: Vec<(Ty<'tcx>, &'tcx hir::Pat)>, - argument_extent: CodeExtent, return_ty: FnOutput<'tcx>, ast_block: &'tcx hir::Block) -> (Mir<'tcx>, ScopeAuxiliaryVec) { + let tcx = hir.tcx(); let cfg = CFG { basic_blocks: vec![] }; let mut builder = Builder { @@ -178,18 +180,32 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>, assert_eq!(builder.cfg.start_new_block(), START_BLOCK); assert_eq!(builder.cfg.start_new_block(), END_BLOCK); - let mut block = START_BLOCK; - let (arg_decls, arg_scope_id) = - unpack!(block = builder.args_and_body(block, - implicit_arguments, - explicit_arguments, - argument_extent, - ast_block)); - builder.cfg.terminate(block, arg_scope_id, span, - TerminatorKind::Goto { target: END_BLOCK }); - builder.cfg.terminate(END_BLOCK, arg_scope_id, span, - TerminatorKind::Return); + let mut arg_decls = None; // assigned to `Some` in closures below + let call_site_extent = + tcx.region_maps.lookup_code_extent( + CodeExtentData::CallSiteScope { fn_id: fn_id, body_id: body_id }); + let _ = builder.in_scope(call_site_extent, START_BLOCK, |builder, call_site_scope_id| { + let mut block = START_BLOCK; + let arg_extent = + tcx.region_maps.lookup_code_extent( + CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body_id }); + unpack!(block = builder.in_scope(arg_extent, block, |builder, arg_scope_id| { + arg_decls = Some(unpack!(block = builder.args_and_body(block, + implicit_arguments, + explicit_arguments, + arg_scope_id, + ast_block))); + block.unit() + })); + + builder.cfg.terminate(block, call_site_scope_id, span, + TerminatorKind::Goto { target: END_BLOCK }); + builder.cfg.terminate(END_BLOCK, call_site_scope_id, span, + TerminatorKind::Return); + + END_BLOCK.unit() + }); assert!( builder.cfg.basic_blocks @@ -197,8 +213,8 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>, .enumerate() .all(|(index, block)| { if block.terminator.is_none() { - panic!("no terminator on block {:?} in {:?}", - index, argument_extent) + panic!("no terminator on block {:?} in fn {:?}", + index, fn_id) } true })); @@ -208,7 +224,7 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>, basic_blocks: builder.cfg.basic_blocks, scopes: builder.scope_datas, var_decls: builder.var_decls, - arg_decls: arg_decls, + arg_decls: arg_decls.take().expect("args never built?"), temp_decls: builder.temp_decls, return_ty: return_ty, span: span @@ -222,39 +238,40 @@ impl<'a,'tcx> Builder<'a,'tcx> { mut block: BasicBlock, implicit_arguments: Vec>, explicit_arguments: Vec<(Ty<'tcx>, &'tcx hir::Pat)>, - argument_extent: CodeExtent, + argument_scope_id: ScopeId, ast_block: &'tcx hir::Block) - -> BlockAnd<(Vec>, ScopeId)> + -> BlockAnd>> { - self.in_scope(argument_extent, block, |this, argument_scope_id| { - // to start, translate the argument patterns and collect the argument types. - let implicits = implicit_arguments.into_iter().map(|ty| (ty, None)); - let explicits = explicit_arguments.into_iter().map(|(ty, pat)| (ty, Some(pat))); + // to start, translate the argument patterns and collect the argument types. + let implicits = implicit_arguments.into_iter().map(|ty| (ty, None)); + let explicits = explicit_arguments.into_iter().map(|(ty, pat)| (ty, Some(pat))); let arg_decls = - implicits - .chain(explicits) - .enumerate() - .map(|(index, (ty, pattern))| { - let lvalue = Lvalue::Arg(index as u32); - if let Some(pattern) = pattern { - let pattern = this.hir.irrefutable_pat(pattern); - unpack!(block = this.lvalue_into_pattern(block, - argument_scope_id, - pattern, - &lvalue)); - } - // Make sure we drop (parts of) the argument even when not matched on. - this.schedule_drop(pattern.as_ref().map_or(ast_block.span, |pat| pat.span), - argument_extent, &lvalue, ty); - ArgDecl { ty: ty, spread: false } - }) - .collect(); - - // start the first basic block and translate the body - unpack!(block = this.ast_block(&Lvalue::ReturnPointer, block, ast_block)); - - block.and((arg_decls, argument_scope_id)) - }) + implicits + .chain(explicits) + .enumerate() + .map(|(index, (ty, pattern))| { + let lvalue = Lvalue::Arg(index as u32); + if let Some(pattern) = pattern { + let pattern = self.hir.irrefutable_pat(pattern); + unpack!(block = self.lvalue_into_pattern(block, + argument_scope_id, + pattern, + &lvalue)); + } + + // Make sure we drop (parts of) the argument even when not matched on. + let argument_extent = self.scope_auxiliary[argument_scope_id].extent; + self.schedule_drop(pattern.as_ref().map_or(ast_block.span, |pat| pat.span), + argument_extent, &lvalue, ty); + + ArgDecl { ty: ty, spread: false } + }) + .collect(); + + // start the first basic block and translate the body + unpack!(block = self.ast_block(&Lvalue::ReturnPointer, block, ast_block)); + + block.and(arg_decls) } fn get_unit_temp(&mut self) -> Lvalue<'tcx> { diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index cc9a4c4e7144b..acca2637c82c1 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -87,7 +87,7 @@ should go to. */ use build::{BlockAnd, BlockAndExtension, Builder, CFG, ScopeAuxiliary}; -use rustc::middle::region::CodeExtent; +use rustc::middle::region::{CodeExtent, CodeExtentData}; use rustc::middle::lang_items; use rustc::middle::subst::{Substs, Subst, VecPerParamSpace}; use rustc::middle::ty::{self, Ty, TyCtxt}; @@ -326,9 +326,13 @@ impl<'a,'tcx> Builder<'a,'tcx> { .push(self.cfg.current_location(block)); } - let scope_id = self.innermost_scope_id(); + assert!(scope_count < self.scopes.len(), + "should never use `exit_scope` to pop *ALL* scopes"); + let scope = self.scopes.iter().rev().skip(scope_count) + .next() + .unwrap(); self.cfg.terminate(block, - scope_id, + scope.id, span, TerminatorKind::Goto { target: target }); } @@ -365,8 +369,17 @@ impl<'a,'tcx> Builder<'a,'tcx> { self.scopes.last().map(|scope| scope.extent).unwrap() } - pub fn extent_of_outermost_scope(&self) -> CodeExtent { - self.scopes.first().map(|scope| scope.extent).unwrap() + /// Returns the extent of the scope which should be exited by a + /// return. + pub fn extent_of_return_scope(&self) -> CodeExtent { + // The outermost scope (`scopes[0]`) will be the `CallSiteScope`. + // We want `scopes[1]`, which is the `ParameterScope`. + assert!(self.scopes.len() >= 2); + assert!(match self.hir.tcx().region_maps.code_extent_data(self.scopes[1].extent) { + CodeExtentData::ParameterScope { .. } => true, + _ => false, + }); + self.scopes[1].extent } // Scheduling drops diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 006c55633f85b..5a6e2edfdf9e7 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -27,7 +27,6 @@ use hair::cx::Cx; use rustc::mir::mir_map::MirMap; use rustc::middle::infer; -use rustc::middle::region::CodeExtentData; use rustc::middle::traits::ProjectionMode; use rustc::middle::ty::{self, Ty, TyCtxt}; use rustc::util::common::ErrorReported; @@ -180,15 +179,13 @@ fn build_mir<'a,'tcx:'a>(cx: Cx<'a,'tcx>, }) .collect(); - let parameter_scope = - cx.tcx().region_maps.lookup_code_extent( - CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body.id }); let (mut mir, scope_auxiliary) = build::construct(cx, span, + fn_id, + body.id, implicit_arg_tys, arguments, - parameter_scope, fn_sig.output, body); From 0769865f7f42d0c52b35552fe8d17e651d32772a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 23 Mar 2016 20:46:38 -0400 Subject: [PATCH 22/24] rewrite scope drop to be iterative while I'm at it, remove the "extra caching" that I was doing for no good reason except laziness. Basically before I was caching at each scope in the chain, but there's not really a reason to do that, since the cached entry point at level N is always equal to the last cached exit point from level N-1. --- src/librustc_mir/build/mod.rs | 5 +++ src/librustc_mir/build/scope.rs | 66 +++++++++++++++++---------------- 2 files changed, 39 insertions(+), 32 deletions(-) diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index a88b138a44dde..b79f492179fb9 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -47,6 +47,10 @@ pub struct Builder<'a, 'tcx: 'a> { var_indices: FnvHashMap, temp_decls: Vec>, unit_temp: Option>, + + // cached block with a RESUME terminator; we create this at the + // first panic + cached_resume_block: Option, } struct CFG<'tcx> { @@ -175,6 +179,7 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>, var_decls: vec![], var_indices: FnvHashMap(), unit_temp: None, + cached_resume_block: None, }; assert_eq!(builder.cfg.start_new_block(), START_BLOCK); diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index acca2637c82c1..f5f6f409eaba4 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -456,21 +456,41 @@ impl<'a,'tcx> Builder<'a,'tcx> { /// See module comment for more details. None indicates there’s no /// cleanup to do at this point. pub fn diverge_cleanup(&mut self) -> Option { - if self.scopes.is_empty() { + if self.scopes.iter().all(|scope| scope.drops.is_empty() && scope.free.is_none()) { return None; } + assert!(!self.scopes.is_empty()); // or `all` above would be true + let unit_temp = self.get_unit_temp(); - let Builder { ref mut hir, ref mut cfg, ref mut scopes, .. } = *self; - - // Given an array of scopes, we generate these from the outermost scope to the innermost - // one. Thus for array [S0, S1, S2] with corresponding cleanup blocks [B0, B1, B2], we will - // generate B0 <- B1 <- B2 in left-to-right order. Control flow of the generated blocks - // always ends up at a block with the Resume terminator. - if scopes.iter().any(|scope| !scope.drops.is_empty() || scope.free.is_some()) { - Some(build_diverge_scope(hir.tcx(), self.fn_span, cfg, &unit_temp, scopes)) + let Builder { ref mut hir, ref mut cfg, ref mut scopes, + ref mut cached_resume_block, .. } = *self; + + // Build up the drops in **reverse** order. The end result will + // look like: + // + // scopes[n] -> scopes[n-1] -> ... -> scopes[0] + // + // However, we build this in **reverse order**. That is, we + // process scopes[0], then scopes[1], etc, pointing each one at + // the result generates from the one before. Along the way, we + // store caches. If everything is cached, we'll just walk right + // to left reading the cached results but never created anything. + + // To start, create the resume terminator. + let mut target = if let Some(target) = *cached_resume_block { + target } else { - None + let resumeblk = cfg.start_new_cleanup_block(); + cfg.terminate(resumeblk, scopes[0].id, self.fn_span, TerminatorKind::Resume); + *cached_resume_block = Some(resumeblk); + resumeblk + }; + + for scope in scopes { + target = build_diverge_scope(hir.tcx(), cfg, &unit_temp, scope, target); } + + Some(target) } /// Utility function for *non*-scope code to build their own drops @@ -640,43 +660,25 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>, } fn build_diverge_scope<'tcx>(tcx: &TyCtxt<'tcx>, - fn_span: Span, cfg: &mut CFG<'tcx>, unit_temp: &Lvalue<'tcx>, - scopes: &mut [Scope<'tcx>]) + scope: &mut Scope<'tcx>, + mut target: BasicBlock) -> BasicBlock { - assert!(scopes.len() >= 1); - // Build up the drops in **reverse** order. The end result will // look like: // - // [drops[n]] -...-> [drops[0]] -> [Free] -> [scopes[..n-1]] + // [drops[n]] -...-> [drops[0]] -> [Free] -> [target] // | | // +------------------------------------+ - // code for scopes[n] + // code for scope // // The code in this function reads from right to left. At each // point, we check for cached blocks representing the // remainder. If everything is cached, we'll just walk right to // left reading the cached results but never created anything. - // To start, translate scopes[1..]. - let (scope, earlier_scopes) = scopes.split_last_mut().unwrap(); - let mut target = if let Some(cached_block) = scope.cached_block { - cached_block - } else if earlier_scopes.is_empty() { - // Diverging from the root scope creates a RESUME terminator. - // FIXME what span to use here? - let resumeblk = cfg.start_new_cleanup_block(); - cfg.terminate(resumeblk, scope.id, fn_span, TerminatorKind::Resume); - resumeblk - } else { - // Diverging from any other scope chains up to the previous scope. - build_diverge_scope(tcx, fn_span, cfg, unit_temp, earlier_scopes) - }; - scope.cached_block = Some(target); - // Next, build up any free. if let Some(ref mut free_data) = scope.free { target = if let Some(cached_block) = free_data.cached_block { From ed7c30b8885afb7e0bf26072dcb7e39658baade0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 24 Mar 2016 06:12:19 -0400 Subject: [PATCH 23/24] rework MIR visitor We now visit more things (e.g., types) and also follow a deliberate style designed to reduce omissions. --- src/librustc/mir/repr.rs | 15 ++ src/librustc/mir/visit.rs | 393 +++++++++++++++++++++++++++++++++++--- 2 files changed, 382 insertions(+), 26 deletions(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 4022d762aec34..47d923cbce359 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -233,8 +233,23 @@ impl Debug for BasicBlock { #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub struct BasicBlockData<'tcx> { + /// List of statements in this block. pub statements: Vec>, + + /// Terminator for this block. + /// + /// NB. This should generally ONLY be `None` during construction. + /// Therefore, you should generally access it via the + /// `terminator()` or `terminator_mut()` methods. The only + /// exception is that certain passes, such as `simplify_cfg`, swap + /// out the terminator temporarily with `None` while they continue + /// to recurse over the set of basic blocks. pub terminator: Option>, + + /// If true, this block lies on an unwind path. This is used + /// during trans where distinct kinds of basic blocks may be + /// generated (particularly for MSVC cleanup). Unwind blocks must + /// only branch to other unwind blocks. pub is_cleanup: bool, } diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index c7e4b825d58a5..5be2e06841477 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -8,12 +8,79 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use middle::const_eval::ConstVal; use middle::def_id::DefId; -use middle::ty::Region; +use middle::subst::Substs; +use middle::ty::{ClosureSubsts, FnOutput, Region, Ty}; use mir::repr::*; +use rustc_const_eval::ConstUsize; use rustc_data_structures::tuple_slice::TupleSlice; use syntax::codemap::Span; +// # The MIR Visitor +// +// ## Overview +// +// There are two visitors, one for immutable and one for mutable references, +// but both are generated by the following macro. The code is written according +// to the following conventions: +// +// - introduce a `visit_foo` and a `super_foo` method for every MIR type +// - `visit_foo`, by default, calls `super_foo` +// - `super_foo`, by default, destructures the `foo` and calls `visit_foo` +// +// This allows you as a user to override `visit_foo` for types are +// interested in, and invoke (within that method) call +// `self.super_foo` to get the default behavior. Just as in an OO +// language, you should never call `super` methods ordinarily except +// in that circumstance. +// +// For the most part, we do not destructure things external to the +// MIR, e.g. types, spans, etc, but simply visit them and stop. This +// avoids duplication with other visitors like `TypeFoldable`. But +// there is one exception: we do destructure the `FnOutput` to reach +// the type within. Just because. +// +// ## Updating +// +// The code is written in a very deliberate style intended to minimize +// the chance of things being overlooked. You'll notice that we always +// use pattern matching to reference fields and we ensure that all +// matches are exhaustive. +// +// For example, the `super_basic_block_data` method begins like this: +// +// ```rust +// fn super_basic_block_data(&mut self, +// block: BasicBlock, +// data: & $($mutability)* BasicBlockData<'tcx>) { +// let BasicBlockData { +// ref $($mutability)* statements, +// ref $($mutability)* terminator, +// is_cleanup: _ +// } = *data; +// +// for statement in statements { +// self.visit_statement(block, statement); +// } +// +// ... +// } +// ``` +// +// Here we used `let BasicBlockData { } = *data` deliberately, +// rather than writing `data.statements` in the body. This is because if one +// adds a new field to `BasicBlockData`, one will be forced to revise this code, +// and hence one will (hopefully) invoke the correct visit methods (if any). +// +// For this to work, ALL MATCHES MUST BE EXHAUSTIVE IN FIELDS AND VARIANTS. +// That means you never write `..` to skip over fields, nor do you write `_` +// to skip over variants in a `match`. +// +// The only place that `_` is acceptable is to match a field (or +// variant argument) that does not require visiting, as in +// `is_cleanup` above. + macro_rules! make_mir_visitor { ($visitor_trait_name:ident, $($mutability:ident)*) => { pub trait $visitor_trait_name<'tcx> { @@ -30,6 +97,11 @@ macro_rules! make_mir_visitor { self.super_basic_block_data(block, data); } + fn visit_scope_data(&mut self, + scope_data: & $($mutability)* ScopeData) { + self.super_scope_data(scope_data); + } + fn visit_statement(&mut self, block: BasicBlock, statement: & $($mutability)* Statement<'tcx>) { @@ -49,6 +121,12 @@ macro_rules! make_mir_visitor { self.super_terminator(block, terminator); } + fn visit_terminator_kind(&mut self, + block: BasicBlock, + kind: & $($mutability)* TerminatorKind<'tcx>) { + self.super_terminator_kind(block, kind); + } + fn visit_rvalue(&mut self, rvalue: & $($mutability)* Rvalue<'tcx>) { self.super_rvalue(rvalue); @@ -65,6 +143,18 @@ macro_rules! make_mir_visitor { self.super_lvalue(lvalue, context); } + fn visit_projection(&mut self, + lvalue: & $($mutability)* LvalueProjection<'tcx>, + context: LvalueContext) { + self.super_projection(lvalue, context); + } + + fn visit_projection_elem(&mut self, + lvalue: & $($mutability)* LvalueElem<'tcx>, + context: LvalueContext) { + self.super_projection_elem(lvalue, context); + } + fn visit_branch(&mut self, source: BasicBlock, target: BasicBlock) { @@ -91,35 +181,143 @@ macro_rules! make_mir_visitor { self.super_span(span); } + fn visit_fn_output(&mut self, + fn_output: & $($mutability)* FnOutput<'tcx>) { + self.super_fn_output(fn_output); + } + + fn visit_ty(&mut self, + ty: & $($mutability)* Ty<'tcx>) { + self.super_ty(ty); + } + + fn visit_substs(&mut self, + substs: & $($mutability)* &'tcx Substs<'tcx>) { + self.super_substs(substs); + } + + fn visit_closure_substs(&mut self, + substs: & $($mutability)* &'tcx ClosureSubsts<'tcx>) { + self.super_closure_substs(substs); + } + + fn visit_const_val(&mut self, + const_val: & $($mutability)* ConstVal) { + self.super_const_val(const_val); + } + + fn visit_const_usize(&mut self, + const_usize: & $($mutability)* ConstUsize) { + self.super_const_usize(const_usize); + } + + fn visit_typed_const_val(&mut self, + val: & $($mutability)* TypedConstVal<'tcx>) { + self.super_typed_const_val(val); + } + + fn visit_var_decl(&mut self, + var_decl: & $($mutability)* VarDecl<'tcx>) { + self.super_var_decl(var_decl); + } + + fn visit_temp_decl(&mut self, + temp_decl: & $($mutability)* TempDecl<'tcx>) { + self.super_temp_decl(temp_decl); + } + + fn visit_arg_decl(&mut self, + arg_decl: & $($mutability)* ArgDecl<'tcx>) { + self.super_arg_decl(arg_decl); + } + + fn visit_scope_id(&mut self, + scope_id: & $($mutability)* ScopeId) { + self.super_scope_id(scope_id); + } + // The `super_xxx` methods comprise the default behavior and are // not meant to be overridden. fn super_mir(&mut self, mir: & $($mutability)* Mir<'tcx>) { - for block in mir.all_basic_blocks() { - let data = & $($mutability)* mir[block]; + let Mir { + ref $($mutability)* basic_blocks, + ref $($mutability)* scopes, + ref $($mutability)* return_ty, + ref $($mutability)* var_decls, + ref $($mutability)* arg_decls, + ref $($mutability)* temp_decls, + ref $($mutability)* span, + } = *mir; + + for (index, data) in basic_blocks.into_iter().enumerate() { + let block = BasicBlock::new(index); self.visit_basic_block_data(block, data); } + + for scope in scopes { + self.visit_scope_data(scope); + } + + self.visit_fn_output(return_ty); + + for var_decl in var_decls { + self.visit_var_decl(var_decl); + } + + for arg_decl in arg_decls { + self.visit_arg_decl(arg_decl); + } + + for temp_decl in temp_decls { + self.visit_temp_decl(temp_decl); + } + + self.visit_span(span); } fn super_basic_block_data(&mut self, block: BasicBlock, data: & $($mutability)* BasicBlockData<'tcx>) { - for statement in & $($mutability)* data.statements { + let BasicBlockData { + ref $($mutability)* statements, + ref $($mutability)* terminator, + is_cleanup: _ + } = *data; + + for statement in statements { self.visit_statement(block, statement); } - if let Some(ref $($mutability)* terminator) = data.terminator { + if let Some(ref $($mutability)* terminator) = *terminator { self.visit_terminator(block, terminator); } } + fn super_scope_data(&mut self, + scope_data: & $($mutability)* ScopeData) { + let ScopeData { + ref $($mutability)* parent_scope, + } = *scope_data; + + if let Some(ref $($mutability)* parent_scope) = *parent_scope { + self.visit_scope_id(parent_scope); + } + } + fn super_statement(&mut self, block: BasicBlock, statement: & $($mutability)* Statement<'tcx>) { - self.visit_span(& $($mutability)* statement.span); - - match statement.kind { + let Statement { + ref $($mutability)* span, + ref $($mutability)* scope, + ref $($mutability)* kind, + } = *statement; + + self.visit_span(span); + self.visit_scope_id(scope); + match *kind { StatementKind::Assign(ref $($mutability)* lvalue, ref $($mutability)* rvalue) => { self.visit_assign(block, lvalue, rvalue); @@ -138,7 +336,21 @@ macro_rules! make_mir_visitor { fn super_terminator(&mut self, block: BasicBlock, terminator: &$($mutability)* Terminator<'tcx>) { - match terminator.kind { + let Terminator { + ref $($mutability)* span, + ref $($mutability)* scope, + ref $($mutability)* kind, + } = *terminator; + + self.visit_span(span); + self.visit_scope_id(scope); + self.visit_terminator_kind(block, kind); + } + + fn super_terminator_kind(&mut self, + block: BasicBlock, + kind: & $($mutability)* TerminatorKind<'tcx>) { + match *kind { TerminatorKind::Goto { target } => { self.visit_branch(block, target); } @@ -161,10 +373,14 @@ macro_rules! make_mir_visitor { } TerminatorKind::SwitchInt { ref $($mutability)* discr, - switch_ty: _, - values: _, + ref $($mutability)* switch_ty, + ref $($mutability)* values, ref targets } => { self.visit_lvalue(discr, LvalueContext::Inspect); + self.visit_ty(switch_ty); + for value in values { + self.visit_const_val(value); + } for &target in targets { self.visit_branch(block, target); } @@ -207,8 +423,9 @@ macro_rules! make_mir_visitor { } Rvalue::Repeat(ref $($mutability)* value, - _) => { + ref $($mutability)* typed_const_val) => { self.visit_operand(value); + self.visit_typed_const_val(typed_const_val); } Rvalue::Ref(r, bk, ref $($mutability)* path) => { @@ -222,34 +439,48 @@ macro_rules! make_mir_visitor { self.visit_lvalue(path, LvalueContext::Inspect); } - Rvalue::Cast(_, ref $($mutability)* operand, _) => { + Rvalue::Cast(_cast_kind, + ref $($mutability)* operand, + ref $($mutability)* ty) => { self.visit_operand(operand); + self.visit_ty(ty); } - Rvalue::BinaryOp(_, + Rvalue::BinaryOp(_bin_op, ref $($mutability)* lhs, ref $($mutability)* rhs) => { self.visit_operand(lhs); self.visit_operand(rhs); } - Rvalue::UnaryOp(_, ref $($mutability)* op) => { + Rvalue::UnaryOp(_un_op, ref $($mutability)* op) => { self.visit_operand(op); } - Rvalue::Box(_) => { + Rvalue::Box(ref $($mutability)* ty) => { + self.visit_ty(ty); } Rvalue::Aggregate(ref $($mutability)* kind, ref $($mutability)* operands) => { match *kind { - AggregateKind::Closure(ref $($mutability)* def_id, _) => { + AggregateKind::Vec => { + } + AggregateKind::Tuple => { + } + AggregateKind::Adt(_adt_def, + _variant_index, + ref $($mutability)* substs) => { + self.visit_substs(substs); + } + AggregateKind::Closure(ref $($mutability)* def_id, + ref $($mutability)* closure_substs) => { self.visit_def_id(def_id); + self.visit_closure_substs(closure_substs); } - _ => { /* nothing to do */ } } - for operand in & $($mutability)* operands[..] { + for operand in operands { self.visit_operand(operand); } } @@ -264,7 +495,8 @@ macro_rules! make_mir_visitor { } Rvalue::InlineAsm { ref $($mutability)* outputs, - ref $($mutability)* inputs, .. } => { + ref $($mutability)* inputs, + asm: _ } => { for output in & $($mutability)* outputs[..] { self.visit_lvalue(output, LvalueContext::Store); } @@ -289,7 +521,7 @@ macro_rules! make_mir_visitor { fn super_lvalue(&mut self, lvalue: & $($mutability)* Lvalue<'tcx>, - _context: LvalueContext) { + context: LvalueContext) { match *lvalue { Lvalue::Var(_) | Lvalue::Temp(_) | @@ -300,12 +532,81 @@ macro_rules! make_mir_visitor { self.visit_def_id(def_id); } Lvalue::Projection(ref $($mutability)* proj) => { - self.visit_lvalue(& $($mutability)* proj.base, - LvalueContext::Projection); + self.visit_projection(proj, context); } } } + fn super_projection(&mut self, + proj: & $($mutability)* LvalueProjection<'tcx>, + context: LvalueContext) { + let Projection { + ref $($mutability)* base, + ref $($mutability)* elem, + } = *proj; + self.visit_lvalue(base, LvalueContext::Projection); + self.visit_projection_elem(elem, context); + } + + fn super_projection_elem(&mut self, + proj: & $($mutability)* LvalueElem<'tcx>, + _context: LvalueContext) { + match *proj { + ProjectionElem::Deref => { + } + ProjectionElem::Field(_field, ref $($mutability)* ty) => { + self.visit_ty(ty); + } + ProjectionElem::Index(ref $($mutability)* operand) => { + self.visit_operand(operand); + } + ProjectionElem::ConstantIndex { offset: _, + min_length: _, + from_end: _ } => { + } + ProjectionElem::Downcast(_adt_def, _variant_index) => { + } + } + } + + fn super_var_decl(&mut self, + var_decl: & $($mutability)* VarDecl<'tcx>) { + let VarDecl { + mutability: _, + name: _, + ref $($mutability)* ty, + ref $($mutability)* scope, + ref $($mutability)* span, + } = *var_decl; + + self.visit_ty(ty); + self.visit_scope_id(scope); + self.visit_span(span); + } + + fn super_temp_decl(&mut self, + temp_decl: & $($mutability)* TempDecl<'tcx>) { + let TempDecl { + ref $($mutability)* ty, + } = *temp_decl; + + self.visit_ty(ty); + } + + fn super_arg_decl(&mut self, + arg_decl: & $($mutability)* ArgDecl<'tcx>) { + let ArgDecl { + ref $($mutability)* ty, + spread: _ + } = *arg_decl; + + self.visit_ty(ty); + } + + fn super_scope_id(&mut self, + _scope_id: & $($mutability)* ScopeId) { + } + fn super_branch(&mut self, _source: BasicBlock, _target: BasicBlock) { @@ -314,17 +615,32 @@ macro_rules! make_mir_visitor { fn super_constant(&mut self, constant: & $($mutability)* Constant<'tcx>) { self.visit_span(& $($mutability)* constant.span); + self.visit_ty(& $($mutability)* constant.ty); self.visit_literal(& $($mutability)* constant.literal); } + fn super_typed_const_val(&mut self, + constant: & $($mutability)* TypedConstVal<'tcx>) { + let TypedConstVal { + ref $($mutability)* span, + ref $($mutability)* ty, + ref $($mutability)* value, + } = *constant; + self.visit_span(span); + self.visit_ty(ty); + self.visit_const_usize(value); + } + fn super_literal(&mut self, literal: & $($mutability)* Literal<'tcx>) { match *literal { - Literal::Item { ref $($mutability)* def_id, .. } => { + Literal::Item { ref $($mutability)* def_id, + ref $($mutability)* substs } => { self.visit_def_id(def_id); + self.visit_substs(substs); }, - Literal::Value { .. } => { - // Nothing to do + Literal::Value { ref $($mutability)* value } => { + self.visit_const_val(value); } } } @@ -334,6 +650,31 @@ macro_rules! make_mir_visitor { fn super_span(&mut self, _span: & $($mutability)* Span) { } + + fn super_fn_output(&mut self, fn_output: & $($mutability)* FnOutput<'tcx>) { + match *fn_output { + FnOutput::FnConverging(ref $($mutability)* ty) => { + self.visit_ty(ty); + } + FnOutput::FnDiverging => { + } + } + } + + fn super_ty(&mut self, _ty: & $($mutability)* Ty<'tcx>) { + } + + fn super_substs(&mut self, _substs: & $($mutability)* &'tcx Substs<'tcx>) { + } + + fn super_closure_substs(&mut self, _substs: & $($mutability)* &'tcx ClosureSubsts<'tcx>) { + } + + fn super_const_val(&mut self, _substs: & $($mutability)* ConstVal) { + } + + fn super_const_usize(&mut self, _substs: & $($mutability)* ConstUsize) { + } } } } From 091a00797ecf91989d901fcbb795869f4c6c7fc1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 24 Mar 2016 07:11:11 -0400 Subject: [PATCH 24/24] pacify the merciless tidy --- src/librustc/mir/visit.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 5be2e06841477..bc0056b0af02f 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -667,7 +667,8 @@ macro_rules! make_mir_visitor { fn super_substs(&mut self, _substs: & $($mutability)* &'tcx Substs<'tcx>) { } - fn super_closure_substs(&mut self, _substs: & $($mutability)* &'tcx ClosureSubsts<'tcx>) { + fn super_closure_substs(&mut self, + _substs: & $($mutability)* &'tcx ClosureSubsts<'tcx>) { } fn super_const_val(&mut self, _substs: & $($mutability)* ConstVal) {