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, AccessDepth};
15 use rustc::mir::{BorrowKind, Mir, Place};
16 use rustc::mir::{Projection, ProjectionElem};
17 use rustc::ty::{self, TyCtxt};
20 pub(super) fn borrow_conflicts_with_place<'gcx, 'tcx>(
21 tcx: TyCtxt<'_, 'gcx, 'tcx>,
23 borrow_place: &Place<'tcx>,
24 borrow_kind: BorrowKind,
25 access_place: &Place<'tcx>,
29 "borrow_conflicts_with_place({:?},{:?},{:?})",
30 borrow_place, access_place, access
33 // This Local/Local case is handled by the more general code below, but
34 // it's so common that it's a speed win to check for it first.
35 if let Place::Local(l1) = borrow_place {
36 if let Place::Local(l2) = access_place {
41 unroll_place(borrow_place, None, |borrow_components| {
42 unroll_place(access_place, None, |access_components| {
43 place_components_conflict(
55 fn place_components_conflict<'gcx, 'tcx>(
56 tcx: TyCtxt<'_, 'gcx, 'tcx>,
58 mut borrow_components: PlaceComponentsIter<'_, 'tcx>,
59 borrow_kind: BorrowKind,
60 mut access_components: PlaceComponentsIter<'_, 'tcx>,
63 // The borrowck rules for proving disjointness are applied from the "root" of the
64 // borrow forwards, iterating over "similar" projections in lockstep until
65 // we can prove overlap one way or another. Essentially, we treat `Overlap` as
66 // a monoid and report a conflict if the product ends up not being `Disjoint`.
68 // At each step, if we didn't run out of borrow or place, we know that our elements
69 // have the same type, and that they only overlap if they are the identical.
71 // For example, if we are comparing these:
72 // BORROW: (*x1[2].y).z.a
73 // ACCESS: (*x1[i].y).w.b
75 // Then our steps are:
76 // x1 | x1 -- places are the same
77 // x1[2] | x1[i] -- equal or disjoint (disjoint if indexes differ)
78 // x1[2].y | x1[i].y -- equal or disjoint
79 // *x1[2].y | *x1[i].y -- equal or disjoint
80 // (*x1[2].y).z | (*x1[i].y).w -- we are disjoint and don't need to check more!
82 // Because `zip` does potentially bad things to the iterator inside, this loop
83 // also handles the case where the access might be a *prefix* of the borrow, e.g.
85 // BORROW: (*x1[2].y).z.a
88 // Then our steps are:
89 // x1 | x1 -- places are the same
90 // x1[2] | x1[i] -- equal or disjoint (disjoint if indexes differ)
91 // x1[2].y | x1[i].y -- equal or disjoint
93 // -- here we run out of access - the borrow can access a part of it. If this
94 // is a full deep access, then we *know* the borrow conflicts with it. However,
95 // if the access is shallow, then we can proceed:
97 // x1[2].y | (*x1[i].y) -- a deref! the access can't get past this, so we
100 // Our invariant is, that at each step of the iteration:
101 // - If we didn't run out of access to match, our borrow and access are comparable
102 // and either equal or disjoint.
103 // - If we did run out of access, the borrow can access a part of it.
105 // loop invariant: borrow_c is always either equal to access_c or disjoint from it.
106 if let Some(borrow_c) = borrow_components.next() {
107 debug!("borrow_conflicts_with_place: borrow_c = {:?}", borrow_c);
109 if let Some(access_c) = access_components.next() {
110 debug!("borrow_conflicts_with_place: access_c = {:?}", access_c);
112 // Borrow and access path both have more components.
116 // - borrow of `a.(...)`, access to `a.(...)`
117 // - borrow of `a.(...)`, access to `b.(...)`
119 // Here we only see the components we have checked so
120 // far (in our examples, just the first component). We
121 // check whether the components being borrowed vs
122 // accessed are disjoint (as in the second example,
123 // but not the first).
124 match place_element_conflict(tcx, mir, borrow_c, access_c) {
125 Overlap::Arbitrary => {
126 // We have encountered different fields of potentially
127 // the same union - the borrow now partially overlaps.
129 // There is no *easy* way of comparing the fields
130 // further on, because they might have different types
131 // (e.g. borrows of `u.a.0` and `u.b.y` where `.0` and
132 // `.y` come from different structs).
134 // We could try to do some things here - e.g. count
135 // dereferences - but that's probably not a good
136 // idea, at least for now, so just give up and
137 // report a conflict. This is unsafe code anyway so
138 // the user could always use raw pointers.
139 debug!("borrow_conflicts_with_place: arbitrary -> conflict");
142 Overlap::EqualOrDisjoint => {
143 // This is the recursive case - proceed to the next element.
145 Overlap::Disjoint => {
146 // We have proven the borrow disjoint - further
147 // projections will remain disjoint.
148 debug!("borrow_conflicts_with_place: disjoint");
153 // Borrow path is longer than the access path. Examples:
155 // - borrow of `a.b.c`, access to `a.b`
157 // Here, we know that the borrow can access a part of
158 // our place. This is a conflict if that is a part our
159 // access cares about.
161 let (base, elem) = match borrow_c {
162 Place::Projection(box Projection { base, elem }) => (base, elem),
163 _ => bug!("place has no base?"),
165 let base_ty = base.ty(mir, tcx).to_ty(tcx);
167 match (elem, &base_ty.sty, access) {
168 (_, _, Shallow(Some(ArtificialField::Discriminant)))
169 | (_, _, Shallow(Some(ArtificialField::ArrayLength)))
170 | (_, _, Shallow(Some(ArtificialField::ShallowBorrow))) => {
171 // The discriminant and array length are like
172 // additional fields on the type; they do not
173 // overlap any existing data there. Furthermore,
174 // they cannot actually be a prefix of any
175 // borrowed place (at least in MIR as it is
178 // e.g. a (mutable) borrow of `a[5]` while we read the
179 // array length of `a`.
180 debug!("borrow_conflicts_with_place: implicit field");
184 (ProjectionElem::Deref, _, Shallow(None)) => {
185 // e.g. a borrow of `*x.y` while we shallowly access `x.y` or some
186 // prefix thereof - the shallow access can't touch anything behind
188 debug!("borrow_conflicts_with_place: shallow access behind ptr");
191 (ProjectionElem::Deref, ty::Ref(_, _, hir::MutImmutable), _) => {
192 // Shouldn't be tracked
193 bug!("Tracking borrow behind shared reference.");
195 (ProjectionElem::Deref, ty::Ref(_, _, hir::MutMutable), AccessDepth::Drop) => {
196 // Values behind a mutatble reference are not access either by Dropping a
197 // value, or by StorageDead
198 debug!("borrow_conflicts_with_place: drop access behind ptr");
202 (ProjectionElem::Field { .. }, ty::Adt(def, _), AccessDepth::Drop) => {
203 // Drop can read/write arbitrary projections, so places
204 // conflict regardless of further projections.
205 if def.has_dtor(tcx) {
210 (ProjectionElem::Deref, _, Deep)
211 | (ProjectionElem::Deref, _, AccessDepth::Drop)
212 | (ProjectionElem::Field { .. }, _, _)
213 | (ProjectionElem::Index { .. }, _, _)
214 | (ProjectionElem::ConstantIndex { .. }, _, _)
215 | (ProjectionElem::Subslice { .. }, _, _)
216 | (ProjectionElem::Downcast { .. }, _, _) => {
217 // Recursive case. This can still be disjoint on a
218 // further iteration if this a shallow access and
219 // there's a deref later on, e.g. a borrow
220 // of `*x.y` while accessing `x`.
225 // Borrow path ran out but access path may not
228 // - borrow of `a.b`, access to `a.b.c`
229 // - borrow of `a.b`, access to `a.b`
231 // In the first example, where we didn't run out of
232 // access, the borrow can access all of our place, so we
235 // If the second example, where we did, then we still know
236 // that the borrow can access a *part* of our place that
237 // our access cares about, so we still have a conflict.
238 if borrow_kind == BorrowKind::Shallow && access_components.next().is_some() {
239 debug!("borrow_conflicts_with_place: shallow borrow");
242 debug!("borrow_conflicts_with_place: full borrow, CONFLICT");
249 /// A linked list of places running up the stack; begins with the
250 /// innermost place and extends to projections (e.g., `a.b` would have
251 /// the place `a` with a "next" pointer to `a.b`). Created by
254 /// NB: This particular impl strategy is not the most obvious. It was
255 /// chosen because it makes a measurable difference to NLL
256 /// performance, as this code (`borrow_conflicts_with_place`) is somewhat hot.
257 struct PlaceComponents<'p, 'tcx: 'p> {
258 component: &'p Place<'tcx>,
259 next: Option<&'p PlaceComponents<'p, 'tcx>>,
262 impl<'p, 'tcx> PlaceComponents<'p, 'tcx> {
263 /// Converts a list of `Place` components into an iterator; this
264 /// iterator yields up a never-ending stream of `Option<&Place>`.
265 /// These begin with the "innermost" place and then with each
266 /// projection therefrom. So given a place like `a.b.c` it would
270 /// Some(`a`), Some(`a.b`), Some(`a.b.c`), None, None, ...
272 fn iter(&self) -> PlaceComponentsIter<'_, 'tcx> {
273 PlaceComponentsIter { value: Some(self) }
277 /// Iterator over components; see `PlaceComponents::iter` for more
280 /// NB: This is not a *true* Rust iterator -- the code above just
281 /// manually invokes `next`. This is because we (sometimes) want to
282 /// keep executing even after `None` has been returned.
283 struct PlaceComponentsIter<'p, 'tcx: 'p> {
284 value: Option<&'p PlaceComponents<'p, 'tcx>>,
287 impl<'p, 'tcx> PlaceComponentsIter<'p, 'tcx> {
288 fn next(&mut self) -> Option<&'p Place<'tcx>> {
289 if let Some(&PlaceComponents { component, next }) = self.value {
298 /// Recursively "unroll" a place into a `PlaceComponents` list,
299 /// invoking `op` with a `PlaceComponentsIter`.
300 fn unroll_place<'tcx, R>(
302 next: Option<&PlaceComponents<'_, 'tcx>>,
303 op: impl FnOnce(PlaceComponentsIter<'_, 'tcx>) -> R,
306 Place::Projection(interior) => unroll_place(
308 Some(&PlaceComponents {
316 Place::Local(_) | Place::Static(_) => {
317 let list = PlaceComponents {
326 // Given that the bases of `elem1` and `elem2` are always either equal
327 // or disjoint (and have the same type!), return the overlap situation
328 // between `elem1` and `elem2`.
329 fn place_element_conflict<'a, 'gcx: 'tcx, 'tcx>(
330 tcx: TyCtxt<'a, 'gcx, 'tcx>,
335 match (elem1, elem2) {
336 (Place::Local(l1), Place::Local(l2)) => {
338 // the same local - base case, equal
339 debug!("place_element_conflict: DISJOINT-OR-EQ-LOCAL");
340 Overlap::EqualOrDisjoint
342 // different locals - base case, disjoint
343 debug!("place_element_conflict: DISJOINT-LOCAL");
347 (Place::Static(static1), Place::Static(static2)) => {
348 if static1.def_id != static2.def_id {
349 debug!("place_element_conflict: DISJOINT-STATIC");
351 } else if tcx.is_static(static1.def_id) == Some(hir::Mutability::MutMutable) {
352 // We ignore mutable statics - they can only be unsafe code.
353 debug!("place_element_conflict: IGNORE-STATIC-MUT");
356 debug!("place_element_conflict: DISJOINT-OR-EQ-STATIC");
357 Overlap::EqualOrDisjoint
360 (Place::Promoted(p1), Place::Promoted(p2)) => {
362 if let ty::Array(_, size) = p1.1.sty {
363 if size.unwrap_usize(tcx) == 0 {
364 // Ignore conflicts with promoted [T; 0].
365 debug!("place_element_conflict: IGNORE-LEN-0-PROMOTED");
366 return Overlap::Disjoint;
369 // the same promoted - base case, equal
370 debug!("place_element_conflict: DISJOINT-OR-EQ-PROMOTED");
371 Overlap::EqualOrDisjoint
373 // different promoteds - base case, disjoint
374 debug!("place_element_conflict: DISJOINT-PROMOTED");
378 (Place::Local(_), Place::Promoted(_)) | (Place::Promoted(_), Place::Local(_)) |
379 (Place::Promoted(_), Place::Static(_)) | (Place::Static(_), Place::Promoted(_)) |
380 (Place::Local(_), Place::Static(_)) | (Place::Static(_), Place::Local(_)) => {
381 debug!("place_element_conflict: DISJOINT-STATIC-LOCAL-PROMOTED");
384 (Place::Projection(pi1), Place::Projection(pi2)) => {
385 match (&pi1.elem, &pi2.elem) {
386 (ProjectionElem::Deref, ProjectionElem::Deref) => {
387 // derefs (e.g. `*x` vs. `*x`) - recur.
388 debug!("place_element_conflict: DISJOINT-OR-EQ-DEREF");
389 Overlap::EqualOrDisjoint
391 (ProjectionElem::Field(f1, _), ProjectionElem::Field(f2, _)) => {
393 // same field (e.g. `a.y` vs. `a.y`) - recur.
394 debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD");
395 Overlap::EqualOrDisjoint
397 let ty = pi1.base.ty(mir, tcx).to_ty(tcx);
399 ty::Adt(def, _) if def.is_union() => {
400 // Different fields of a union, we are basically stuck.
401 debug!("place_element_conflict: STUCK-UNION");
405 // Different fields of a struct (`a.x` vs. `a.y`). Disjoint!
406 debug!("place_element_conflict: DISJOINT-FIELD");
412 (ProjectionElem::Downcast(_, v1), ProjectionElem::Downcast(_, v2)) => {
413 // different variants are treated as having disjoint fields,
414 // even if they occupy the same "space", because it's
415 // impossible for 2 variants of the same enum to exist
416 // (and therefore, to be borrowed) at the same time.
418 // Note that this is different from unions - we *do* allow
419 // this code to compile:
422 // fn foo(x: &mut Result<i32, i32>) {
424 // if let Ok(ref mut a) = *x {
427 // // here, you would *think* that the
428 // // *entirety* of `x` would be borrowed,
429 // // but in fact only the `Ok` variant is,
430 // // so the `Err` variant is *entirely free*:
431 // if let Err(ref mut a) = *x {
438 debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD");
439 Overlap::EqualOrDisjoint
441 debug!("place_element_conflict: DISJOINT-FIELD");
445 (ProjectionElem::Index(..), ProjectionElem::Index(..))
446 | (ProjectionElem::Index(..), ProjectionElem::ConstantIndex { .. })
447 | (ProjectionElem::Index(..), ProjectionElem::Subslice { .. })
448 | (ProjectionElem::ConstantIndex { .. }, ProjectionElem::Index(..))
449 | (ProjectionElem::Subslice { .. }, ProjectionElem::Index(..)) => {
450 // Array indexes (`a[0]` vs. `a[i]`). These can either be disjoint
451 // (if the indexes differ) or equal (if they are the same), so this
452 // is the recursive case that gives "equal *or* disjoint" its meaning.
453 debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-INDEX");
454 Overlap::EqualOrDisjoint
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 }) => {
462 debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX");
463 Overlap::EqualOrDisjoint
465 debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX");
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
487 debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-FE");
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 }) => {
497 "place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-SUBSLICE");
498 Overlap::EqualOrDisjoint
500 debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-SUBSLICE");
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 }) => {
509 debug!("place_element_conflict: \
510 DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-SUBSLICE-FE");
511 Overlap::EqualOrDisjoint
513 debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-SUBSLICE-FE");
517 (ProjectionElem::Subslice { .. }, ProjectionElem::Subslice { .. }) => {
518 debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-SUBSLICES");
519 Overlap::EqualOrDisjoint
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 {:?}",
533 (Place::Projection(_), _) | (_, Place::Projection(_)) => bug!(
534 "unexpected elements in place_element_conflict: {:?} and {:?}",