mirror of
https://github.com/rust-lang/rust.git
synced 2024-12-13 00:56:14 +00:00
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:
commit
22fa4bb0eb
@ -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::*;
|
||||
|
@ -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
|
||||
),
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user