Skip to content

Commit 22fa4bb

Browse files
committedApr 19, 2019
Auto merge of #60063 - spastorino:place2_2, r=oli-obk
Convert Place unroll to a proper iterator r? @oli-obk
·
1.88.01.36.0
2 parents 6c22051 + 0a386ba commit 22fa4bb

File tree

2 files changed

+298
-254
lines changed

2 files changed

+298
-254
lines changed
 

‎src/librustc/mir/mod.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use crate::rustc_serialize::{self as serialize};
2020
use smallvec::SmallVec;
2121
use std::borrow::Cow;
2222
use std::fmt::{self, Debug, Formatter, Write};
23+
use std::iter::FusedIterator;
2324
use std::ops::{Index, IndexMut};
2425
use std::slice;
2526
use std::vec::IntoIter;
@@ -2058,8 +2059,101 @@ impl<'tcx> Place<'tcx> {
20582059
Place::Base(PlaceBase::Static(..)) => None,
20592060
}
20602061
}
2062+
2063+
/// Recursively "iterates" over place components, generating a `PlaceBase` and
2064+
/// `PlaceProjections` list and invoking `op` with a `PlaceProjectionsIter`.
2065+
pub fn iterate<R>(
2066+
&self,
2067+
op: impl FnOnce(&PlaceBase<'tcx>, PlaceProjectionsIter<'_, 'tcx>) -> R,
2068+
) -> R {
2069+
self.iterate2(&PlaceProjections::Empty, op)
2070+
}
2071+
2072+
fn iterate2<R>(
2073+
&self,
2074+
next: &PlaceProjections<'_, 'tcx>,
2075+
op: impl FnOnce(&PlaceBase<'tcx>, PlaceProjectionsIter<'_, 'tcx>) -> R,
2076+
) -> R {
2077+
match self {
2078+
Place::Projection(interior) => interior.base.iterate2(
2079+
&PlaceProjections::List {
2080+
projection: interior,
2081+
next,
2082+
},
2083+
op,
2084+
),
2085+
2086+
Place::Base(base) => op(base, next.iter()),
2087+
}
2088+
}
2089+
}
2090+
2091+
/// A linked list of projections running up the stack; begins with the
2092+
/// innermost projection and extends to the outermost (e.g., `a.b.c`
2093+
/// would have the place `b` with a "next" pointer to `b.c`).
2094+
/// Created by `Place::iterate`.
2095+
///
2096+
/// N.B., this particular impl strategy is not the most obvious. It was
2097+
/// chosen because it makes a measurable difference to NLL
2098+
/// performance, as this code (`borrow_conflicts_with_place`) is somewhat hot.
2099+
pub enum PlaceProjections<'p, 'tcx: 'p> {
2100+
Empty,
2101+
2102+
List {
2103+
projection: &'p PlaceProjection<'tcx>,
2104+
next: &'p PlaceProjections<'p, 'tcx>,
2105+
}
2106+
}
2107+
2108+
impl<'p, 'tcx> PlaceProjections<'p, 'tcx> {
2109+
fn iter(&self) -> PlaceProjectionsIter<'_, 'tcx> {
2110+
PlaceProjectionsIter { value: self }
2111+
}
20612112
}
20622113

