about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2018-04-06 15:53:49 -0400
committerNiko Matsakis <niko@alum.mit.edu>2018-04-15 07:06:29 -0400
commite1123674b1c719e0a5ced078c15a81a22e16dec0 (patch)
tree8f6c39fe029adc87e679b0ff86b88d8e98a9fc80 /src
parent70592664b6b071af2fe03afb60ffb9cfab6feaf7 (diff)
downloadrust-e1123674b1c719e0a5ced078c15a81a22e16dec0.tar.gz
rust-e1123674b1c719e0a5ced078c15a81a22e16dec0.zip
extract code to build borrow-set into `borrow_check::borrow_set`
Also:

- Extract common helper functions into a helper trait.
- Kill a bit of dead code.
Diffstat (limited to 'src')
-rw-r--r--src/librustc_mir/borrow_check/borrow_set.rs252
-rw-r--r--src/librustc_mir/borrow_check/mod.rs1
-rw-r--r--src/librustc_mir/borrow_check/place_ext.rs60
-rw-r--r--src/librustc_mir/dataflow/impls/borrows.rs416
4 files changed, 322 insertions, 407 deletions
diff --git a/src/librustc_mir/borrow_check/borrow_set.rs b/src/librustc_mir/borrow_check/borrow_set.rs
index 4d541d0ffd2..a25c3afd63c 100644
--- a/src/librustc_mir/borrow_check/borrow_set.rs
+++ b/src/librustc_mir/borrow_check/borrow_set.rs
@@ -8,12 +8,16 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use borrow_check::place_ext::PlaceExt;
 use dataflow::indexes::BorrowIndex;
-use rustc::mir::{self, Location};
-use rustc::ty::{Region, RegionKind};
+use rustc::mir::traversal;
+use rustc::mir::visit::{PlaceContext, Visitor};
+use rustc::mir::{self, Location, Mir, Place};
+use rustc::ty::{self, Region, RegionKind, TyCtxt};
 use rustc::util::nodemap::{FxHashMap, FxHashSet};
 use rustc_data_structures::indexed_vec::IndexVec;
 use std::fmt;
+use std::hash::Hash;
 use syntax_pos::Span;
 
 crate struct BorrowSet<'tcx> {
@@ -71,8 +75,250 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> {
             mir::BorrowKind::Mut { .. } => "mut ",
         };
         let region = format!("{}", self.region);
-        let region = if region.len() > 0 { format!("{} ", region) } else { region };
+        let region = if region.len() > 0 {
+            format!("{} ", region)
+        } else {
+            region
+        };
         write!(w, "&{}{}{:?}", region, kind, self.borrowed_place)
     }
 }
 
