use crate::borrow_check::Overlap;
use crate::borrow_check::{Deep, Shallow, AccessDepth};
use rustc::hir;
-use rustc::mir::{BorrowKind, Mir, Place, PlaceBase, Projection, ProjectionElem, StaticKind};
+use rustc::mir::{
+ BorrowKind, Body, Place, PlaceBase, Projection, ProjectionElem, ProjectionsIter,
+ StaticKind
+};
use rustc::ty::{self, TyCtxt};
use std::cmp::max;
/// dataflow).
crate fn places_conflict<'gcx, 'tcx>(
tcx: TyCtxt<'_, 'gcx, 'tcx>,
- mir: &Mir<'tcx>,
+ mir: &Body<'tcx>,
borrow_place: &Place<'tcx>,
access_place: &Place<'tcx>,
bias: PlaceConflictBias,
/// order to make the conservative choice and preserve soundness.
pub(super) fn borrow_conflicts_with_place<'gcx, 'tcx>(
tcx: TyCtxt<'_, 'gcx, 'tcx>,
- mir: &Mir<'tcx>,
+ mir: &Body<'tcx>,
borrow_place: &Place<'tcx>,
borrow_kind: BorrowKind,
access_place: &Place<'tcx>,
}
}
- unroll_place(borrow_place, None, |borrow_components| {
- unroll_place(access_place, None, |access_components| {
+ borrow_place.iterate(|borrow_base, borrow_projections| {
+ access_place.iterate(|access_base, access_projections| {
place_components_conflict(
tcx,
mir,
- borrow_components,
+ (borrow_base, borrow_projections),
borrow_kind,
- access_components,
+ (access_base, access_projections),
access,
bias,
)
fn place_components_conflict<'gcx, 'tcx>(
tcx: TyCtxt<'_, 'gcx, 'tcx>,
- mir: &Mir<'tcx>,
- mut borrow_components: PlaceComponentsIter<'_, 'tcx>,
+ mir: &Body<'tcx>,
+ borrow_projections: (&PlaceBase<'tcx>, ProjectionsIter<'_, 'tcx>),
borrow_kind: BorrowKind,
- mut access_components: PlaceComponentsIter<'_, 'tcx>,
+ access_projections: (&PlaceBase<'tcx>, ProjectionsIter<'_, 'tcx>),
access: AccessDepth,
bias: PlaceConflictBias,
) -> bool {
// - 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.
+
+ let borrow_base = borrow_projections.0;
+ let access_base = access_projections.0;
+
+ match place_base_conflict(tcx, borrow_base, access_base) {
+ Overlap::Arbitrary => {
+ bug!("Two base can't return Arbitrary");
+ }
+ 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;
+ }
+ }
+
+ let mut borrow_projections = borrow_projections.1;
+ let mut access_projections = access_projections.1;
+
loop {
// loop invariant: borrow_c is always either equal to access_c or disjoint from it.
- if let Some(borrow_c) = borrow_components.next() {
+ if let Some(borrow_c) = borrow_projections.next() {
debug!("borrow_conflicts_with_place: borrow_c = {:?}", borrow_c);
- if let Some(access_c) = access_components.next() {
+ if let Some(access_c) = access_projections.next() {
debug!("borrow_conflicts_with_place: access_c = {:?}", access_c);
// Borrow and access path both have more components.
// 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, bias) {
+ match place_projection_conflict(tcx, mir, borrow_c, access_c, bias) {
Overlap::Arbitrary => {
// We have encountered different fields of potentially
// the same union - the borrow now partially overlaps.
// 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 = &borrow_c.base;
+ let elem = &borrow_c.elem;
let base_ty = base.ty(mir, tcx).ty;
match (elem, &base_ty.sty, access) {
// 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() {
+ if borrow_kind == BorrowKind::Shallow && access_projections.next().is_some() {
debug!("borrow_conflicts_with_place: shallow borrow");
return false;
} else {
}
}
-/// 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`.
-///
-/// N.B., 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 "innermost" 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.
-///
-/// N.B., 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::Base(PlaceBase::Local(_)) | Place::Base(PlaceBase::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>(
+fn place_base_conflict<'a, 'gcx: 'tcx, 'tcx>(
tcx: TyCtxt<'a, 'gcx, 'tcx>,
- mir: &Mir<'tcx>,
- elem1: &Place<'tcx>,
- elem2: &Place<'tcx>,
- bias: PlaceConflictBias,
+ elem1: &PlaceBase<'tcx>,
+ elem2: &PlaceBase<'tcx>,
) -> Overlap {
match (elem1, elem2) {
- (Place::Base(PlaceBase::Local(l1)), Place::Base(PlaceBase::Local(l2))) => {
+ (PlaceBase::Local(l1), PlaceBase::Local(l2)) => {
if l1 == l2 {
// the same local - base case, equal
debug!("place_element_conflict: DISJOINT-OR-EQ-LOCAL");
Overlap::Disjoint
}
}
- (Place::Base(PlaceBase::Static(s1)), Place::Base(PlaceBase::Static(s2))) => {
+ (PlaceBase::Static(s1), PlaceBase::Static(s2)) => {
match (&s1.kind, &s2.kind) {
(StaticKind::Static(def_id_1), StaticKind::Static(def_id_2)) => {
if def_id_1 != def_id_2 {
debug!("place_element_conflict: DISJOINT-STATIC");
Overlap::Disjoint
- } else if tcx.is_static(*def_id_1) == Some(hir::Mutability::MutMutable) {
+ } else if tcx.is_mutable_static(*def_id_1) {
// We ignore mutable statics - they can only be unsafe code.
debug!("place_element_conflict: IGNORE-STATIC-MUT");
Overlap::Disjoint
},
(StaticKind::Promoted(promoted_1), StaticKind::Promoted(promoted_2)) => {
if promoted_1 == promoted_2 {
- if let ty::Array(_, size) = s1.ty.sty {
- if size.unwrap_usize(tcx) == 0 {
+ if let ty::Array(_, len) = s1.ty.sty {
+ if let Some(0) = len.assert_usize(tcx) {
// Ignore conflicts with promoted [T; 0].
debug!("place_element_conflict: IGNORE-LEN-0-PROMOTED");
return Overlap::Disjoint;
}
}
}
- (Place::Base(PlaceBase::Local(_)), Place::Base(PlaceBase::Static(_))) |
- (Place::Base(PlaceBase::Static(_)), Place::Base(PlaceBase::Local(_))) => {
+ (PlaceBase::Local(_), PlaceBase::Static(_)) |
+ (PlaceBase::Static(_), PlaceBase::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).ty;
- 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
- }
- }
+ }
+}
+
+// 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_projection_conflict<'a, 'gcx: 'tcx, 'tcx>(
+ tcx: TyCtxt<'a, 'gcx, 'tcx>,
+ mir: &Body<'tcx>,
+ pi1: &Projection<'tcx>,
+ pi2: &Projection<'tcx>,
+ bias: PlaceConflictBias,
+) -> Overlap {
+ 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).ty;
+ 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
}
- }
- (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 {
+ _ => {
+ // Different fields of a struct (`a.x` vs. `a.y`). Disjoint!
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).
- match bias {
- PlaceConflictBias::Overlap => {
- // If we are biased towards overlapping, then this is the recursive
- // case that gives "equal *or* disjoint" its meaning.
- debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-INDEX");
- Overlap::EqualOrDisjoint
- }
- PlaceConflictBias::NoOverlap => {
- // If we are biased towards no overlapping, then this is disjoint.
- debug!("place_element_conflict: DISJOINT-ARRAY-INDEX");
- Overlap::Disjoint
- }
- }
- }
- (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::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).
+ match bias {
+ PlaceConflictBias::Overlap => {
+ // If we are biased towards overlapping, then this is the recursive
+ // case that gives "equal *or* disjoint" its meaning.
+ debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-INDEX");
+ Overlap::EqualOrDisjoint
}
- (ProjectionElem::Subslice { .. }, ProjectionElem::Subslice { .. }) => {
- debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-SUBSLICES");
- Overlap::EqualOrDisjoint
+ PlaceConflictBias::NoOverlap => {
+ // If we are biased towards no overlapping, then this is disjoint.
+ debug!("place_element_conflict: DISJOINT-ARRAY-INDEX");
+ Overlap::Disjoint
}
- (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
+ (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 {:?}",
+ pi1,
+ pi2
),
}
}