1 // Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use borrow_check::ArtificialField;
12 use borrow_check::Overlap;
13 use borrow_check::{Deep, Shallow, ShallowOrDeep};
15 use rustc::mir::{Mir, Place};
16 use rustc::mir::{Projection, ProjectionElem};
17 use rustc::ty::{self, TyCtxt};
20 pub(super) fn places_conflict<'gcx, 'tcx>(
21 tcx: TyCtxt<'_, 'gcx, 'tcx>,
23 borrow_place: &Place<'tcx>,
24 access_place: &Place<'tcx>,
25 access: ShallowOrDeep,
28 "places_conflict({:?},{:?},{:?})",
29 borrow_place, access_place, access
32 unroll_place(borrow_place, None, |borrow_components| {
33 unroll_place(access_place, None, |access_components| {
34 place_components_conflict(tcx, mir, borrow_components, access_components, access)
39 fn place_components_conflict<'gcx, 'tcx>(
40 tcx: TyCtxt<'_, 'gcx, 'tcx>,
42 mut borrow_components: PlaceComponentsIter<'_, 'tcx>,
43 mut access_components: PlaceComponentsIter<'_, 'tcx>,
44 access: ShallowOrDeep,
46 // The borrowck rules for proving disjointness are applied from the "root" of the
47 // borrow forwards, iterating over "similar" projections in lockstep until
48 // we can prove overlap one way or another. Essentially, we treat `Overlap` as
49 // a monoid and report a conflict if the product ends up not being `Disjoint`.
51 // At each step, if we didn't run out of borrow or place, we know that our elements
52 // have the same type, and that they only overlap if they are the identical.
54 // For example, if we are comparing these:
55 // BORROW: (*x1[2].y).z.a
56 // ACCESS: (*x1[i].y).w.b
58 // Then our steps are:
59 // x1 | x1 -- places are the same
60 // x1[2] | x1[i] -- equal or disjoint (disjoint if indexes differ)
61 // x1[2].y | x1[i].y -- equal or disjoint
62 // *x1[2].y | *x1[i].y -- equal or disjoint
63 // (*x1[2].y).z | (*x1[i].y).w -- we are disjoint and don't need to check more!
65 // Because `zip` does potentially bad things to the iterator inside, this loop
66 // also handles the case where the access might be a *prefix* of the borrow, e.g.
68 // BORROW: (*x1[2].y).z.a
71 // Then our steps are:
72 // x1 | x1 -- places are the same
73 // x1[2] | x1[i] -- equal or disjoint (disjoint if indexes differ)
74 // x1[2].y | x1[i].y -- equal or disjoint
76 // -- here we run out of access - the borrow can access a part of it. If this
77 // is a full deep access, then we *know* the borrow conflicts with it. However,
78 // if the access is shallow, then we can proceed:
80 // x1[2].y | (*x1[i].y) -- a deref! the access can't get past this, so we
83 // Our invariant is, that at each step of the iteration:
84 // - If we didn't run out of access to match, our borrow and access are comparable
85 // and either equal or disjoint.
86 // - If we did run out of accesss, the borrow can access a part of it.
88 // loop invariant: borrow_c is always either equal to access_c or disjoint from it.
89 if let Some(borrow_c) = borrow_components.next() {
90 debug!("places_conflict: borrow_c = {:?}", borrow_c);
92 if let Some(access_c) = access_components.next() {
93 debug!("places_conflict: access_c = {:?}", access_c);
95 // Borrow and access path both have more components.
99 // - borrow of `a.(...)`, access to `a.(...)`
100 // - borrow of `a.(...)`, access to `b.(...)`
102 // Here we only see the components we have checked so
103 // far (in our examples, just the first component). We
104 // check whether the components being borrowed vs
105 // accessed are disjoint (as in the second example,
106 // but not the first).
107 match place_element_conflict(tcx, mir, borrow_c, access_c) {
108 Overlap::Arbitrary => {
109 // We have encountered different fields of potentially
110 // the same union - the borrow now partially overlaps.
112 // There is no *easy* way of comparing the fields
113 // further on, because they might have different types
114 // (e.g. borrows of `u.a.0` and `u.b.y` where `.0` and
115 // `.y` come from different structs).
117 // We could try to do some things here - e.g. count
118 // dereferences - but that's probably not a good
119 // idea, at least for now, so just give up and
120 // report a conflict. This is unsafe code anyway so
121 // the user could always use raw pointers.
122 debug!("places_conflict: arbitrary -> conflict");
125 Overlap::EqualOrDisjoint => {
126 // This is the recursive case - proceed to the next element.
128 Overlap::Disjoint => {
129 // We have proven the borrow disjoint - further
130 // projections will remain disjoint.
131 debug!("places_conflict: disjoint");
136 // Borrow path is longer than the access path. Examples:
138 // - borrow of `a.b.c`, access to `a.b`
140 // Here, we know that the borrow can access a part of
141 // our place. This is a conflict if that is a part our
142 // access cares about.
144 let (base, elem) = match borrow_c {
145 Place::Projection(box Projection { base, elem }) => (base, elem),
146 _ => bug!("place has no base?"),
148 let base_ty = base.ty(mir, tcx).to_ty(tcx);
150 match (elem, &base_ty.sty, access) {
151 (_, _, Shallow(Some(ArtificialField::Discriminant)))
152 | (_, _, Shallow(Some(ArtificialField::ArrayLength))) => {
153 // The discriminant and array length are like
154 // additional fields on the type; they do not
155 // overlap any existing data there. Furthermore,
156 // they cannot actually be a prefix of any
157 // borrowed place (at least in MIR as it is
160 // e.g. a (mutable) borrow of `a[5]` while we read the
161 // array length of `a`.
162 debug!("places_conflict: implicit field");
166 (ProjectionElem::Deref, _, Shallow(None)) => {
167 // e.g. a borrow of `*x.y` while we shallowly access `x.y` or some
168 // prefix thereof - the shallow access can't touch anything behind
170 debug!("places_conflict: shallow access behind ptr");
173 (ProjectionElem::Deref, ty::TyRef(_, _, hir::MutImmutable), _) => {
174 // the borrow goes through a dereference of a shared reference.
176 // I'm not sure why we are tracking these borrows - shared
177 // references can *always* be aliased, which means the
178 // permission check already account for this borrow.
179 debug!("places_conflict: behind a shared ref");
183 (ProjectionElem::Deref, _, Deep)
184 | (ProjectionElem::Field { .. }, _, _)
185 | (ProjectionElem::Index { .. }, _, _)
186 | (ProjectionElem::ConstantIndex { .. }, _, _)
187 | (ProjectionElem::Subslice { .. }, _, _)
188 | (ProjectionElem::Downcast { .. }, _, _) => {
189 // Recursive case. This can still be disjoint on a
190 // further iteration if this a shallow access and
191 // there's a deref later on, e.g. a borrow
192 // of `*x.y` while accessing `x`.
197 // Borrow path ran out but access path may not
200 // - borrow of `a.b`, access to `a.b.c`
201 // - borrow of `a.b`, access to `a.b`
203 // In the first example, where we didn't run out of
204 // access, the borrow can access all of our place, so we
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.
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");
219 /// A linked list of places running up the stack; begins with the
220 /// innermost place and extends to projections (e.g., `a.b` would have
221 /// the place `a` with a "next" pointer to `a.b`). Created by
224 /// NB: This particular impl strategy is not the most obvious. It was
225 /// chosen because it makes a measurable difference to NLL
226 /// performance, as this code (`places_conflict`) is somewhat hot.
227 struct PlaceComponents<'p, 'tcx: 'p> {
228 component: &'p Place<'tcx>,
229 next: Option<&'p PlaceComponents<'p, 'tcx>>,
232 impl<'p, 'tcx> PlaceComponents<'p, 'tcx> {
233 /// Converts a list of `Place` components into an iterator; this
234 /// iterator yields up a never-ending stream of `Option<&Place>`.
235 /// These begin with the "innermst" place and then with each
236 /// projection therefrom. So given a place like `a.b.c` it would
240 /// Some(`a`), Some(`a.b`), Some(`a.b.c`), None, None, ...
242 fn iter(&self) -> PlaceComponentsIter<'_, 'tcx> {
243 PlaceComponentsIter { value: Some(self) }
247 /// Iterator over components; see `PlaceComponents::iter` for more
250 /// NB: This is not a *true* Rust iterator -- the code above just
251 /// manually invokes `next`. This is because we (sometimes) want to
252 /// keep executing even after `None` has been returned.
253 struct PlaceComponentsIter<'p, 'tcx: 'p> {
254 value: Option<&'p PlaceComponents<'p, 'tcx>>,
257 impl<'p, 'tcx> PlaceComponentsIter<'p, 'tcx> {
258 fn next(&mut self) -> Option<&'p Place<'tcx>> {
259 if let Some(&PlaceComponents { component, next }) = self.value {
268 /// Recursively "unroll" a place into a `PlaceComponents` list,
269 /// invoking `op` with a `PlaceComponentsIter`.
270 fn unroll_place<'tcx, R>(
272 next: Option<&PlaceComponents<'_, 'tcx>>,
273 op: impl FnOnce(PlaceComponentsIter<'_, 'tcx>) -> R,
276 Place::Projection(interior) => unroll_place(
278 Some(&PlaceComponents {
286 Place::Local(_) | Place::Static(_) => {
287 let list = PlaceComponents {
296 // Given that the bases of `elem1` and `elem2` are always either equal
297 // or disjoint (and have the same type!), return the overlap situation
298 // between `elem1` and `elem2`.
299 fn place_element_conflict<'a, 'gcx: 'tcx, 'tcx>(
300 tcx: TyCtxt<'a, 'gcx, 'tcx>,
305 match (elem1, elem2) {
306 (Place::Local(l1), Place::Local(l2)) => {
308 // the same local - base case, equal
309 debug!("place_element_conflict: DISJOINT-OR-EQ-LOCAL");
310 Overlap::EqualOrDisjoint
312 // different locals - base case, disjoint
313 debug!("place_element_conflict: DISJOINT-LOCAL");
317 (Place::Static(static1), Place::Static(static2)) => {
318 if static1.def_id != static2.def_id {
319 debug!("place_element_conflict: DISJOINT-STATIC");
321 } else if tcx.is_static(static1.def_id) == Some(hir::Mutability::MutMutable) {
322 // We ignore mutable statics - they can only be unsafe code.
323 debug!("place_element_conflict: IGNORE-STATIC-MUT");
326 debug!("place_element_conflict: DISJOINT-OR-EQ-STATIC");
327 Overlap::EqualOrDisjoint
330 (Place::Promoted(p1), Place::Promoted(p2)) => {
332 // the same promoted - base case, equal
333 debug!("place_element_conflict: DISJOINT-OR-EQ-PROMOTED");
334 Overlap::EqualOrDisjoint
336 // different promoteds - base case, disjoint
337 debug!("place_element_conflict: DISJOINT-PROMOTED");
341 (Place::Local(_), Place::Promoted(_)) | (Place::Promoted(_), Place::Local(_)) |
342 (Place::Promoted(_), Place::Static(_)) | (Place::Static(_), Place::Promoted(_)) |
343 (Place::Local(_), Place::Static(_)) | (Place::Static(_), Place::Local(_)) => {
344 debug!("place_element_conflict: DISJOINT-STATIC-LOCAL-PROMOTED");
347 (Place::Projection(pi1), Place::Projection(pi2)) => {
348 match (&pi1.elem, &pi2.elem) {
349 (ProjectionElem::Deref, ProjectionElem::Deref) => {
350 // derefs (e.g. `*x` vs. `*x`) - recur.
351 debug!("place_element_conflict: DISJOINT-OR-EQ-DEREF");
352 Overlap::EqualOrDisjoint
354 (ProjectionElem::Field(f1, _), ProjectionElem::Field(f2, _)) => {
356 // same field (e.g. `a.y` vs. `a.y`) - recur.
357 debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD");
358 Overlap::EqualOrDisjoint
360 let ty = pi1.base.ty(mir, tcx).to_ty(tcx);
362 ty::TyAdt(def, _) if def.is_union() => {
363 // Different fields of a union, we are basically stuck.
364 debug!("place_element_conflict: STUCK-UNION");
368 // Different fields of a struct (`a.x` vs. `a.y`). Disjoint!
369 debug!("place_element_conflict: DISJOINT-FIELD");
375 (ProjectionElem::Downcast(_, v1), ProjectionElem::Downcast(_, v2)) => {
376 // different variants are treated as having disjoint fields,
377 // even if they occupy the same "space", because it's
378 // impossible for 2 variants of the same enum to exist
379 // (and therefore, to be borrowed) at the same time.
381 // Note that this is different from unions - we *do* allow
382 // this code to compile:
385 // fn foo(x: &mut Result<i32, i32>) {
387 // if let Ok(ref mut a) = *x {
390 // // here, you would *think* that the
391 // // *entirety* of `x` would be borrowed,
392 // // but in fact only the `Ok` variant is,
393 // // so the `Err` variant is *entirely free*:
394 // if let Err(ref mut a) = *x {
401 debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD");
402 Overlap::EqualOrDisjoint
404 debug!("place_element_conflict: DISJOINT-FIELD");
408 (ProjectionElem::Index(..), ProjectionElem::Index(..))
409 | (ProjectionElem::Index(..), ProjectionElem::ConstantIndex { .. })
410 | (ProjectionElem::Index(..), ProjectionElem::Subslice { .. })
411 | (ProjectionElem::ConstantIndex { .. }, ProjectionElem::Index(..))
412 | (ProjectionElem::Subslice { .. }, ProjectionElem::Index(..)) => {
413 // Array indexes (`a[0]` vs. `a[i]`). These can either be disjoint
414 // (if the indexes differ) or equal (if they are the same), so this
415 // is the recursive case that gives "equal *or* disjoint" its meaning.
416 debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-INDEX");
417 Overlap::EqualOrDisjoint
419 (ProjectionElem::ConstantIndex { offset: o1, min_length: _, from_end: false },
420 ProjectionElem::ConstantIndex { offset: o2, min_length: _, from_end: false })
421 | (ProjectionElem::ConstantIndex { offset: o1, min_length: _, from_end: true },
422 ProjectionElem::ConstantIndex {
423 offset: o2, min_length: _, from_end: true }) => {
425 debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX");
426 Overlap::EqualOrDisjoint
428 debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX");
432 (ProjectionElem::ConstantIndex {
433 offset: offset_from_begin, min_length: min_length1, from_end: false },
434 ProjectionElem::ConstantIndex {
435 offset: offset_from_end, min_length: min_length2, from_end: true })
436 | (ProjectionElem::ConstantIndex {
437 offset: offset_from_end, min_length: min_length1, from_end: true },
438 ProjectionElem::ConstantIndex {
439 offset: offset_from_begin, min_length: min_length2, from_end: false }) => {
440 // both patterns matched so it must be at least the greater of the two
441 let min_length = max(min_length1, min_length2);
442 // `offset_from_end` can be in range `[1..min_length]`, 1 indicates the last
443 // element (like -1 in Python) and `min_length` the first.
444 // Therefore, `min_length - offset_from_end` gives the minimal possible
445 // offset from the beginning
446 if *offset_from_begin >= min_length - offset_from_end {
447 debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-FE");
448 Overlap::EqualOrDisjoint
450 debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-FE");
454 (ProjectionElem::ConstantIndex { offset, min_length: _, from_end: false },
455 ProjectionElem::Subslice {from, .. })
456 | (ProjectionElem::Subslice {from, .. },
457 ProjectionElem::ConstantIndex { offset, min_length: _, from_end: false }) => {
460 "place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-SUBSLICE");
461 Overlap::EqualOrDisjoint
463 debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-SUBSLICE");
467 (ProjectionElem::ConstantIndex { offset, min_length: _, from_end: true },
468 ProjectionElem::Subslice {from: _, to })
469 | (ProjectionElem::Subslice {from: _, to },
470 ProjectionElem::ConstantIndex { offset, min_length: _, from_end: true }) => {
472 debug!("place_element_conflict: \
473 DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-SUBSLICE-FE");
474 Overlap::EqualOrDisjoint
476 debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-SUBSLICE-FE");
480 (ProjectionElem::Subslice { .. }, ProjectionElem::Subslice { .. }) => {
481 debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-SUBSLICES");
482 Overlap::EqualOrDisjoint
484 (ProjectionElem::Deref, _)
485 | (ProjectionElem::Field(..), _)
486 | (ProjectionElem::Index(..), _)
487 | (ProjectionElem::ConstantIndex { .. }, _)
488 | (ProjectionElem::Subslice { .. }, _)
489 | (ProjectionElem::Downcast(..), _) => bug!(
490 "mismatched projections in place_element_conflict: {:?} and {:?}",
496 (Place::Projection(_), _) | (_, Place::Projection(_)) => bug!(
497 "unexpected elements in place_element_conflict: {:?} and {:?}",