2114+
impl<'p, 'tcx> IntoIterator for &'p PlaceProjections<'p, 'tcx> {
2115+
type Item = &'p PlaceProjection<'tcx>;
2116+
type IntoIter = PlaceProjectionsIter<'p, 'tcx>;
2117+
2118+
/// Converts a list of `PlaceProjection` components into an iterator;
2119+
/// this iterator yields up a never-ending stream of `Option<&Place>`.
2120+
/// These begin with the "innermost" projection and then with each
2121+
/// projection therefrom. So given a place like `a.b.c` it would
2122+
/// yield up:
2123+
///
2124+
/// ```notrust
2125+
/// Some(`a`), Some(`a.b`), Some(`a.b.c`), None, None, ...
2126+
/// ```
2127+
fn into_iter(self) -> Self::IntoIter {
2128+
self.iter()
2129+
}
2130+
}
2131+
2132+
/// Iterator over components; see `PlaceProjections::iter` for more
2133+
/// information.
2134+
///
2135+
/// N.B., this is not a *true* Rust iterator -- the code above just
2136+
/// manually invokes `next`. This is because we (sometimes) want to
2137+
/// keep executing even after `None` has been returned.
2138+
pub struct PlaceProjectionsIter<'p, 'tcx: 'p> {
2139+
pub value: &'p PlaceProjections<'p, 'tcx>,
2140+
}
2141+
2142+
impl<'p, 'tcx> Iterator for PlaceProjectionsIter<'p, 'tcx> {
2143+
type Item = &'p PlaceProjection<'tcx>;
2144+
2145+
fn next(&mut self) -> Option<Self::Item> {
2146+
if let &PlaceProjections::List { projection, next } = self.value {
2147+
self.value = next;
2148+
Some(projection)
2149+
} else {
2150+
None
2151+
}
2152+
}
2153+
}
2154+
2155+
impl<'p, 'tcx> FusedIterator for PlaceProjectionsIter<'p, 'tcx> {}
2156+
20632157
impl<'tcx> Debug for Place<'tcx> {
20642158
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
20652159
use self::Place::*;

‎src/librustc_mir/borrow_check/places_conflict.rs

Lines changed: 204 additions & 254 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ use crate::borrow_check::ArtificialField;
22
use crate::borrow_check::Overlap;
33
use crate::borrow_check::{Deep, Shallow, AccessDepth};
44
use rustc::hir;
5-
use rustc::mir::{BorrowKind, Mir, Place, PlaceBase, Projection, ProjectionElem, StaticKind};
5+
use rustc::mir::{
6+
BorrowKind, Mir, Place, PlaceBase, PlaceProjection, ProjectionElem, PlaceProjectionsIter,
7+
StaticKind
8+
};
69
use rustc::ty::{self, TyCtxt};
710
use std::cmp::max;
811

@@ -65,14 +68,14 @@ pub(super) fn borrow_conflicts_with_place<'gcx, 'tcx>(
6568
}
6669
}
6770

