1
Fork 0

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.
This commit is contained in:
Niko Matsakis 2018-04-06 15:53:49 -04:00
parent 70592664b6
commit e1123674b1
4 changed files with 321 additions and 406 deletions

View file

@ -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());
}
}

View file

@ -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;

View file

@ -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),
}
}
}
}

View file

@ -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,
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,
},
borrow_set,
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());
}
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),
}
}
}
}
}
}