Skip to content

Commit e10414e

Browse files
committed
avoid use of vectors etc
1 parent edf14d1 commit e10414e

File tree

1 file changed

+150
-79
lines changed

1 file changed

+150
-79
lines changed

src/librustc_mir/borrow_check/places_conflict.rs

Lines changed: 150 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,9 @@ use rustc::hir;
1515
use rustc::mir::{Mir, Place};
1616
use rustc::mir::{Projection, ProjectionElem};
1717
use rustc::ty::{self, TyCtxt};
18-
use rustc_data_structures::small_vec::SmallVec;
19-
use std::iter;
2018

21-
pub(super) fn places_conflict<'a, 'gcx: 'tcx, 'tcx>(
22-
tcx: TyCtxt<'a, 'gcx, 'tcx>,
19+
pub(super) fn places_conflict<'gcx, 'tcx>(
20+
tcx: TyCtxt<'_, 'gcx, 'tcx>,
2321
mir: &Mir<'tcx>,
2422
borrow_place: &Place<'tcx>,
2523
access_place: &Place<'tcx>,
@@ -30,21 +28,20 @@ pub(super) fn places_conflict<'a, 'gcx: 'tcx, 'tcx>(
3028
borrow_place, access_place, access
3129
);
3230

33-
let borrow_components = place_elements(borrow_place);
34-
let access_components = place_elements(access_place);
35-
debug!(
36-
"places_conflict: components {:?} / {:?}",
37-
borrow_components, access_components
38-
);
31+
unroll_place(borrow_place, None, |borrow_components| {
32+
unroll_place(access_place, None, |access_components| {
33+
place_components_conflict(tcx, mir, borrow_components, access_components, access)
34+
})
35+
})
36+
}
3937

40-
let borrow_components = borrow_components
41-
.into_iter()
42-
.map(Some)
43-
.chain(iter::repeat(None));
44-
let access_components = access_components
45-
.into_iter()
46-
.map(Some)
47-
.chain(iter::repeat(None));
38+
fn place_components_conflict<'gcx, 'tcx>(
39+
tcx: TyCtxt<'_, 'gcx, 'tcx>,
40+
mir: &Mir<'tcx>,
41+
borrow_components: PlaceComponentsIter<'_, 'tcx>,
42+
access_components: PlaceComponentsIter<'_, 'tcx>,
43+
access: ShallowOrDeep,
44+
) -> bool {
4845
// The borrowck rules for proving disjointness are applied from the "root" of the
4946
// borrow forwards, iterating over "similar" projections in lockstep until
5047
// we can prove overlap one way or another. Essentially, we treat `Overlap` as
@@ -89,24 +86,56 @@ pub(super) fn places_conflict<'a, 'gcx: 'tcx, 'tcx>(
8986
for (borrow_c, access_c) in borrow_components.zip(access_components) {
9087
// loop invariant: borrow_c is always either equal to access_c or disjoint from it.
9188
debug!("places_conflict: {:?} vs. {:?}", borrow_c, access_c);
92-
match (borrow_c, access_c) {
93-
(None, _) => {
94-
// If we didn't run out of access, the borrow can access all of our
95-
// place (e.g. a borrow of `a.b` with an access to `a.b.c`),
96-
// so we have a conflict.
89+
if let Some(borrow_c) = borrow_c {
90+
if let Some(access_c) = access_c {
91+
// Borrow and access path both have more components.
9792
//
98-
// If we did, then we still know that the borrow can access a *part*
99-
// of our place that our access cares about (a borrow of `a.b.c`
100-
// with an access to `a.b`), so we still have a conflict.
93+
// Examples:
10194
//
102-
// FIXME: Differs from AST-borrowck; includes drive-by fix
103-
// to #38899. Will probably need back-compat mode flag.
104-
debug!("places_conflict: full borrow, CONFLICT");
105-
return true;
106-
}
107-
(Some(borrow_c), None) => {
108-
// We know that the borrow can access a part of our place. This
109-
// is a conflict if that is a part our access cares about.
95+
// - borrow of `a.(...)`, access to `a.(...)`
96+
// - borrow of `a.(...)`, access to `b.(...)`
97+
//
98+
// Here we only see the components we have checked so
99+
// far (in our examples, just the first component). We
100+
// check whether the components being borrowed vs
101+
// accessed are disjoint (as in the second example,
102+
// but not the first).
103+
match place_element_conflict(tcx, mir, borrow_c, access_c) {
104+
Overlap::Arbitrary => {
105+
// We have encountered different fields of potentially
106+
// the same union - the borrow now partially overlaps.
107+
//
108+
// There is no *easy* way of comparing the fields
109+
// further on, because they might have different types
110+
// (e.g. borrows of `u.a.0` and `u.b.y` where `.0` and
111+
// `.y` come from different structs).
112+
//
113+
// We could try to do some things here - e.g. count
114+
// dereferences - but that's probably not a good
115+
// idea, at least for now, so just give up and
116+
// report a conflict. This is unsafe code anyway so
117+
// the user could always use raw pointers.
118+
debug!("places_conflict: arbitrary -> conflict");
119+
return true;
120+
}
121+
Overlap::EqualOrDisjoint => {
122+
// This is the recursive case - proceed to the next element.
123+
}
124+
Overlap::Disjoint => {
125+
// We have proven the borrow disjoint - further
126+
// projections will remain disjoint.
127+
debug!("places_conflict: disjoint");
128+
return false;
129+
}
130+
}
131+
} else {
132+
// Borrow path is longer than the access path. Examples:
133+
//
134+
// - borrow of `a.b.c`, access to `a.b`
135+
//
136+
// Here, we know that the borrow can access a part of
137+
// our place. This is a conflict if that is a part our
138+
// access cares about.
110139

111140
let (base, elem) = match borrow_c {
112141
Place::Projection(box Projection { base, elem }) => (base, elem),
@@ -164,56 +193,98 @@ pub(super) fn places_conflict<'a, 'gcx: 'tcx, 'tcx>(
164193
}
165194
}
166195
}
167-
(Some(borrow_c), Some(access_c)) => {
168-
match place_element_conflict(tcx, mir, &borrow_c, access_c) {
169-
Overlap::Arbitrary => {
170-
// We have encountered different fields of potentially
171-
// the same union - the borrow now partially overlaps.
172-
//
173-
// There is no *easy* way of comparing the fields
174-
// further on, because they might have different types
175-
// (e.g. borrows of `u.a.0` and `u.b.y` where `.0` and
176-
// `.y` come from different structs).
177-
//
178-
// We could try to do some things here - e.g. count
179-
// dereferences - but that's probably not a good
180-
// idea, at least for now, so just give up and
181-
// report a conflict. This is unsafe code anyway so
182-
// the user could always use raw pointers.
183-
debug!("places_conflict: arbitrary -> conflict");
184-
return true;
185-
}
186-
Overlap::EqualOrDisjoint => {
187-
// This is the recursive case - proceed to the next element.
188-
}
189-
Overlap::Disjoint => {
190-
// We have proven the borrow disjoint - further
191-
// projections will remain disjoint.
192-
debug!("places_conflict: disjoint");
193-
return false;
194-
}
195-
}
196-
}
196+
} else {
197+
// Borrow path ran out but access path may not
198+
// have. Examples:
199+
//
200+
// - borrow of `a.b`, access to `a.b.c`
201+
// - borrow of `a.b`, access to `a.b`
202+
//
203+
// In the first example, where we didn't run out of
204+
// access, the borrow can access all of our place, so we
205+
// have a conflict.
206+
//
207+
// If the second example, where we did, then we still know
208+
// that the borrow can access a *part* of our place that
209+
// our access cares about, so we still have a conflict.
210+
//
211+
// FIXME: Differs from AST-borrowck; includes drive-by fix
212+
// to #38899. Will probably need back-compat mode flag.
213+
debug!("places_conflict: full borrow, CONFLICT");
214+
return true;
197215
}
198216
}
199217
unreachable!("iter::repeat returned None")
200218
}
201219

202-
/// Return all the prefixes of `place` in reverse order, including
203-
/// downcasts.
204-
fn place_elements<'a, 'tcx>(place: &'a Place<'tcx>) -> SmallVec<[&'a Place<'tcx>; 8]> {
205-
let mut result = SmallVec::new();
206-
let mut place = place;
207-
loop {
208-
result.push(place);
209-
match place {
210-
Place::Projection(interior) => {
211-
place = &interior.base;
212-
}
213-
Place::Local(_) | Place::Static(_) => {
214-
result.reverse();
215-
return result;
216-
}
220+
/// A linked list of places running up the stack; begins with the
221+
/// innermost place and extends to projections (e.g., `a.b` would have
222+
/// the place `a` with a "next" pointer to `a.b`). Created by
223+
/// `unroll_place`.
224+
struct PlaceComponents<'p, 'tcx: 'p> {
225+
component: &'p Place<'tcx>,
226+
next: Option<&'p PlaceComponents<'p, 'tcx>>,
227+
}
228+
229+
impl<'p, 'tcx> PlaceComponents<'p, 'tcx> {
230+
/// Converts a list of `Place` components into an iterator; this
231+
/// iterator yields up a never-ending stream of `Option<&Place>`.
232+
/// These begin with the "innermst" place and then with each
233+
/// projection therefrom. So given a place like `a.b.c` it would
234+
/// yield up:
235+
///
236+
/// ```notrust
237+
/// Some(`a`), Some(`a.b`), Some(`a.b.c`), None, None, ...
238+
/// ```
239+
fn iter(&self) -> PlaceComponentsIter<'_, 'tcx> {
240+
PlaceComponentsIter { value: Some(self) }
241+
}
242+
}
243+
244+
/// Iterator over components; see `PlaceComponents::iter` for more
245+
/// information.
246+
struct PlaceComponentsIter<'p, 'tcx: 'p> {
247+
value: Option<&'p PlaceComponents<'p, 'tcx>>
248+
}
249+
250+
impl<'p, 'tcx> Iterator for PlaceComponentsIter<'p, 'tcx> {
251+
type Item = Option<&'p Place<'tcx>>;
252+
253+
fn next(&mut self) -> Option<Self::Item> {
254+
if let Some(&PlaceComponents { component, next }) = self.value {
255+
self.value = next;
256+
Some(Some(component))
257+
} else {
258+
Some(None)
259+
}
260+
}
261+
}
262+
263+
/// Recursively "unroll" a place into a `PlaceComponents` list,
264+
/// invoking `op` with a `PlaceComponentsIter`.
265+
fn unroll_place<'tcx, R>(
266+
place: &Place<'tcx>,
267+
next: Option<&PlaceComponents<'_, 'tcx>>,
268+
op: impl FnOnce(PlaceComponentsIter<'_, 'tcx>) -> R
269+
) -> R {
270+
match place {
271+
Place::Projection(interior) => {
272+
unroll_place(
273+
&interior.base,
274+
Some(&PlaceComponents {
275+
component: place,
276+
next,
277+
}),
278+
op,
279+
)
280+
}
281+
282+
Place::Local(_) | Place::Static(_) => {
283+
let list = PlaceComponents {
284+
component: place,
285+
next,
286+
};
287+
op(list.iter())
217288
}
218289
}
219290
}

0 commit comments

Comments
 (0)