+impl<'tcx> BorrowSet<'tcx> {
+    pub fn build(tcx: TyCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) -> Self {
+        let mut visitor = GatherBorrows {
+            tcx,
+            mir,
+            idx_vec: IndexVec::new(),
+            location_map: FxHashMap(),
+            activation_map: FxHashMap(),
+            region_map: FxHashMap(),
+            local_map: FxHashMap(),
+            region_span_map: FxHashMap(),
+            pending_activations: FxHashMap(),
+        };
+
+        for (block, block_data) in traversal::preorder(mir) {
+            visitor.visit_basic_block_data(block, block_data);
+        }
+
+        // Double check: We should have found an activation for every pending
+        // activation.
+        assert_eq!(
+            visitor
+                .pending_activations
+                .iter()
+                .find(|&(_local, &borrow_index)| visitor.idx_vec[borrow_index]
+                    .activation_location
+                    .is_none()),
+            None,
+            "never found an activation for this borrow!",
+        );
+
+        BorrowSet {
+            borrows: visitor.idx_vec,
+            location_map: visitor.location_map,
+            activation_map: visitor.activation_map,
+            region_map: visitor.region_map,
+            local_map: visitor.local_map,
+            region_span_map: visitor.region_span_map,
+        }
+    }
+}
+
+struct GatherBorrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
+    tcx: TyCtxt<'a, 'gcx, 'tcx>,
+    mir: &'a Mir<'tcx>,
+    idx_vec: IndexVec<BorrowIndex, BorrowData<'tcx>>,
+    location_map: FxHashMap<Location, BorrowIndex>,
+    activation_map: FxHashMap<Location, FxHashSet<BorrowIndex>>,
+    region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
+    local_map: FxHashMap<mir::Local, FxHashSet<BorrowIndex>>,
+    region_span_map: FxHashMap<RegionKind, Span>,
+
+    /// When we encounter a 2-phase borrow statement, it will always
+    /// be assigning into a temporary TEMP:
+    ///
+    ///    TEMP = &foo
+    ///
+    /// We add TEMP into this map with `b`, where `b` is the index of
+    /// the borrow. When we find a later use of this activation, we
+    /// remove from the map (and add to the "tombstone" set below).
+    pending_activations: FxHashMap<mir::Local, BorrowIndex>,
+}
+
+impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> {
+    fn visit_assign(
+        &mut self,
+        block: mir::BasicBlock,
+        assigned_place: &mir::Place<'tcx>,
+        rvalue: &mir::Rvalue<'tcx>,
+        location: mir::Location,
+    ) {
+        if let mir::Rvalue::Ref(region, kind, ref borrowed_place) = *rvalue {
+            if borrowed_place.is_unsafe_place(self.tcx, self.mir) {
+                return;
+            }
+
+            let borrow = BorrowData {
+                kind,
+                region,
+                reserve_location: location,
+                activation_location: None,
+                borrowed_place: borrowed_place.clone(),
+                assigned_place: assigned_place.clone(),
+            };
+            let idx = self.idx_vec.push(borrow);
+            self.location_map.insert(location, idx);
+
+            self.insert_as_pending_if_two_phase(location, &assigned_place, region, kind, idx);
+
+            insert(&mut self.region_map, &region, idx);
+            if let Some(local) = borrowed_place.root_local() {
+                insert(&mut self.local_map, &local, idx);
+            }
+        }
+
+        return self.super_assign(block, assigned_place, rvalue, location);
+
+        fn insert<'a, K, V>(map: &'a mut FxHashMap<K, FxHashSet<V>>, k: &K, v: V)
+        where
+            K: Clone + Eq + Hash,
+            V: Eq + Hash,
+        {
+            map.entry(k.clone()).or_insert(FxHashSet()).insert(v);
+        }
+    }
+
+    fn visit_place(
+        &mut self,
+        place: &mir::Place<'tcx>,
+        context: PlaceContext<'tcx>,
+        location: Location,
+    ) {
+        self.super_place(place, context, location);
+
+        // We found a use of some temporary TEMP...
+        if let Place::Local(temp) = place {
+            // ... check whether we (earlier) saw a 2-phase borrow like
+            //
+            //     TMP = &mut place
+            match self.pending_activations.get(temp) {
+                Some(&borrow_index) => {
+                    let borrow_data = &mut self.idx_vec[borrow_index];
+
+                    // Watch out: the use of TMP in the borrow
+                    // itself doesn't count as an
+                    // activation. =)
+                    if borrow_data.reserve_location == location && context == PlaceContext::Store {
+                        return;
+                    }
+
+                    if let Some(other_activation) = borrow_data.activation_location {
+                        span_bug!(
+                            self.mir.source_info(location).span,
+                            "found two activations for 2-phase borrow temporary {:?}: \
+                             {:?} and {:?}",
+                            temp,
+                            location,
+                            other_activation,
+                        );
+                    }
+
+                    // Otherwise, this is the unique later use
+                    // that we expect.
+                    borrow_data.activation_location = Some(location);
+                    self.activation_map
+                        .entry(location)
+                        .or_insert(FxHashSet())
+                        .insert(borrow_index);
+                }
+
+                None => {}
+            }
+        }
+    }
+
+    fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: mir::Location) {
+        if let mir::Rvalue::Ref(region, kind, ref place) = *rvalue {
+            // double-check that we already registered a BorrowData for this
+
+            let borrow_index = self.location_map[&location];
+            let borrow_data = &self.idx_vec[borrow_index];
+            assert_eq!(borrow_data.reserve_location, location);
+            assert_eq!(borrow_data.kind, kind);
+            assert_eq!(borrow_data.region, region);
+            assert_eq!(borrow_data.borrowed_place, *place);
+        }
+
+        return self.super_rvalue(rvalue, location);
+    }
+
+    fn visit_statement(
+        &mut self,
+        block: mir::BasicBlock,
+        statement: &mir::Statement<'tcx>,
+        location: Location,
+    ) {
+        if let mir::StatementKind::EndRegion(region_scope) = statement.kind {
+            self.region_span_map
+                .insert(ty::ReScope(region_scope), statement.source_info.span);
+        }
+        return self.super_statement(block, statement, location);
+    }
+}
+
+impl<'a, 'gcx, 'tcx> GatherBorrows<'a, 'gcx, 'tcx> {
+    /// Returns true if the borrow represented by `kind` is
+    /// allowed to be split into separate Reservation and
+    /// Activation phases.
+    fn allow_two_phase_borrow(&self, kind: mir::BorrowKind) -> bool {
+        self.tcx.two_phase_borrows()
+            && (kind.allows_two_phase_borrow()
+                || self.tcx.sess.opts.debugging_opts.two_phase_beyond_autoref)
+    }
+
+    /// If this is a two-phase borrow, then we will record it
+    /// as "pending" until we find the activating use.
+    fn insert_as_pending_if_two_phase(
+        &mut self,
+        start_location: Location,
+        assigned_place: &mir::Place<'tcx>,
+        region: Region<'tcx>,
+        kind: mir::BorrowKind,
+        borrow_index: BorrowIndex,
+    ) {
+        debug!(
+            "Borrows::insert_as_pending_if_two_phase({:?}, {:?}, {:?}, {:?})",
+            start_location, assigned_place, region, borrow_index,
+        );
+
+        if !self.allow_two_phase_borrow(kind) {
+            debug!("  -> {:?}", start_location);
+            return;
+        }
+
+        // When we encounter a 2-phase borrow statement, it will always
+        // be assigning into a temporary TEMP:
+        //
+        //    TEMP = &foo
+        //
+        // so extract `temp`.
+        let temp = if let &mir::Place::Local(temp) = assigned_place {
+            temp
+        } else {
+            span_bug!(
+                self.mir.source_info(start_location).span,
+                "expected 2-phase borrow to assign to a local, not `{:?}`",
+                assigned_place,
+            );
+        };
+
+        // Insert `temp` into the list of pending activations. From
+        // now on, we'll be on the lookout for a use of it. Note that
+        // we are guaranteed that this use will come after the
+        // assignment.
+        let old_value = self.pending_activations.insert(temp, borrow_index);
+        assert!(old_value.is_none());
+    }
+}
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index 69044a3c83e..6efe0c39d07 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -54,6 +54,7 @@ use self::MutateMode::{JustWrite, WriteAndRead};
 crate mod borrow_set;
 mod error_reporting;
 mod flows;
