mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-05 20:34:52 +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 smallvec::SmallVec;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::fmt::{self, Debug, Formatter, Write};
|
use std::fmt::{self, Debug, Formatter, Write};
|
||||||
|
use std::iter::FusedIterator;
|
||||||
use std::ops::{Index, IndexMut};
|
use std::ops::{Index, IndexMut};
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use std::vec::IntoIter;
|
use std::vec::IntoIter;
|
||||||
@ -2058,8 +2059,101 @@ impl<'tcx> Place<'tcx> {
|
|||||||
Place::Base(PlaceBase::Static(..)) => None,
|
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> {
|
impl<'tcx> Debug for Place<'tcx> {
|
||||||
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
||||||
use self::Place::*;
|
use self::Place::*;
|
||||||
|
@ -2,7 +2,10 @@ use crate::borrow_check::ArtificialField;
|
|||||||
use crate::borrow_check::Overlap;
|
use crate::borrow_check::Overlap;
|
||||||
use crate::borrow_check::{Deep, Shallow, AccessDepth};
|
use crate::borrow_check::{Deep, Shallow, AccessDepth};
|
||||||
use rustc::hir;
|
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 rustc::ty::{self, TyCtxt};
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
|
|
||||||
@ -65,14 +68,14 @@ pub(super) fn borrow_conflicts_with_place<'gcx, 'tcx>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unroll_place(borrow_place, None, |borrow_components| {
|
borrow_place.iterate(|borrow_base, borrow_projections| {
|
||||||
unroll_place(access_place, None, |access_components| {
|
access_place.iterate(|access_base, access_projections| {
|
||||||
place_components_conflict(
|
place_components_conflict(
|
||||||
tcx,
|
tcx,
|
||||||
mir,
|
mir,
|
||||||
borrow_components,
|
(borrow_base, borrow_projections),
|
||||||
borrow_kind,
|
borrow_kind,
|
||||||
access_components,
|
(access_base, access_projections),
|
||||||
access,
|
access,
|
||||||
bias,
|
bias,
|
||||||
)
|
)
|
||||||
@ -83,9 +86,9 @@ pub(super) fn borrow_conflicts_with_place<'gcx, 'tcx>(
|
|||||||
fn place_components_conflict<'gcx, 'tcx>(
|
fn place_components_conflict<'gcx, 'tcx>(
|
||||||
tcx: TyCtxt<'_, 'gcx, 'tcx>,
|
tcx: TyCtxt<'_, 'gcx, 'tcx>,
|
||||||
mir: &Mir<'tcx>,
|
mir: &Mir<'tcx>,
|
||||||
mut borrow_components: PlaceComponentsIter<'_, 'tcx>,
|
borrow_projections: (&PlaceBase<'tcx>, PlaceProjectionsIter<'_, 'tcx>),
|
||||||
borrow_kind: BorrowKind,
|
borrow_kind: BorrowKind,
|
||||||
mut access_components: PlaceComponentsIter<'_, 'tcx>,
|
access_projections: (&PlaceBase<'tcx>, PlaceProjectionsIter<'_, 'tcx>),
|
||||||
access: AccessDepth,
|
access: AccessDepth,
|
||||||
bias: PlaceConflictBias,
|
bias: PlaceConflictBias,
|
||||||
) -> bool {
|
) -> 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
|
// - If we didn't run out of access to match, our borrow and access are comparable
|
||||||
// and either equal or disjoint.
|
// and either equal or disjoint.
|
||||||
// - If we did run out of access, the borrow can access a part of it.
|
// - 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 {
|
||||||
// loop invariant: borrow_c is always either equal to access_c or disjoint from it.
|
// 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);
|
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);
|
debug!("borrow_conflicts_with_place: access_c = {:?}", access_c);
|
||||||
|
|
||||||
// Borrow and access path both have more components.
|
// 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
|
// check whether the components being borrowed vs
|
||||||
// accessed are disjoint (as in the second example,
|
// accessed are disjoint (as in the second example,
|
||||||
// but not the first).
|
// 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 => {
|
Overlap::Arbitrary => {
|
||||||
// We have encountered different fields of potentially
|
// We have encountered different fields of potentially
|
||||||
// the same union - the borrow now partially overlaps.
|
// 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
|
// our place. This is a conflict if that is a part our
|
||||||
// access cares about.
|
// access cares about.
|
||||||
|
|
||||||
let (base, elem) = match borrow_c {
|
let base = &borrow_c.base;
|
||||||
Place::Projection(box Projection { base, elem }) => (base, elem),
|
let elem = &borrow_c.elem;
|
||||||
_ => bug!("place has no base?"),
|
|
||||||
};
|
|
||||||
let base_ty = base.ty(mir, tcx).ty;
|
let base_ty = base.ty(mir, tcx).ty;
|
||||||
|
|
||||||
match (elem, &base_ty.sty, access) {
|
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
|
// If the second example, where we did, then we still know
|
||||||
// that the borrow can access a *part* of our place that
|
// that the borrow can access a *part* of our place that
|
||||||
// our access cares about, so we still have a conflict.
|
// 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");
|
debug!("borrow_conflicts_with_place: shallow borrow");
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} 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
|
// Given that the bases of `elem1` and `elem2` are always either equal
|
||||||
// or disjoint (and have the same type!), return the overlap situation
|
// or disjoint (and have the same type!), return the overlap situation
|
||||||
// between `elem1` and `elem2`.
|
// 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>,
|
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||||
mir: &Mir<'tcx>,
|
elem1: &PlaceBase<'tcx>,
|
||||||
elem1: &Place<'tcx>,
|
elem2: &PlaceBase<'tcx>,
|
||||||
elem2: &Place<'tcx>,
|
|
||||||
bias: PlaceConflictBias,
|
|
||||||
) -> Overlap {
|
) -> Overlap {
|
||||||
match (elem1, elem2) {
|
match (elem1, elem2) {
|
||||||
(Place::Base(PlaceBase::Local(l1)), Place::Base(PlaceBase::Local(l2))) => {
|
(PlaceBase::Local(l1), PlaceBase::Local(l2)) => {
|
||||||
if l1 == l2 {
|
if l1 == l2 {
|
||||||
// the same local - base case, equal
|
// the same local - base case, equal
|
||||||
debug!("place_element_conflict: DISJOINT-OR-EQ-LOCAL");
|
debug!("place_element_conflict: DISJOINT-OR-EQ-LOCAL");
|
||||||
@ -370,7 +315,7 @@ fn place_element_conflict<'a, 'gcx: 'tcx, 'tcx>(
|
|||||||
Overlap::Disjoint
|
Overlap::Disjoint
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Place::Base(PlaceBase::Static(s1)), Place::Base(PlaceBase::Static(s2))) => {
|
(PlaceBase::Static(s1), PlaceBase::Static(s2)) => {
|
||||||
match (&s1.kind, &s2.kind) {
|
match (&s1.kind, &s2.kind) {
|
||||||
(StaticKind::Static(def_id_1), StaticKind::Static(def_id_2)) => {
|
(StaticKind::Static(def_id_1), StaticKind::Static(def_id_2)) => {
|
||||||
if def_id_1 != 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(_))) |
|
(PlaceBase::Local(_), PlaceBase::Static(_)) |
|
||||||
(Place::Base(PlaceBase::Static(_)), Place::Base(PlaceBase::Local(_))) => {
|
(PlaceBase::Static(_), PlaceBase::Local(_)) => {
|
||||||
debug!("place_element_conflict: DISJOINT-STATIC-LOCAL-PROMOTED");
|
debug!("place_element_conflict: DISJOINT-STATIC-LOCAL-PROMOTED");
|
||||||
Overlap::Disjoint
|
Overlap::Disjoint
|
||||||
}
|
}
|
||||||
(Place::Projection(pi1), Place::Projection(pi2)) => {
|
}
|
||||||
match (&pi1.elem, &pi2.elem) {
|
}
|
||||||
(ProjectionElem::Deref, ProjectionElem::Deref) => {
|
|
||||||
// derefs (e.g., `*x` vs. `*x`) - recur.
|
// Given that the bases of `elem1` and `elem2` are always either equal
|
||||||
debug!("place_element_conflict: DISJOINT-OR-EQ-DEREF");
|
// or disjoint (and have the same type!), return the overlap situation
|
||||||
Overlap::EqualOrDisjoint
|
// between `elem1` and `elem2`.
|
||||||
}
|
fn place_projection_conflict<'a, 'gcx: 'tcx, 'tcx>(
|
||||||
(ProjectionElem::Field(f1, _), ProjectionElem::Field(f2, _)) => {
|
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||||
if f1 == f2 {
|
mir: &Mir<'tcx>,
|
||||||
// same field (e.g., `a.y` vs. `a.y`) - recur.
|
pi1: &PlaceProjection<'tcx>,
|
||||||
debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD");
|
pi2: &PlaceProjection<'tcx>,
|
||||||
Overlap::EqualOrDisjoint
|
bias: PlaceConflictBias,
|
||||||
} else {
|
) -> Overlap {
|
||||||
let ty = pi1.base.ty(mir, tcx).ty;
|
match (&pi1.elem, &pi2.elem) {
|
||||||
match ty.sty {
|
(ProjectionElem::Deref, ProjectionElem::Deref) => {
|
||||||
ty::Adt(def, _) if def.is_union() => {
|
// derefs (e.g., `*x` vs. `*x`) - recur.
|
||||||
// Different fields of a union, we are basically stuck.
|
debug!("place_element_conflict: DISJOINT-OR-EQ-DEREF");
|
||||||
debug!("place_element_conflict: STUCK-UNION");
|
Overlap::EqualOrDisjoint
|
||||||
Overlap::Arbitrary
|
}
|
||||||
}
|
(ProjectionElem::Field(f1, _), ProjectionElem::Field(f2, _)) => {
|
||||||
_ => {
|
if f1 == f2 {
|
||||||
// Different fields of a struct (`a.x` vs. `a.y`). Disjoint!
|
// same field (e.g., `a.y` vs. `a.y`) - recur.
|
||||||
debug!("place_element_conflict: DISJOINT-FIELD");
|
debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD");
|
||||||
Overlap::Disjoint
|
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 fields of a struct (`a.x` vs. `a.y`). Disjoint!
|
||||||
// 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");
|
debug!("place_element_conflict: DISJOINT-FIELD");
|
||||||
Overlap::Disjoint
|
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!(
|
(ProjectionElem::Downcast(_, v1), ProjectionElem::Downcast(_, v2)) => {
|
||||||
"unexpected elements in place_element_conflict: {:?} and {:?}",
|
// different variants are treated as having disjoint fields,
|
||||||
elem1,
|
// even if they occupy the same "space", because it's
|
||||||
elem2
|
// 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