68-
unroll_place(borrow_place, None, |borrow_components| {
69-
unroll_place(access_place, None, |access_components| {
71+
borrow_place.iterate(|borrow_base, borrow_projections| {
72+
access_place.iterate(|access_base, access_projections| {
7073
place_components_conflict(
7174
tcx,
7275
mir,
73-
borrow_components,
76+
(borrow_base, borrow_projections),
7477
borrow_kind,
75-
access_components,
78+
(access_base, access_projections),
7679
access,
7780
bias,
7881
)
@@ -83,9 +86,9 @@ pub(super) fn borrow_conflicts_with_place<'gcx, 'tcx>(
8386
fn place_components_conflict<'gcx, 'tcx>(
8487
tcx: TyCtxt<'_, 'gcx, 'tcx>,
8588
mir: &Mir<'tcx>,
86-
mut borrow_components: PlaceComponentsIter<'_, 'tcx>,
89+
borrow_projections: (&PlaceBase<'tcx>, PlaceProjectionsIter<'_, 'tcx>),
8790
borrow_kind: BorrowKind,
88-
mut access_components: PlaceComponentsIter<'_, 'tcx>,
91+
access_projections: (&PlaceBase<'tcx>, PlaceProjectionsIter<'_, 'tcx>),
8992
access: AccessDepth,
9093
bias: PlaceConflictBias,
9194
) -> bool {
@@ -130,12 +133,34 @@ fn place_components_conflict<'gcx, 'tcx>(
130133
// - If we didn't run out of access to match, our borrow and access are comparable
131134
// and either equal or disjoint.
132135
// - If we did run out of access, the borrow can access a part of it.
136+
137+
let borrow_base = borrow_projections.0;
138+
let access_base = access_projections.0;
139+
140+
match place_base_conflict(tcx, borrow_base, access_base) {
141+
Overlap::Arbitrary => {
142+
bug!("Two base can't return Arbitrary");
143+
}
144+
Overlap::EqualOrDisjoint => {
145+
// This is the recursive case - proceed to the next element.
146+
}
147+
Overlap::Disjoint => {
148+
// We have proven the borrow disjoint - further
149+
// projections will remain disjoint.
150+
debug!("borrow_conflicts_with_place: disjoint");
151+
return false;
152+
}
153+
}
154+
155+
let mut borrow_projections = borrow_projections.1;
156+
let mut access_projections = access_projections.1;
157+
133158
loop {
134159
// loop invariant: borrow_c is always either equal to access_c or disjoint from it.
135-
if let Some(borrow_c) = borrow_components.next() {
160+
if let Some(borrow_c) = borrow_projections.next() {
136161
debug!("borrow_conflicts_with_place: borrow_c = {:?}", borrow_c);
137162

138-
if let Some(access_c) = access_components.next() {
163+
if let Some(access_c) = access_projections.next() {
139164
debug!("borrow_conflicts_with_place: access_c = {:?}", access_c);
140165

141166
// Borrow and access path both have more components.
@@ -150,7 +175,7 @@ fn place_components_conflict<'gcx, 'tcx>(
150175
// check whether the components being borrowed vs
151176
// accessed are disjoint (as in the second example,
152177
// but not the first).
153-
match place_element_conflict(tcx, mir, borrow_c, access_c, bias) {
178+
match place_projection_conflict(tcx, mir, borrow_c, access_c, bias) {
154179
Overlap::Arbitrary => {
155180
// We have encountered different fields of potentially
156181
// the same union - the borrow now partially overlaps.
@@ -187,10 +212,8 @@ fn place_components_conflict<'gcx, 'tcx>(
187212
// our place. This is a conflict if that is a part our
188213
// access cares about.
189214

190-
let (base, elem) = match borrow_c {
191-
Place::Projection(box Projection { base, elem }) => (base, elem),
192-
_ => bug!("place has no base?"),
193-
};
215+
let base = &borrow_c.base;
216+
let elem = &borrow_c.elem;
194217
let base_ty = base.ty(mir, tcx).ty;
195218

196219
match (elem, &base_ty.sty, access) {
@@ -261,7 +284,7 @@ fn place_components_conflict<'gcx, 'tcx>(
261284
// If the second example, where we did, then we still know
262285
// that the borrow can access a *part* of our place that
263286
// our access cares about, so we still have a conflict.
264-
if borrow_kind == BorrowKind::Shallow && access_components.next().is_some() {
287+
if borrow_kind == BorrowKind::Shallow && access_projections.next().is_some() {
265288
debug!("borrow_conflicts_with_place: shallow borrow");
266289
return false;
267290
} else {
@@ -272,94 +295,16 @@ fn place_components_conflict<'gcx, 'tcx>(
272295
}
273296
}
274297

275-
/// A linked list of places running up the stack; begins with the
276-
/// innermost place and extends to projections (e.g., `a.b` would have
277-
/// the place `a` with a "next" pointer to `a.b`). Created by
278-
/// `unroll_place`.
279-
///
280-
/// N.B., this particular impl strategy is not the most obvious. It was
281-
/// chosen because it makes a measurable difference to NLL
282-
/// performance, as this code (`borrow_conflicts_with_place`) is somewhat hot.
283-
struct PlaceComponents<'p, 'tcx: 'p> {
284-
component: &'p Place<'tcx>,
285-
next: Option<&'p PlaceComponents<'p, 'tcx>>,
286-
}
287-
288-
impl<'p, 'tcx> PlaceComponents<'p, 'tcx> {
289-
/// Converts a list of `Place` components into an iterator; this
290-
/// iterator yields up a never-ending stream of `Option<&Place>`.
291-
/// These begin with the "innermost" place and then with each
292-
/// projection therefrom. So given a place like `a.b.c` it would
293-
/// yield up:
294-
///
295-
/// ```notrust
296-
/// Some(`a`), Some(`a.b`), Some(`a.b.c`), None, None, ...
297-
/// ```
298-
fn iter(&self) -> PlaceComponentsIter<'_, 'tcx> {
299-
PlaceComponentsIter { value: Some(self) }
300-
}
301-
}
302-
303-
/// Iterator over components; see `PlaceComponents::iter` for more
304-
/// information.
305-
///
306-
/// N.B., this is not a *true* Rust iterator -- the code above just
307-
/// manually invokes `next`. This is because we (sometimes) want to
308-
/// keep executing even after `None` has been returned.
309-
struct PlaceComponentsIter<'p, 'tcx: 'p> {
310-
value: Option<&'p PlaceComponents<'p, 'tcx>>,
311-
}
312-
313-
impl<'p, 'tcx> PlaceComponentsIter<'p, 'tcx> {
314-
fn next(&mut self) -> Option<&'p Place<'tcx>> {
315-
if let Some(&PlaceComponents { component, next }) = self.value {
316-
self.value = next;
317-
Some(component)
318-
} else {
319-
None
320-
}
321-
}
322-
}
323-
324-
/// Recursively "unroll" a place into a `PlaceComponents` list,
325-
/// invoking `op` with a `PlaceComponentsIter`.
326-
fn unroll_place<'tcx, R>(
327-
place: &Place<'tcx>,
328-
next: Option<&PlaceComponents<'_, 'tcx>>,
329-
op: impl FnOnce(PlaceComponentsIter<'_, 'tcx>) -> R,
330-
) -> R {
331-
match place {
332-
Place::Projection(interior) => unroll_place(
333-
&interior.base,
334-
Some(&PlaceComponents {
335-
component: place,
336-
next,
337-
}),
338-
op,
339-
),
340-
341-
Place::Base(PlaceBase::Local(_)) | Place::Base(PlaceBase::Static(_)) => {
342-
let list = PlaceComponents {
343-
component: place,
344-
next,
345-
};
346-
op(list.iter())
347-
}
348-
}
349-
}
350-
351298
// Given that the bases of `elem1` and `elem2` are always either equal
352299
// or disjoint (and have the same type!), return the overlap situation
353300
// between `elem1` and `elem2`.
354-
fn place_element_conflict<'a, 'gcx: 'tcx, 'tcx>(
301+
fn place_base_conflict<'a, 'gcx: 'tcx, 'tcx>(
355302
tcx: TyCtxt<'a, 'gcx, 'tcx>,
356-
mir: &Mir<'tcx>,
357-
elem1: &Place<'tcx>,
358-
elem2: &Place<'tcx>,
359-
bias: PlaceConflictBias,
303+
elem1: &PlaceBase<'tcx>,
304+
elem2: &PlaceBase<'tcx>,
360305
) -> Overlap {
361306
match (elem1, elem2) {
362-
(Place::Base(PlaceBase::Local(l1)), Place::Base(PlaceBase::Local(l2))) => {
307+
(PlaceBase::Local(l1), PlaceBase::Local(l2)) => {
363308
if l1 == l2 {
364309
// the same local - base case, equal
365310
debug!("place_element_conflict: DISJOINT-OR-EQ-LOCAL");
@@ -370,7 +315,7 @@ fn place_element_conflict<'a, 'gcx: 'tcx, 'tcx>(
370315
Overlap::Disjoint
371316
}
372317
}
373-
(Place::Base(PlaceBase::Static(s1)), Place::Base(PlaceBase::Static(s2))) => {
318+
(PlaceBase::Static(s1), PlaceBase::Static(s2)) => {
374319
match (&s1.kind, &s2.kind) {
375320
(StaticKind::Static(def_id_1), StaticKind::Static(def_id_2)) => {
376321
if def_id_1 != def_id_2 {
@@ -409,174 +354,179 @@ fn place_element_conflict<'a, 'gcx: 'tcx, 'tcx>(
409354
}
410355
}
411356
}
412-
(Place::Base(PlaceBase::Local(_)), Place::Base(PlaceBase::Static(_))) |
413-
(Place::Base(PlaceBase::Static(_)), Place::Base(PlaceBase::Local(_))) => {
357+
(PlaceBase::Local(_), PlaceBase::Static(_)) |
358+
(PlaceBase::Static(_), PlaceBase::Local(_)) => {
414359
debug!("place_element_conflict: DISJOINT-STATIC-LOCAL-PROMOTED");
415360
Overlap::Disjoint
416361
}
417-
(Place::Projection(pi1), Place::Projection(pi2)) => {
418-
match (&pi1.elem, &pi2.elem) {
419-
(ProjectionElem::Deref, ProjectionElem::Deref) => {
420-
// derefs (e.g., `*x` vs. `*x`) - recur.
421-
debug!("place_element_conflict: DISJOINT-OR-EQ-DEREF");
422-
Overlap::EqualOrDisjoint
423-
}
424-
(ProjectionElem::Field(f1, _), ProjectionElem::Field(f2, _)) => {
425-
if f1 == f2 {
426-
// same field (e.g., `a.y` vs. `a.y`) - recur.
427-
debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD");
428-
Overlap::EqualOrDisjoint
429-
} else {
430-
let ty = pi1.base.ty(mir, tcx).ty;
431-
match ty.sty {
432-
ty::Adt(def, _) if def.is_union() => {
433-
// Different fields of a union, we are basically stuck.
434-
debug!("place_element_conflict: STUCK-UNION");
435-
Overlap::Arbitrary
436-
}
437-
_ => {
438-
// Different fields of a struct (`a.x` vs. `a.y`). Disjoint!
439-
debug!("place_element_conflict: DISJOINT-FIELD");
440-
Overlap::Disjoint
441-
}
442-
}
362+
}
363+
}
364+
365+
// Given that the bases of `elem1` and `elem2` are always either equal
366+
// or disjoint (and have the same type!), return the overlap situation
367+
// between `elem1` and `elem2`.
368+
fn place_projection_conflict<'a, 'gcx: 'tcx, 'tcx>(
369+
tcx: TyCtxt<'a, 'gcx, 'tcx>,
370+
mir: &Mir<'tcx>,
371+
pi1: &PlaceProjection<'tcx>,
372+
pi2: &PlaceProjection<'tcx>,
373+
bias: PlaceConflictBias,
374+
) -> Overlap {
375+
match (&pi1.elem, &pi2.elem) {
376+
(ProjectionElem::Deref, ProjectionElem::Deref) => {
377+
// derefs (e.g., `*x` vs. `*x`) - recur.
378+
debug!("place_element_conflict: DISJOINT-OR-EQ-DEREF");
379+
Overlap::EqualOrDisjoint
380+
}
381+
(ProjectionElem::Field(f1, _), ProjectionElem::Field(f2, _)) => {
382+
if f1 == f2 {
383+
// same field (e.g., `a.y` vs. `a.y`) - recur.
384+
debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD");
385+
Overlap::EqualOrDisjoint
386+
} else {
387+
let ty = pi1.base.ty(mir, tcx).ty;
388+
match ty.sty {
389+
ty::Adt(def, _) if def.is_union() => {
390+
// Different fields of a union, we are basically stuck.
391+
debug!("place_element_conflict: STUCK-UNION");
392+
Overlap::Arbitrary
443393
}
444-
}
445-
(ProjectionElem::Downcast(_, v1), ProjectionElem::Downcast(_, v2)) => {
446-
// different variants are treated as having disjoint fields,
447-
// even if they occupy the same "space", because it's
448-
// impossible for 2 variants of the same enum to exist
449-
// (and therefore, to be borrowed) at the same time.
450-
//
451-
// Note that this is different from unions - we *do* allow
452-
// this code to compile:
453-
//
454-
// ```
455-
// fn foo(x: &mut Result<i32, i32>) {
456-
// let mut v = None;
457-
// if let Ok(ref mut a) = *x {
458-
// v = Some(a);
459-
// }
460-
// // here, you would *think* that the
461-
// // *entirety* of `x` would be borrowed,
462-
// // but in fact only the `Ok` variant is,
463-
// // so the `Err` variant is *entirely free*:
464-
// if let Err(ref mut a) = *x {
465-
// v = Some(a);
466-
// }
467-
// drop(v);
468-
// }
469-
// ```
470-
if v1 == v2 {
471-
debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD");
472-
Overlap::EqualOrDisjoint
473-
} else {
394+
_ => {
395+
// Different fields of a struct (`a.x` vs. `a.y`). Disjoint!
474396
debug!("place_element_conflict: DISJOINT-FIELD");
475397
Overlap::Disjoint
476398
}
477399
}
478-
(ProjectionElem::Index(..), ProjectionElem::Index(..))
479-
| (ProjectionElem::Index(..), ProjectionElem::ConstantIndex { .. })
480-
| (ProjectionElem::Index(..), ProjectionElem::Subslice { .. })
481-
| (ProjectionElem::ConstantIndex { .. }, ProjectionElem::Index(..))
482-
| (ProjectionElem::Subslice { .. }, ProjectionElem::Index(..)) => {
483-
// Array indexes (`a[0]` vs. `a[i]`). These can either be disjoint
484-
// (if the indexes differ) or equal (if they are the same).
485-
match bias {
486-
PlaceConflictBias::Overlap => {
487-
// If we are biased towards overlapping, then this is the recursive
488-
// case that gives "equal *or* disjoint" its meaning.
489-
debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-INDEX");
490-
Overlap::EqualOrDisjoint
491-
}
492-
PlaceConflictBias::NoOverlap => {
493-
// If we are biased towards no overlapping, then this is disjoint.
494-
debug!("place_element_conflict: DISJOINT-ARRAY-INDEX");
495-
Overlap::Disjoint
496-
}
497-
}
498-
}
499-
(ProjectionElem::ConstantIndex { offset: o1, min_length: _, from_end: false },
500-
ProjectionElem::ConstantIndex { offset: o2, min_length: _, from_end: false })
501-
| (ProjectionElem::ConstantIndex { offset: o1, min_length: _, from_end: true },
502-
ProjectionElem::ConstantIndex {
503-
offset: o2, min_length: _, from_end: true }) => {
504-
if o1 == o2 {
505-
debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX");
506-
Overlap::EqualOrDisjoint
507-
} else {
508-
debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX");
509-
Overlap::Disjoint
510-
}
511-
}
512-
(ProjectionElem::ConstantIndex {
513-
offset: offset_from_begin, min_length: min_length1, from_end: false },
514-
ProjectionElem::ConstantIndex {
515-
offset: offset_from_end, min_length: min_length2, from_end: true })
516-
| (ProjectionElem::ConstantIndex {
517-
offset: offset_from_end, min_length: min_length1, from_end: true },
518-
ProjectionElem::ConstantIndex {
519-
offset: offset_from_begin, min_length: min_length2, from_end: false }) => {
520-
// both patterns matched so it must be at least the greater of the two
521-
let min_length = max(min_length1, min_length2);
522-
// `offset_from_end` can be in range `[1..min_length]`, 1 indicates the last
523-
// element (like -1 in Python) and `min_length` the first.
524-
// Therefore, `min_length - offset_from_end` gives the minimal possible
525-
// offset from the beginning
526-
if *offset_from_begin >= min_length - offset_from_end {
527-
debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-FE");
528-
Overlap::EqualOrDisjoint
529-
} else {
530-
debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-FE");
531-
Overlap::Disjoint
532-
}
533-
}
534-
(ProjectionElem::ConstantIndex { offset, min_length: _, from_end: false },
535-
ProjectionElem::Subslice {from, .. })
536-
| (ProjectionElem::Subslice {from, .. },
537-
ProjectionElem::ConstantIndex { offset, min_length: _, from_end: false }) => {
538-
if offset >= from {
539-
debug!(
540-
"place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-SUBSLICE");
541-
Overlap::EqualOrDisjoint
542-
} else {
543-
debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-SUBSLICE");
544-
Overlap::Disjoint
545-
}
546-
}
547-
(ProjectionElem::ConstantIndex { offset, min_length: _, from_end: true },
548-
ProjectionElem::Subslice {from: _, to })
549-
| (ProjectionElem::Subslice {from: _, to },
550-
ProjectionElem::ConstantIndex { offset, min_length: _, from_end: true }) => {
551-
if offset > to {
552-
debug!("place_element_conflict: \
553-
DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-SUBSLICE-FE");
554-
Overlap::EqualOrDisjoint
555-
} else {
556-
debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-SUBSLICE-FE");
557-
Overlap::Disjoint
558-
}
400+
}
401+
}
402+
(ProjectionElem::Downcast(_, v1), ProjectionElem::Downcast(_, v2)) => {
403+
// different variants are treated as having disjoint fields,
404+
// even if they occupy the same "space", because it's
405+
// impossible for 2 variants of the same enum to exist
406+
// (and therefore, to be borrowed) at the same time.
407+
//
408+
// Note that this is different from unions - we *do* allow
409+
// this code to compile:
410+
//
411+
// ```
412+
// fn foo(x: &mut Result<i32, i32>) {
413+
// let mut v = None;
414+
// if let Ok(ref mut a) = *x {
415+
// v = Some(a);
416+
// }
417+
// // here, you would *think* that the
418+
// // *entirety* of `x` would be borrowed,
419+
// // but in fact only the `Ok` variant is,
420+
// // so the `Err` variant is *entirely free*:
421+
// if let Err(ref mut a) = *x {
422+
// v = Some(a);
423+
// }
424+
// drop(v);
425+
// }
426+
// ```
427+
if v1 == v2 {
428+
debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD");
429+
Overlap::EqualOrDisjoint
430+
} else {
431+
debug!("place_element_conflict: DISJOINT-FIELD");
432+
Overlap::Disjoint
433+
}
434+
}
435+
(ProjectionElem::Index(..), ProjectionElem::Index(..))
436+
| (ProjectionElem::Index(..), ProjectionElem::ConstantIndex { .. })
437+
| (ProjectionElem::Index(..), ProjectionElem::Subslice { .. })
438+
| (ProjectionElem::ConstantIndex { .. }, ProjectionElem::Index(..))
439+
| (ProjectionElem::Subslice { .. }, ProjectionElem::Index(..)) => {
440+
// Array indexes (`a[0]` vs. `a[i]`). These can either be disjoint
441+
// (if the indexes differ) or equal (if they are the same).
442+
match bias {
443+
PlaceConflictBias::Overlap => {
444+
// If we are biased towards overlapping, then this is the recursive
445+
// case that gives "equal *or* disjoint" its meaning.
446+
debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-INDEX");
447+
Overlap::EqualOrDisjoint
559448
}
560-
(ProjectionElem::Subslice { .. }, ProjectionElem::Subslice { .. }) => {
561-
debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-SUBSLICES");
562-
Overlap::EqualOrDisjoint
449+
PlaceConflictBias::NoOverlap => {
450+
// If we are biased towards no overlapping, then this is disjoint.
451+
debug!("place_element_conflict: DISJOINT-ARRAY-INDEX");
452+
Overlap::Disjoint
563453
}
564-
(ProjectionElem::Deref, _)
565-
| (ProjectionElem::Field(..), _)
566-
| (ProjectionElem::Index(..), _)
567-
| (ProjectionElem::ConstantIndex { .. }, _)
568-
| (ProjectionElem::Subslice { .. }, _)
569-
| (ProjectionElem::Downcast(..), _) => bug!(
570-
"mismatched projections in place_element_conflict: {:?} and {:?}",
571-
elem1,
572-
elem2
573-
),
574454
}
575455
}
576-
(Place::Projection(_), _) | (_, Place::Projection(_)) => bug!(
577-
"unexpected elements in place_element_conflict: {:?} and {:?}",
578-
elem1,
579-
elem2
456+
(ProjectionElem::ConstantIndex { offset: o1, min_length: _, from_end: false },
457+
ProjectionElem::ConstantIndex { offset: o2, min_length: _, from_end: false })
458+
| (ProjectionElem::ConstantIndex { offset: o1, min_length: _, from_end: true },
459+
ProjectionElem::ConstantIndex {
460+
offset: o2, min_length: _, from_end: true }) => {
461+
if o1 == o2 {
462+
debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX");
463+
Overlap::EqualOrDisjoint
464+
} else {
465+
debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX");
466+
Overlap::Disjoint
467+
}
468+
}
469+
(ProjectionElem::ConstantIndex {
470+
offset: offset_from_begin, min_length: min_length1, from_end: false },
471+
ProjectionElem::ConstantIndex {
472+
offset: offset_from_end, min_length: min_length2, from_end: true })
473+
| (ProjectionElem::ConstantIndex {
474+
offset: offset_from_end, min_length: min_length1, from_end: true },
475+
ProjectionElem::ConstantIndex {
476+
offset: offset_from_begin, min_length: min_length2, from_end: false }) => {
477+
// both patterns matched so it must be at least the greater of the two
478+
let min_length = max(min_length1, min_length2);
479+
// `offset_from_end` can be in range `[1..min_length]`, 1 indicates the last
480+
// element (like -1 in Python) and `min_length` the first.
481+
// Therefore, `min_length - offset_from_end` gives the minimal possible
482+
// offset from the beginning
483+
if *offset_from_begin >= min_length - offset_from_end {
484+
debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-FE");
485+
Overlap::EqualOrDisjoint
486+
} else {
487+
debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-FE");
488+
Overlap::Disjoint
489+
}
490+
}
491+
(ProjectionElem::ConstantIndex { offset, min_length: _, from_end: false },
492+
ProjectionElem::Subslice {from, .. })
493+
| (ProjectionElem::Subslice {from, .. },
494+
ProjectionElem::ConstantIndex { offset, min_length: _, from_end: false }) => {
495+
if offset >= from {
496+
debug!(
497+
"place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-SUBSLICE");
498+
Overlap::EqualOrDisjoint
499+
} else {
500+
debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-SUBSLICE");
501+
Overlap::Disjoint
502+
}
503+
}
504+
(ProjectionElem::ConstantIndex { offset, min_length: _, from_end: true },
505+
ProjectionElem::Subslice {from: _, to })
506+
| (ProjectionElem::Subslice {from: _, to },
507+
ProjectionElem::ConstantIndex { offset, min_length: _, from_end: true }) => {
508+
if offset > to {
509+
debug!("place_element_conflict: \
510+
DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-SUBSLICE-FE");
511+
Overlap::EqualOrDisjoint
512+
} else {
513+
debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-SUBSLICE-FE");
514+
Overlap::Disjoint
515+
}
516+
}
517+
(ProjectionElem::Subslice { .. }, ProjectionElem::Subslice { .. }) => {
518+
debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-SUBSLICES");
519+
Overlap::EqualOrDisjoint
520+
}
521+
(ProjectionElem::Deref, _)
522+
| (ProjectionElem::Field(..), _)
523+
| (ProjectionElem::Index(..), _)
524+
| (ProjectionElem::ConstantIndex { .. }, _)
525+
| (ProjectionElem::Subslice { .. }, _)
526+
| (ProjectionElem::Downcast(..), _) => bug!(
527+
"mismatched projections in place_element_conflict: {:?} and {:?}",
528+
pi1,
529+
pi2
580530
),
581531
}
582532
}

0 commit comments

Comments
 (0)
Please sign in to comment.