+crate mod place_ext;
 mod prefixes;
 
 pub(crate) mod nll;
diff --git a/src/librustc_mir/borrow_check/place_ext.rs b/src/librustc_mir/borrow_check/place_ext.rs
new file mode 100644
index 00000000000..f6ffe3c6d23
--- /dev/null
+++ b/src/librustc_mir/borrow_check/place_ext.rs
@@ -0,0 +1,60 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use rustc::hir;
+use rustc::mir::ProjectionElem;
+use rustc::mir::{Local, Mir, Place};
+use rustc::ty::{self, TyCtxt};
+
+/// Extension methods for the `Place` type.
+crate trait PlaceExt<'tcx> {
+    /// True if this is a deref of a raw pointer.
+    fn is_unsafe_place(&self, tcx: TyCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) -> bool;
+
+    /// If this is a place like `x.f.g`, returns the local
+    /// `x`. Returns `None` if this is based in a static.
+    fn root_local(&self) -> Option<Local>;
+}
+
+impl<'tcx> PlaceExt<'tcx> for Place<'tcx> {
+    fn is_unsafe_place(&self, tcx: TyCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) -> bool {
+        match self {
+            Place::Local(_) => false,
+            Place::Static(static_) => {
+                tcx.is_static(static_.def_id) == Some(hir::Mutability::MutMutable)
+            }
+            Place::Projection(proj) => match proj.elem {
+                ProjectionElem::Field(..)
+                | ProjectionElem::Downcast(..)
+                | ProjectionElem::Subslice { .. }
+                | ProjectionElem::ConstantIndex { .. }
+                | ProjectionElem::Index(_) => proj.base.is_unsafe_place(tcx, mir),
+                ProjectionElem::Deref => {
+                    let ty = proj.base.ty(mir, tcx).to_ty(tcx);
+                    match ty.sty {
+                        ty::TyRawPtr(..) => true,
+                        _ => proj.base.is_unsafe_place(tcx, mir),
+                    }
+                }
+            },
+        }
+    }
+
+    fn root_local(&self) -> Option<Local> {
+        let mut p = self;
+        loop {
+            match p {
+                Place::Projection(pi) => p = &pi.base,
+                Place::Static(_) => return None,
+                Place::Local(l) => return Some(*l),
+            }
+        }
+    }
+}
diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs
index 381718234a6..79f2915e12c 100644
--- a/src/librustc_mir/dataflow/impls/borrows.rs
+++ b/src/librustc_mir/dataflow/impls/borrows.rs
@@ -9,18 +9,16 @@
 // except according to those terms.
 
 use borrow_check::borrow_set::{BorrowSet, BorrowData};
