Auto merge of #60063 - spastorino:place2_2, r=oli-obk

Convert Place unroll to a proper iterator

r? @oli-obk
This commit is contained in:
bors 2019-04-19 09:57:26 +00:00
commit 22fa4bb0eb
2 changed files with 300 additions and 256 deletions

View File

@ -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<R>(
&self,
op: impl FnOnce(&PlaceBase<'tcx>, PlaceProjectionsIter<'_, 'tcx>) -> R,
) -> R {
self.iterate2(&PlaceProjections::Empty, op)
}
fn iterate2<R>(
&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<Self::Item> {
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::*;

View File

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