blob: c0f059619a4974b59e5264c1e8f7a4ee4c980a3f [file] [log] [blame]
// Copyright 2018 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 borrow_check::ArtificialField;
use borrow_check::Overlap;
use borrow_check::{Deep, Shallow, AccessDepth};
use rustc::hir;
use rustc::mir::{BorrowKind, Mir, Place};
use rustc::mir::{Projection, ProjectionElem};
use rustc::ty::{self, TyCtxt};
use std::cmp::max;
pub(super) fn borrow_conflicts_with_place<'gcx, 'tcx>(
tcx: TyCtxt<'_, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
borrow_place: &Place<'tcx>,
borrow_kind: BorrowKind,
access_place: &Place<'tcx>,
access: AccessDepth,
) -> bool {
debug!(
"borrow_conflicts_with_place({:?},{:?},{:?})",
borrow_place, access_place, access
);
// This Local/Local case is handled by the more general code below, but
// it's so common that it's a speed win to check for it first.
if let Place::Local(l1) = borrow_place {
if let Place::Local(l2) = access_place {
return l1 == l2;
}
}
unroll_place(borrow_place, None, |borrow_components| {
unroll_place(access_place, None, |access_components| {
place_components_conflict(
tcx,
mir,
borrow_components,
borrow_kind,
access_components,
access
)
})
})
}
fn place_components_conflict<'gcx, 'tcx>(
tcx: TyCtxt<'_, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
mut borrow_components: PlaceComponentsIter<'_, 'tcx>,
borrow_kind: BorrowKind,
mut access_components: PlaceComponentsIter<'_, 'tcx>,
access: AccessDepth,
) -> bool {
// The borrowck rules for proving disjointness are applied from the "root" of the
// borrow forwards, iterating over "similar" projections in lockstep until
// we can prove overlap one way or another. Essentially, we treat `Overlap` as
// a monoid and report a conflict if the product ends up not being `Disjoint`.
//
// At each step, if we didn't run out of borrow or place, we know that our elements
// have the same type, and that they only overlap if they are the identical.
//
// For example, if we are comparing these:
// BORROW: (*x1[2].y).z.a
// ACCESS: (*x1[i].y).w.b
//
// Then our steps are:
// x1 | x1 -- places are the same
// x1[2] | x1[i] -- equal or disjoint (disjoint if indexes differ)
// x1[2].y | x1[i].y -- equal or disjoint
// *x1[2].y | *x1[i].y -- equal or disjoint
// (*x1[2].y).z | (*x1[i].y).w -- we are disjoint and don't need to check more!
//
// Because `zip` does potentially bad things to the iterator inside, this loop
// also handles the case where the access might be a *prefix* of the borrow, e.g.
//
// BORROW: (*x1[2].y).z.a
// ACCESS: x1[i].y
//
// Then our steps are:
// x1 | x1 -- places are the same
// x1[2] | x1[i] -- equal or disjoint (disjoint if indexes differ)
// x1[2].y | x1[i].y -- equal or disjoint
//
// -- here we run out of access - the borrow can access a part of it. If this
// is a full deep access, then we *know* the borrow conflicts with it. However,
// if the access is shallow, then we can proceed:
//
// x1[2].y | (*x1[i].y) -- a deref! the access can't get past this, so we
// are disjoint
//
// Our invariant is, that at each step of the iteration:
// - If we didn't run out of access to match, our borrow and access are comparable
// and either equal or disjoint.
// - If we did run out of access, the borrow can access a part of it.
loop {
// loop invariant: borrow_c is always either equal to access_c or disjoint from it.
if let Some(borrow_c) = borrow_components.next() {
debug!("borrow_conflicts_with_place: borrow_c = {:?}", borrow_c);
if let Some(access_c) = access_components.next() {
debug!("borrow_conflicts_with_place: access_c = {:?}", access_c);
// Borrow and access path both have more components.
//
// Examples:
//
// - borrow of `a.(...)`, access to `a.(...)`
// - borrow of `a.(...)`, access to `b.(...)`
//
// Here we only see the components we have checked so
// far (in our examples, just the first component). We
// check whether the components being borrowed vs
// accessed are disjoint (as in the second example,
// but not the first).
match place_element_conflict(tcx, mir, borrow_c, access_c) {
Overlap::Arbitrary => {
// We have encountered different fields of potentially
// the same union - the borrow now partially overlaps.
//
// There is no *easy* way of comparing the fields
// further on, because they might have different types
// (e.g. borrows of `u.a.0` and `u.b.y` where `.0` and
// `.y` come from different structs).
//
// We could try to do some things here - e.g. count
// dereferences - but that's probably not a good
// idea, at least for now, so just give up and
// report a conflict. This is unsafe code anyway so
// the user could always use raw pointers.
debug!("borrow_conflicts_with_place: arbitrary -> conflict");
return true;
}
Overlap::EqualOrDisjoint => {
// This is the recursive case - proceed to the next element.
}
Overlap::Disjoint => {
// We have proven the borrow disjoint - further
// projections will remain disjoint.
debug!("borrow_conflicts_with_place: disjoint");
return false;
}
}
} else {
// Borrow path is longer than the access path. Examples:
//
// - borrow of `a.b.c`, access to `a.b`
//
// Here, we know that the borrow can access a part of
// our place. This is a conflict if that is a part our
// access cares about.
let (base, elem) = match borrow_c {
Place::Projection(box Projection { base, elem }) => (base, elem),
_ => bug!("place has no base?"),
};
let base_ty = base.ty(mir, tcx).to_ty(tcx);
match (elem, &base_ty.sty, access) {
(_, _, Shallow(Some(ArtificialField::Discriminant)))
| (_, _, Shallow(Some(ArtificialField::ArrayLength)))
| (_, _, Shallow(Some(ArtificialField::ShallowBorrow))) => {
// The discriminant and array length are like
// additional fields on the type; they do not
// overlap any existing data there. Furthermore,
// they cannot actually be a prefix of any
// borrowed place (at least in MIR as it is
// currently.)
//
// e.g. a (mutable) borrow of `a[5]` while we read the
// array length of `a`.
debug!("borrow_conflicts_with_place: implicit field");
return false;
}
(ProjectionElem::Deref, _, Shallow(None)) => {
// e.g. a borrow of `*x.y` while we shallowly access `x.y` or some
// prefix thereof - the shallow access can't touch anything behind
// the pointer.
debug!("borrow_conflicts_with_place: shallow access behind ptr");
return false;
}
(ProjectionElem::Deref, ty::Ref(_, _, hir::MutImmutable), _) => {
// Shouldn't be tracked
bug!("Tracking borrow behind shared reference.");
}
(ProjectionElem::Deref, ty::Ref(_, _, hir::MutMutable), AccessDepth::Drop) => {
// Values behind a mutatble reference are not access either by Dropping a
// value, or by StorageDead
debug!("borrow_conflicts_with_place: drop access behind ptr");
return false;
}
(ProjectionElem::Field { .. }, ty::Adt(def, _), AccessDepth::Drop) => {
// Drop can read/write arbitrary projections, so places
// conflict regardless of further projections.
if def.has_dtor(tcx) {
return true;
}
}
(ProjectionElem::Deref, _, Deep)
| (ProjectionElem::Deref, _, AccessDepth::Drop)
| (ProjectionElem::Field { .. }, _, _)
| (ProjectionElem::Index { .. }, _, _)
| (ProjectionElem::ConstantIndex { .. }, _, _)
| (ProjectionElem::Subslice { .. }, _, _)
| (ProjectionElem::Downcast { .. }, _, _) => {
// Recursive case. This can still be disjoint on a
// further iteration if this a shallow access and
// there's a deref later on, e.g. a borrow
// of `*x.y` while accessing `x`.
}
}
}
} else {
// Borrow path ran out but access path may not
// have. Examples:
//
// - borrow of `a.b`, access to `a.b.c`
// - borrow of `a.b`, access to `a.b`
//
// In the first example, where we didn't run out of
// access, the borrow can access all of our place, so we
// have a conflict.
//
// If the second example, where we did, then we still know
// that the borrow can access a *part* of our place that
// our access cares about, so we still have a conflict.
if borrow_kind == BorrowKind::Shallow && access_components.next().is_some() {
debug!("borrow_conflicts_with_place: shallow borrow");
return false;
} else {
debug!("borrow_conflicts_with_place: full borrow, CONFLICT");
return true;
}
}
}
}
/// A linked list of places running up the stack; begins with the
/// innermost place and extends to projections (e.g., `a.b` would have
/// the place `a` with a "next" pointer to `a.b`). Created by
/// `unroll_place`.
///
/// NB: This particular impl strategy is not the most obvious. It was
/// chosen because it makes a measurable difference to NLL
/// performance, as this code (`borrow_conflicts_with_place`) is somewhat hot.
struct PlaceComponents<'p, 'tcx: 'p> {
component: &'p Place<'tcx>,
next: Option<&'p PlaceComponents<'p, 'tcx>>,
}
impl<'p, 'tcx> PlaceComponents<'p, 'tcx> {
/// Converts a list of `Place` components into an iterator; this
/// iterator yields up a never-ending stream of `Option<&Place>`.
/// These begin with the "innermst" place and then with each
/// projection therefrom. So given a place like `a.b.c` it would
/// yield up:
///
/// ```notrust
/// Some(`a`), Some(`a.b`), Some(`a.b.c`), None, None, ...
/// ```
fn iter(&self) -> PlaceComponentsIter<'_, 'tcx> {
PlaceComponentsIter { value: Some(self) }
}
}
/// Iterator over components; see `PlaceComponents::iter` for more
/// information.
///
/// NB: This is not a *true* Rust iterator -- the code above just
/// manually invokes `next`. This is because we (sometimes) want to
/// keep executing even after `None` has been returned.
struct PlaceComponentsIter<'p, 'tcx: 'p> {
value: Option<&'p PlaceComponents<'p, 'tcx>>,
}
impl<'p, 'tcx> PlaceComponentsIter<'p, 'tcx> {
fn next(&mut self) -> Option<&'p Place<'tcx>> {
if let Some(&PlaceComponents { component, next }) = self.value {
self.value = next;
Some(component)
} else {
None
}
}
}
/// Recursively "unroll" a place into a `PlaceComponents` list,
/// invoking `op` with a `PlaceComponentsIter`.
fn unroll_place<'tcx, R>(
place: &Place<'tcx>,
next: Option<&PlaceComponents<'_, 'tcx>>,
op: impl FnOnce(PlaceComponentsIter<'_, 'tcx>) -> R,
) -> R {
match place {
Place::Projection(interior) => unroll_place(
&interior.base,
Some(&PlaceComponents {
component: place,
next,
}),
op,
),
Place::Promoted(_) |
Place::Local(_) | Place::Static(_) => {
let list = PlaceComponents {
component: place,
next,
};
op(list.iter())
}
}
}
// Given that the bases of `elem1` and `elem2` are always either equal
// or disjoint (and have the same type!), return the overlap situation
// between `elem1` and `elem2`.
fn place_element_conflict<'a, 'gcx: 'tcx, 'tcx>(
tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
elem1: &Place<'tcx>,
elem2: &Place<'tcx>,
) -> Overlap {
match (elem1, elem2) {
(Place::Local(l1), Place::Local(l2)) => {
if l1 == l2 {
// the same local - base case, equal
debug!("place_element_conflict: DISJOINT-OR-EQ-LOCAL");
Overlap::EqualOrDisjoint
} else {
// different locals - base case, disjoint
debug!("place_element_conflict: DISJOINT-LOCAL");
Overlap::Disjoint
}
}
(Place::Static(static1), Place::Static(static2)) => {
if static1.def_id != static2.def_id {
debug!("place_element_conflict: DISJOINT-STATIC");
Overlap::Disjoint
} else if tcx.is_static(static1.def_id) == Some(hir::Mutability::MutMutable) {
// We ignore mutable statics - they can only be unsafe code.
debug!("place_element_conflict: IGNORE-STATIC-MUT");
Overlap::Disjoint
} else {
debug!("place_element_conflict: DISJOINT-OR-EQ-STATIC");
Overlap::EqualOrDisjoint
}
}
(Place::Promoted(p1), Place::Promoted(p2)) => {
if p1.0 == p2.0 {
if let ty::Array(_, size) = p1.1.sty {
if size.unwrap_usize(tcx) == 0 {
// Ignore conflicts with promoted [T; 0].
debug!("place_element_conflict: IGNORE-LEN-0-PROMOTED");
return Overlap::Disjoint;
}
}
// the same promoted - base case, equal
debug!("place_element_conflict: DISJOINT-OR-EQ-PROMOTED");
Overlap::EqualOrDisjoint
} else {
// different promoteds - base case, disjoint
debug!("place_element_conflict: DISJOINT-PROMOTED");
Overlap::Disjoint
}
}
(Place::Local(_), Place::Promoted(_)) | (Place::Promoted(_), Place::Local(_)) |
(Place::Promoted(_), Place::Static(_)) | (Place::Static(_), Place::Promoted(_)) |
(Place::Local(_), Place::Static(_)) | (Place::Static(_), Place::Local(_)) => {
debug!("place_element_conflict: DISJOINT-STATIC-LOCAL-PROMOTED");
Overlap::Disjoint
}
(Place::Projection(pi1), Place::Projection(pi2)) => {
match (&pi1.elem, &pi2.elem) {
(ProjectionElem::Deref, ProjectionElem::Deref) => {
// derefs (e.g. `*x` vs. `*x`) - recur.
debug!("place_element_conflict: DISJOINT-OR-EQ-DEREF");
Overlap::EqualOrDisjoint
}
(ProjectionElem::Field(f1, _), ProjectionElem::Field(f2, _)) => {
if f1 == f2 {
// same field (e.g. `a.y` vs. `a.y`) - recur.
debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD");
Overlap::EqualOrDisjoint
} else {
let ty = pi1.base.ty(mir, tcx).to_ty(tcx);
match ty.sty {
ty::Adt(def, _) if def.is_union() => {
// Different fields of a union, we are basically stuck.
debug!("place_element_conflict: STUCK-UNION");
Overlap::Arbitrary
}
_ => {
// Different fields of a struct (`a.x` vs. `a.y`). Disjoint!
debug!("place_element_conflict: DISJOINT-FIELD");
Overlap::Disjoint
}
}
}
}
(ProjectionElem::Downcast(_, v1), ProjectionElem::Downcast(_, v2)) => {
// different variants are treated as having disjoint fields,
// even if they occupy the same "space", because it's
// impossible for 2 variants of the same enum to exist
// (and therefore, to be borrowed) at the same time.
//
// Note that this is different from unions - we *do* allow
// this code to compile:
//
// ```
// fn foo(x: &mut Result<i32, i32>) {
// let mut v = None;
// if let Ok(ref mut a) = *x {
// v = Some(a);
// }
// // here, you would *think* that the
// // *entirety* of `x` would be borrowed,
// // but in fact only the `Ok` variant is,
// // so the `Err` variant is *entirely free*:
// if let Err(ref mut a) = *x {
// v = Some(a);
// }
// drop(v);
// }
// ```
if v1 == v2 {
debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD");
Overlap::EqualOrDisjoint
} else {
debug!("place_element_conflict: DISJOINT-FIELD");
Overlap::Disjoint
}
}
(ProjectionElem::Index(..), ProjectionElem::Index(..))
| (ProjectionElem::Index(..), ProjectionElem::ConstantIndex { .. })
| (ProjectionElem::Index(..), ProjectionElem::Subslice { .. })
| (ProjectionElem::ConstantIndex { .. }, ProjectionElem::Index(..))
| (ProjectionElem::Subslice { .. }, ProjectionElem::Index(..)) => {
// Array indexes (`a[0]` vs. `a[i]`). These can either be disjoint
// (if the indexes differ) or equal (if they are the same), so this
// is the recursive case that gives "equal *or* disjoint" its meaning.
debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-INDEX");
Overlap::EqualOrDisjoint
}
(ProjectionElem::ConstantIndex { offset: o1, min_length: _, from_end: false },
ProjectionElem::ConstantIndex { offset: o2, min_length: _, from_end: false })
| (ProjectionElem::ConstantIndex { offset: o1, min_length: _, from_end: true },
ProjectionElem::ConstantIndex {
offset: o2, min_length: _, from_end: true }) => {
if o1 == o2 {
debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX");
Overlap::EqualOrDisjoint
} else {
debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX");
Overlap::Disjoint
}
}
(ProjectionElem::ConstantIndex {
offset: offset_from_begin, min_length: min_length1, from_end: false },
ProjectionElem::ConstantIndex {
offset: offset_from_end, min_length: min_length2, from_end: true })
| (ProjectionElem::ConstantIndex {
offset: offset_from_end, min_length: min_length1, from_end: true },
ProjectionElem::ConstantIndex {
offset: offset_from_begin, min_length: min_length2, from_end: false }) => {
// both patterns matched so it must be at least the greater of the two
let min_length = max(min_length1, min_length2);
// `offset_from_end` can be in range `[1..min_length]`, 1 indicates the last
// element (like -1 in Python) and `min_length` the first.
// Therefore, `min_length - offset_from_end` gives the minimal possible
// offset from the beginning
if *offset_from_begin >= min_length - offset_from_end {
debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-FE");
Overlap::EqualOrDisjoint
} else {
debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-FE");
Overlap::Disjoint
}
}
(ProjectionElem::ConstantIndex { offset, min_length: _, from_end: false },
ProjectionElem::Subslice {from, .. })
| (ProjectionElem::Subslice {from, .. },
ProjectionElem::ConstantIndex { offset, min_length: _, from_end: false }) => {
if offset >= from {
debug!(
"place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-SUBSLICE");
Overlap::EqualOrDisjoint
} else {
debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-SUBSLICE");
Overlap::Disjoint
}
}
(ProjectionElem::ConstantIndex { offset, min_length: _, from_end: true },
ProjectionElem::Subslice {from: _, to })
| (ProjectionElem::Subslice {from: _, to },
ProjectionElem::ConstantIndex { offset, min_length: _, from_end: true }) => {
if offset > to {
debug!("place_element_conflict: \
DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-SUBSLICE-FE");
Overlap::EqualOrDisjoint
} else {
debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-SUBSLICE-FE");
Overlap::Disjoint
}
}
(ProjectionElem::Subslice { .. }, ProjectionElem::Subslice { .. }) => {
debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-SUBSLICES");
Overlap::EqualOrDisjoint
}
(ProjectionElem::Deref, _)
| (ProjectionElem::Field(..), _)
| (ProjectionElem::Index(..), _)
| (ProjectionElem::ConstantIndex { .. }, _)
| (ProjectionElem::Subslice { .. }, _)
| (ProjectionElem::Downcast(..), _) => bug!(
"mismatched projections in place_element_conflict: {:?} and {:?}",
elem1,
elem2
),
}
}
(Place::Projection(_), _) | (_, Place::Projection(_)) => bug!(
"unexpected elements in place_element_conflict: {:?} and {:?}",
elem1,
elem2
),
}
}