]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_mir/borrow_check/places_conflict.rs
Use `assert_usize` instead of `unwrap_usize` in several places
[rust.git] / src / librustc_mir / borrow_check / places_conflict.rs
index fbe8b8485dda5b9d886ee7b1d8a3834d4c4ab11e..8aa27eef72a5e022efa1e534520245934713184a 100644 (file)
@@ -2,7 +2,10 @@
 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;
 
@@ -23,7 +26,7 @@
 /// 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,
@@ -45,7 +48,7 @@
 /// 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>,
@@ -65,14 +68,14 @@ pub(super) fn borrow_conflicts_with_place<'gcx, '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,
             )
@@ -82,10 +85,10 @@ pub(super) fn borrow_conflicts_with_place<'gcx, 'tcx>(
 
 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 {
@@ -130,12 +133,34 @@ fn place_components_conflict<'gcx, 'tcx>(
     //  - 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.
@@ -150,7 +175,7 @@ fn place_components_conflict<'gcx, 'tcx>(
                 // 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.
@@ -187,10 +212,8 @@ fn place_components_conflict<'gcx, 'tcx>(
                 // 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) {
@@ -261,7 +284,7 @@ fn place_components_conflict<'gcx, 'tcx>(
             // 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 {
@@ -272,94 +295,16 @@ fn place_components_conflict<'gcx, 'tcx>(
     }
 }
 
-/// 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");
@@ -370,13 +315,13 @@ fn place_element_conflict<'a, 'gcx: 'tcx, 'tcx>(
                 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
@@ -387,8 +332,8 @@ fn place_element_conflict<'a, 'gcx: 'tcx, 'tcx>(
                 },
                 (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;
@@ -409,174 +354,179 @@ fn place_element_conflict<'a, 'gcx: 'tcx, 'tcx>(
                 }
             }
         }
-        (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
         ),
     }
 }