diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 3f376fb052a27..b4f63d9a40cdf 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -20,6 +20,7 @@ use crate::rustc_serialize::{self as serialize}; use smallvec::SmallVec; use std::borrow::Cow; use std::fmt::{self, Debug, Formatter, Write}; +use std::iter::FusedIterator; use std::ops::{Index, IndexMut}; use std::slice; use std::vec::IntoIter; @@ -2058,8 +2059,101 @@ impl<'tcx> Place<'tcx> { Place::Base(PlaceBase::Static(..)) => None, } } + + /// Recursively "iterates" over place components, generating a `PlaceBase` and + /// `PlaceProjections` list and invoking `op` with a `PlaceProjectionsIter`. + pub fn iterate( + &self, + op: impl FnOnce(&PlaceBase<'tcx>, PlaceProjectionsIter<'_, 'tcx>) -> R, + ) -> R { + self.iterate2(&PlaceProjections::Empty, op) + } + + fn iterate2( + &self, + next: &PlaceProjections<'_, 'tcx>, + op: impl FnOnce(&PlaceBase<'tcx>, PlaceProjectionsIter<'_, 'tcx>) -> R, + ) -> R { + match self { + Place::Projection(interior) => interior.base.iterate2( + &PlaceProjections::List { + projection: interior, + next, + }, + op, + ), + + Place::Base(base) => op(base, next.iter()), + } + } +} + +/// A linked list of projections running up the stack; begins with the +/// innermost projection and extends to the outermost (e.g., `a.b.c` +/// would have the place `b` with a "next" pointer to `b.c`). +/// Created by `Place::iterate`. +/// +/// 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. +pub enum PlaceProjections<'p, 'tcx: 'p> { + Empty, + + List { + projection: &'p PlaceProjection<'tcx>, + next: &'p PlaceProjections<'p, 'tcx>, + } +} + +impl<'p, 'tcx> PlaceProjections<'p, 'tcx> { + fn iter(&self) -> PlaceProjectionsIter<'_, 'tcx> { + PlaceProjectionsIter { value: self } + } } +impl<'p, 'tcx> IntoIterator for &'p PlaceProjections<'p, 'tcx> { + type Item = &'p PlaceProjection<'tcx>; + type IntoIter = PlaceProjectionsIter<'p, 'tcx>; + + /// Converts a list of `PlaceProjection` components into an iterator; + /// this iterator yields up a never-ending stream of `Option<&Place>`. + /// These begin with the "innermost" projection 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 into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +/// Iterator over components; see `PlaceProjections::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. +pub struct PlaceProjectionsIter<'p, 'tcx: 'p> { + pub value: &'p PlaceProjections<'p, 'tcx>, +} + +impl<'p, 'tcx> Iterator for PlaceProjectionsIter<'p, 'tcx> { + type Item = &'p PlaceProjection<'tcx>; + + fn next(&mut self) -> Option { + if let &PlaceProjections::List { projection, next } = self.value { + self.value = next; + Some(projection) + } else { + None + } + } +} + +impl<'p, 'tcx> FusedIterator for PlaceProjectionsIter<'p, 'tcx> {} + impl<'tcx> Debug for Place<'tcx> { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { use self::Place::*; diff --git a/src/librustc_mir/borrow_check/places_conflict.rs b/src/librustc_mir/borrow_check/places_conflict.rs index fbe8b8485dda5..cebf05ad9b99d 100644 --- a/src/librustc_mir/borrow_check/places_conflict.rs +++ b/src/librustc_mir/borrow_check/places_conflict.rs @@ -2,7 +2,10 @@ use crate::borrow_check::ArtificialField; 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, Mir, Place, PlaceBase, PlaceProjection, ProjectionElem, PlaceProjectionsIter, + StaticKind +}; use rustc::ty::{self, TyCtxt}; use std::cmp::max; @@ -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, ) @@ -83,9 +86,9 @@ 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>, + borrow_projections: (&PlaceBase<'tcx>, PlaceProjectionsIter<'_, 'tcx>), borrow_kind: BorrowKind, - mut access_components: PlaceComponentsIter<'_, 'tcx>, + access_projections: (&PlaceBase<'tcx>, PlaceProjectionsIter<'_, '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,7 +315,7 @@ 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 { @@ -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: &Mir<'tcx>, + pi1: &PlaceProjection<'tcx>, + pi2: &PlaceProjection<'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) { - // 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) { + // 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 ), } }