+use borrow_check::place_ext::PlaceExt;
 
 use rustc;
 use rustc::hir;
 use rustc::hir::def_id::DefId;
 use rustc::middle::region;
 use rustc::mir::{self, Location, Place, Mir};
-use rustc::mir::traversal;
-use rustc::mir::visit::{PlaceContext, Visitor};
-use rustc::ty::{self, Region, TyCtxt};
+use rustc::ty::{Region, TyCtxt};
 use rustc::ty::RegionKind;
 use rustc::ty::RegionKind::ReScope;
-use rustc::util::nodemap::{FxHashMap, FxHashSet};
 
 use rustc_data_structures::bitslice::{BitwiseOperator};
 use rustc_data_structures::indexed_set::{IdxSet};
@@ -34,7 +32,6 @@ use borrow_check::nll::ToRegionVid;
 
 use syntax_pos::Span;
 
-use std::hash::Hash;
 use std::rc::Rc;
 
 /// `Borrows` stores the data used in the analyses that track the flow
@@ -82,373 +79,15 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
         let root_scope = body_id.map(|body_id| {
             region::Scope::CallSite(tcx.hir.body(body_id).value.hir_id.local_id)
         });
-        let mut visitor = GatherBorrows {
-            tcx,
-            mir,
-            idx_vec: IndexVec::new(),
-            location_map: FxHashMap(),
-            activation_map: FxHashMap(),
-            region_map: FxHashMap(),
-            local_map: FxHashMap(),
-            region_span_map: FxHashMap(),
-            nonlexical_regioncx: nonlexical_regioncx.clone(),
-            pending_activations: FxHashMap(),
-        };
-        for (block, block_data) in traversal::preorder(mir) {
-            visitor.visit_basic_block_data(block, block_data);
-        }
-
-        // Double check: We should have found an activation for every pending
-        // activation.
-        assert_eq!(
-            visitor
-                .pending_activations
-                .iter()
-                .find(|&(_local, &borrow_index)| {
-                    visitor.idx_vec[borrow_index].activation_location.is_none()
-                }),
-            None,
-            "never found an activation for this borrow!",
-        );
-
-        return Borrows { tcx: tcx,
-                         mir: mir,
-                         borrow_set: BorrowSet {
-                             borrows: visitor.idx_vec,
-                             location_map: visitor.location_map,
-                             activation_map: visitor.activation_map,
-                             region_map: visitor.region_map,
-                             local_map: visitor.local_map,
-                             region_span_map: visitor.region_span_map,
-                         },
-                         scope_tree,
-                         root_scope,
-                         nonlexical_regioncx };
-
-        struct GatherBorrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
-            tcx: TyCtxt<'a, 'gcx, 'tcx>,
-            mir: &'a Mir<'tcx>,
-            idx_vec: IndexVec<BorrowIndex, BorrowData<'tcx>>,
-            location_map: FxHashMap<Location, BorrowIndex>,
-            activation_map: FxHashMap<Location, FxHashSet<BorrowIndex>>,
-            region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
-            local_map: FxHashMap<mir::Local, FxHashSet<BorrowIndex>>,
-            region_span_map: FxHashMap<RegionKind, Span>,
-            nonlexical_regioncx: Option<Rc<RegionInferenceContext<'tcx>>>,
-
-            /// When we encounter a 2-phase borrow statement, it will always
-            /// be assigning into a temporary TEMP:
-            ///
-            ///    TEMP = &foo
-            ///
-            /// We add TEMP into this map with `b`, where `b` is the index of
-            /// the borrow. When we find a later use of this activation, we
-            /// remove from the map (and add to the "tombstone" set below).
-            pending_activations: FxHashMap<mir::Local, BorrowIndex>,
-        }
-
-        impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> {
-            fn visit_assign(&mut self,
-                            block: mir::BasicBlock,
-                            assigned_place: &mir::Place<'tcx>,
-                            rvalue: &mir::Rvalue<'tcx>,
-                            location: mir::Location) {
-                fn root_local(mut p: &mir::Place<'_>) -> Option<mir::Local> {
-                    loop { match p {
-                        mir::Place::Projection(pi) => p = &pi.base,
-                        mir::Place::Static(_) => return None,
-                        mir::Place::Local(l) => return Some(*l)
-                    }}
-                }
-
-                if let mir::Rvalue::Ref(region, kind, ref borrowed_place) = *rvalue {
-                    if is_unsafe_place(self.tcx, self.mir, borrowed_place) { return; }
-
-                    let borrow = BorrowData {
-                        kind,
-                        region,
-                        reserve_location: location,
-                        activation_location: None,
-                        borrowed_place: borrowed_place.clone(),
-                        assigned_place: assigned_place.clone(),
-                    };
-                    let idx = self.idx_vec.push(borrow);
-                    self.location_map.insert(location, idx);
-
-                    self.insert_as_pending_if_two_phase(
-                        location,
-                        &assigned_place,
-                        region,
-                        kind,
-                        idx,
-                    );
-
-                    insert(&mut self.region_map, &region, idx);
-                    if let Some(local) = root_local(borrowed_place) {
-                        insert(&mut self.local_map, &local, idx);
-                    }
-                }
-
-                return self.super_assign(block, assigned_place, rvalue, location);
-
-                fn insert<'a, K, V>(map: &'a mut FxHashMap<K, FxHashSet<V>>,
-                                    k: &K,
-                                    v: V)
-                    where K: Clone+Eq+Hash, V: Eq+Hash
-                {
-                    map.entry(k.clone())
-                        .or_insert(FxHashSet())
-                        .insert(v);
-                }
-            }
-
-            fn visit_place(
-                &mut self,
-                place: &mir::Place<'tcx>,
-                context: PlaceContext<'tcx>,
-                location: Location,
-            ) {
-                self.super_place(place, context, location);
-
-                // We found a use of some temporary TEMP...
-                if let Place::Local(temp) = place {
-                    // ... check whether we (earlier) saw a 2-phase borrow like
-                    //
-                    //     TMP = &mut place
-                    match self.pending_activations.get(temp) {
-                        Some(&borrow_index) => {
-                            let borrow_data = &mut self.idx_vec[borrow_index];
-
-                            // Watch out: the use of TMP in the borrow
-                            // itself doesn't count as an
-                            // activation. =)
-                            if borrow_data.reserve_location == location
-                                && context == PlaceContext::Store
-                            {
-                                return;
-                            }
-
-                            if let Some(other_activation) = borrow_data.activation_location {
-                                span_bug!(
-                                    self.mir.source_info(location).span,
-                                    "found two activations for 2-phase borrow temporary {:?}: \
-                                     {:?} and {:?}",
-                                    temp,
-                                    location,
-                                    other_activation,
-                                );
-                            }
-
-                            // Otherwise, this is the unique later use
-                            // that we expect.
-                            borrow_data.activation_location = Some(location);
-                            self.activation_map
-                                .entry(location)
-                                .or_insert(FxHashSet())
-                                .insert(borrow_index);
-                        }
-
-                        None => {}
-                    }
-                }
-            }
-
-            fn visit_rvalue(&mut self,
-                            rvalue: &mir::Rvalue<'tcx>,
-                            location: mir::Location) {
-                if let mir::Rvalue::Ref(region, kind, ref place) = *rvalue {
-                    // double-check that we already registered a BorrowData for this
-
-                    let borrow_index = self.location_map[&location];
-                    let borrow_data = &self.idx_vec[borrow_index];
-                    assert_eq!(borrow_data.reserve_location, location);
-                    assert_eq!(borrow_data.kind, kind);
-                    assert_eq!(borrow_data.region, region);
-                    assert_eq!(borrow_data.borrowed_place, *place);
-                }
-
-                return self.super_rvalue(rvalue, location);
-            }
-
-            fn visit_statement(&mut self,
-                               block: mir::BasicBlock,
-                               statement: &mir::Statement<'tcx>,
-                               location: Location) {
-                if let mir::StatementKind::EndRegion(region_scope) = statement.kind {
-                    self.region_span_map.insert(ReScope(region_scope), statement.source_info.span);
-                }
-                return self.super_statement(block, statement, location);
-            }
-        }
-
-        /// A MIR visitor that determines if a specific place is used in a two-phase activating
-        /// manner in a given chunk of MIR.
-        struct ContainsUseOfPlace<'b, 'tcx: 'b> {
-            target: &'b Place<'tcx>,
-            use_found: bool,
-        }
-
-        impl<'b, 'tcx: 'b> ContainsUseOfPlace<'b, 'tcx> {
-            fn new(place: &'b Place<'tcx>) -> Self {
-                Self { target: place, use_found: false }
-            }
-
-            /// return whether `context` should be considered a "use" of a
-            /// place found in that context. "Uses" activate associated
-            /// borrows (at least when such uses occur while the borrow also
-            /// has a reservation at the time).
-            fn is_potential_use(context: PlaceContext) -> bool {
-                match context {
-                    // storage effects on a place do not activate it
-                    PlaceContext::StorageLive | PlaceContext::StorageDead => false,
-
-                    // validation effects do not activate a place
-                    //
-                    // FIXME: Should they? Is it just another read? Or can we
-                    // guarantee it won't dereference the stored address? How
-                    // "deep" does validation go?
-                    PlaceContext::Validate => false,
-
-                    // FIXME: This is here to not change behaviour from before
-                    // AsmOutput existed, but it's not necessarily a pure overwrite.
-                    // so it's possible this should activate the place.
-                    PlaceContext::AsmOutput |
-                    // pure overwrites of a place do not activate it. (note
-                    // PlaceContext::Call is solely about dest place)
-                    PlaceContext::Store | PlaceContext::Call => false,
-
-                    // reads of a place *do* activate it
-                    PlaceContext::Move |
-                    PlaceContext::Copy |
-                    PlaceContext::Drop |
-                    PlaceContext::Inspect |
-                    PlaceContext::Borrow { .. } |
-                    PlaceContext::Projection(..) => true,
-                }
-            }
-        }
-
-        impl<'b, 'tcx: 'b> Visitor<'tcx> for ContainsUseOfPlace<'b, 'tcx> {
-            fn visit_place(&mut self,
-                           place: &mir::Place<'tcx>,
-                           context: PlaceContext<'tcx>,
-                           location: Location) {
-                if Self::is_potential_use(context) && place == self.target {
-                    self.use_found = true;
-                    return;
-                    // There is no need to keep checking the statement, we already found a use
-                }
-
-                self.super_place(place, context, location);
-            }
-        }
-
-        impl<'a, 'gcx, 'tcx> GatherBorrows<'a, 'gcx, 'tcx> {
-            /// Returns true if the borrow represented by `kind` is
-            /// allowed to be split into separate Reservation and
-            /// Activation phases.
-            fn allow_two_phase_borrow(&self, kind: mir::BorrowKind) -> bool {
-                self.tcx.two_phase_borrows() &&
-                    (kind.allows_two_phase_borrow() ||
-                     self.tcx.sess.opts.debugging_opts.two_phase_beyond_autoref)
-            }
-
-            /// Returns true if the given location contains an NLL-activating use of the given place
-            fn location_contains_use(&self, location: Location, place: &Place) -> bool {
-                let mut use_checker = ContainsUseOfPlace::new(place);
-                let block = &self.mir.basic_blocks().get(location.block).unwrap_or_else(|| {
-                    panic!("could not find block at location {:?}", location);
-                });
-                if location.statement_index != block.statements.len() {
-                    // This is a statement
-                    let stmt = block.statements.get(location.statement_index).unwrap_or_else(|| {
-                        panic!("could not find statement at location {:?}");
-                    });
-                    use_checker.visit_statement(location.block, stmt, location);
-                } else {
-                    // This is a terminator
-                    match block.terminator {
-                        Some(ref term) => {
-                            use_checker.visit_terminator(location.block, term, location);
-                        }
-                        None => {
-                            // There is no way for Place to be used by the terminator if there is no
-                            // terminator
-                        }
-                    }
-                }
-
-                use_checker.use_found
-            }
-
-            /// Determines if the provided region is terminated after the provided location.
-            /// EndRegion statements terminate their enclosed region::Scope.
-            /// We also consult with the NLL region inference engine, should one be available
-            fn region_terminated_after(&self, region: Region<'tcx>, location: Location) -> bool {
-                let block_data = &self.mir[location.block];
-                if location.statement_index != block_data.statements.len() {
-                    let stmt = &block_data.statements[location.statement_index];
-                    if let mir::StatementKind::EndRegion(region_scope) = stmt.kind {
-                        if &ReScope(region_scope) == region {
-                            // We encountered an EndRegion statement that terminates the provided
-                            // region
-                            return true;
-                        }
-                    }
-                }
-                if let Some(ref regioncx) = self.nonlexical_regioncx {
-                    if !regioncx.region_contains_point(region, location) {
-                        // NLL says the region has ended already
-                        return true;
-                    }
-                }
-
-                false
-            }
-
-            /// If this is a two-phase borrow, then we will record it
-            /// as "pending" until we find the activating use.
-            fn insert_as_pending_if_two_phase(
-                &mut self,
-                start_location: Location,
-                assigned_place: &mir::Place<'tcx>,
-                region: Region<'tcx>,
-                kind: mir::BorrowKind,
-                borrow_index: BorrowIndex,
-            ) {
-                debug!(
-                    "Borrows::insert_as_pending_if_two_phase({:?}, {:?}, {:?}, {:?})",
-                    start_location, assigned_place, region, borrow_index,
-                );
-
-                if !self.allow_two_phase_borrow(kind) {
-                    debug!("  -> {:?}", start_location);
-                    return;
-                }
-
-                // When we encounter a 2-phase borrow statement, it will always
-                // be assigning into a temporary TEMP:
-                //
-                //    TEMP = &foo
-                //
-                // so extract `temp`.
-                let temp = if let &mir::Place::Local(temp) = assigned_place {
-                    temp
-                } else {
-                    span_bug!(
-                        self.mir.source_info(start_location).span,
-                        "expected 2-phase borrow to assign to a local, not `{:?}`",
-                        assigned_place,
-                    );
-                };
-
-                // Insert `temp` into the list of pending activations. From
-                // now on, we'll be on the lookout for a use of it. Note that
-                // we are guaranteed that this use will come after the
-                // assignment.
-                let old_value = self.pending_activations.insert(temp, borrow_index);
-                assert!(old_value.is_none());
-            }
+        let borrow_set = BorrowSet::build(tcx, mir);
+
+        Borrows {
+            tcx: tcx,
+            mir: mir,
+            borrow_set,
+            scope_tree,
+            root_scope,
+            nonlexical_regioncx,
         }
     }
 
