From e1123674b1c719e0a5ced078c15a81a22e16dec0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 6 Apr 2018 15:53:49 -0400 Subject: [PATCH] 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. --- src/librustc_mir/borrow_check/borrow_set.rs | 252 +++++++++++- src/librustc_mir/borrow_check/mod.rs | 1 + src/librustc_mir/borrow_check/place_ext.rs | 60 +++ src/librustc_mir/dataflow/impls/borrows.rs | 414 +------------------- 4 files changed, 321 insertions(+), 406 deletions(-) create mode 100644 src/librustc_mir/borrow_check/place_ext.rs 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>, + location_map: FxHashMap, + activation_map: FxHashMap>, + region_map: FxHashMap, FxHashSet>, + local_map: FxHashMap>, + region_span_map: FxHashMap, + + /// 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, +} + +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, ®ion, 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: &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 or the MIT license +// , 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; +} + +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 { + 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); - } + let borrow_set = BorrowSet::build(tcx, mir); - // 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>, - location_map: FxHashMap, - activation_map: FxHashMap>, - region_map: FxHashMap, FxHashSet>, - local_map: FxHashMap>, - region_span_map: FxHashMap, - nonlexical_regioncx: Option>>, - - /// 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, - } - - 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 { - 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, ®ion, 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: &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()); - } + 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), - } - } - } - } - } -}