@@ -597,7 +236,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
                 // propagate_call_return method.
 
                 if let mir::Rvalue::Ref(region, _, ref place) = *rhs {
-                    if is_unsafe_place(self.tcx, self.mir, place) { return; }
+                    if place.is_unsafe_place(self.tcx, self.mir) { return; }
                     let index = self.borrow_set.location_map.get(&location).unwrap_or_else(|| {
                         panic!("could not find BorrowIndex for location {:?}", location);
                     });
@@ -744,34 +383,3 @@ impl<'a, 'gcx, 'tcx> InitialFlow for Borrows<'a, 'gcx, 'tcx> {
     }
 }
 
-fn is_unsafe_place<'a, 'gcx: 'tcx, 'tcx: 'a>(
-    tcx: TyCtxt<'a, 'gcx, 'tcx>,
-    mir: &'a Mir<'tcx>,
-    place: &mir::Place<'tcx>
-) -> bool {
-    use self::mir::Place::*;
-    use self::mir::ProjectionElem;
-
-    match *place {
-        Local(_) => false,
-        Static(ref static_) => tcx.is_static(static_.def_id) == Some(hir::Mutability::MutMutable),
-        Projection(ref proj) => {
-            match proj.elem {
-                ProjectionElem::Field(..) |
-                ProjectionElem::Downcast(..) |
-                ProjectionElem::Subslice { .. } |
-                ProjectionElem::ConstantIndex { .. } |
-                ProjectionElem::Index(_) => {
-                    is_unsafe_place(tcx, mir, &proj.base)
-                }
-                ProjectionElem::Deref => {
-                    let ty = proj.base.ty(mir, tcx).to_ty(tcx);
-                    match ty.sty {
-                        ty::TyRawPtr(..) => true,
-                        _ => is_unsafe_place(tcx, mir, &proj.base),
-                    }
-                }
-            }
-        }
-    }
-}