]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/borrow_check/places_conflict.rs
715d6e0c0d1b3685431039b4c0911dd7ee6a13c1
[rust.git] / src / librustc_mir / borrow_check / places_conflict.rs
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.
4 //
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.
10
11 use borrow_check::ArtificialField;
12 use borrow_check::Overlap;
13 use borrow_check::{Deep, Shallow, AccessDepth};
14 use rustc::hir;
15 use rustc::mir::{BorrowKind, Mir, Place};
16 use rustc::mir::{Projection, ProjectionElem};
17 use rustc::ty::{self, TyCtxt};
18 use std::cmp::max;
19
20 pub(super) fn borrow_conflicts_with_place<'gcx, 'tcx>(
21     tcx: TyCtxt<'_, 'gcx, 'tcx>,
22     mir: &Mir<'tcx>,
23     borrow_place: &Place<'tcx>,
24     borrow_kind: BorrowKind,
25     access_place: &Place<'tcx>,
26     access: AccessDepth,
27 ) -> bool {
28     debug!(
29         "borrow_conflicts_with_place({:?},{:?},{:?})",
30         borrow_place, access_place, access
31     );
32
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 {
37             return l1 == l2;
38         }
39     }
40
41     unroll_place(borrow_place, None, |borrow_components| {
42         unroll_place(access_place, None, |access_components| {
43             place_components_conflict(
44                 tcx,
45                 mir,
46                 borrow_components,
47                 borrow_kind,
48                 access_components,
49                 access
50             )
51         })
52     })
53 }
54
55 fn place_components_conflict<'gcx, 'tcx>(
56     tcx: TyCtxt<'_, 'gcx, 'tcx>,
57     mir: &Mir<'tcx>,
58     mut borrow_components: PlaceComponentsIter<'_, 'tcx>,
59     borrow_kind: BorrowKind,
60     mut access_components: PlaceComponentsIter<'_, 'tcx>,
61     access: AccessDepth,
62 ) -> bool {
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`.
67     //
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.
70     //
71     // For example, if we are comparing these:
72     // BORROW:  (*x1[2].y).z.a
73     // ACCESS:  (*x1[i].y).w.b
74     //
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!
81     //
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.
84     //
85     // BORROW:  (*x1[2].y).z.a
86     // ACCESS:  x1[i].y
87     //
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
92     //
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:
96     //
97     //       x1[2].y    | (*x1[i].y)    -- a deref! the access can't get past this, so we
98     //                                     are disjoint
99     //
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.
104     loop {
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);
108
109             if let Some(access_c) = access_components.next() {
110                 debug!("borrow_conflicts_with_place: access_c = {:?}", access_c);
111
112                 // Borrow and access path both have more components.
113                 //
114                 // Examples:
115                 //
116                 // - borrow of `a.(...)`, access to `a.(...)`
117                 // - borrow of `a.(...)`, access to `b.(...)`
118                 //
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.
128                         //
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).
133                         //
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");
140                         return true;
141                     }
142                     Overlap::EqualOrDisjoint => {
143                         // This is the recursive case - proceed to the next element.
144                     }
145                     Overlap::Disjoint => {
146                         // We have proven the borrow disjoint - further
147                         // projections will remain disjoint.
148                         debug!("borrow_conflicts_with_place: disjoint");
149                         return false;
150                     }
151                 }
152             } else {
153                 // Borrow path is longer than the access path. Examples:
154                 //
155                 // - borrow of `a.b.c`, access to `a.b`
156                 //
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.
160
161                 let (base, elem) = match borrow_c {
162                     Place::Projection(box Projection { base, elem }) => (base, elem),
163                     _ => bug!("place has no base?"),
164                 };
165                 let base_ty = base.ty(mir, tcx).to_ty(tcx);
166
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
176                         // currently.)
177                         //
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");
181                         return false;
182                     }
183
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
187                         // the pointer.
188                         debug!("borrow_conflicts_with_place: shallow access behind ptr");
189                         return false;
190                     }
191                     (ProjectionElem::Deref, ty::Ref(_, _, hir::MutImmutable), _) => {
192                         // Shouldn't be tracked
193                         bug!("Tracking borrow behind shared reference.");
194                     }
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");
199                         return false;
200                     }
201
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) {
206                             return true;
207                         }
208                     }
209
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`.
221                     }
222                 }
223             }
224         } else {
225             // Borrow path ran out but access path may not
226             // have. Examples:
227             //
228             // - borrow of `a.b`, access to `a.b.c`
229             // - borrow of `a.b`, access to `a.b`
230             //
231             // In the first example, where we didn't run out of
232             // access, the borrow can access all of our place, so we
233             // have a conflict.
234             //
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");
240                 return false;
241             } else {
242                 debug!("borrow_conflicts_with_place: full borrow, CONFLICT");
243                 return true;
244             }
245         }
246     }
247 }
248
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
252 /// `unroll_place`.
253 ///
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>>,
260 }
261
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
267     /// yield up:
268     ///
269     /// ```notrust
270     /// Some(`a`), Some(`a.b`), Some(`a.b.c`), None, None, ...
271     /// ```
272     fn iter(&self) -> PlaceComponentsIter<'_, 'tcx> {
273         PlaceComponentsIter { value: Some(self) }
274     }
275 }
276
277 /// Iterator over components; see `PlaceComponents::iter` for more
278 /// information.
279 ///
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>>,
285 }
286
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 {
290             self.value = next;
291             Some(component)
292         } else {
293             None
294         }
295     }
296 }
297
298 /// Recursively "unroll" a place into a `PlaceComponents` list,
299 /// invoking `op` with a `PlaceComponentsIter`.
300 fn unroll_place<'tcx, R>(
301     place: &Place<'tcx>,
302     next: Option<&PlaceComponents<'_, 'tcx>>,
303     op: impl FnOnce(PlaceComponentsIter<'_, 'tcx>) -> R,
304 ) -> R {
305     match place {
306         Place::Projection(interior) => unroll_place(
307             &interior.base,
308             Some(&PlaceComponents {
309                 component: place,
310                 next,
311             }),
312             op,
313         ),
314
315         Place::Promoted(_) |
316         Place::Local(_) | Place::Static(_) => {
317             let list = PlaceComponents {
318                 component: place,
319                 next,
320             };
321             op(list.iter())
322         }
323     }
324 }
325
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>,
331     mir: &Mir<'tcx>,
332     elem1: &Place<'tcx>,
333     elem2: &Place<'tcx>,
334 ) -> Overlap {
335     match (elem1, elem2) {
336         (Place::Local(l1), Place::Local(l2)) => {
337             if l1 == l2 {
338                 // the same local - base case, equal
339                 debug!("place_element_conflict: DISJOINT-OR-EQ-LOCAL");
340                 Overlap::EqualOrDisjoint
341             } else {
342                 // different locals - base case, disjoint
343                 debug!("place_element_conflict: DISJOINT-LOCAL");
344                 Overlap::Disjoint
345             }
346         }
347         (Place::Static(static1), Place::Static(static2)) => {
348             if static1.def_id != static2.def_id {
349                 debug!("place_element_conflict: DISJOINT-STATIC");
350                 Overlap::Disjoint
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");
354                 Overlap::Disjoint
355             } else {
356                 debug!("place_element_conflict: DISJOINT-OR-EQ-STATIC");
357                 Overlap::EqualOrDisjoint
358             }
359         }
360         (Place::Promoted(p1), Place::Promoted(p2)) => {
361             if p1.0 == p2.0 {
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;
367                     }
368                 }
369                 // the same promoted - base case, equal
370                 debug!("place_element_conflict: DISJOINT-OR-EQ-PROMOTED");
371                 Overlap::EqualOrDisjoint
372             } else {
373                 // different promoteds - base case, disjoint
374                 debug!("place_element_conflict: DISJOINT-PROMOTED");
375                 Overlap::Disjoint
376             }
377         }
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");
382             Overlap::Disjoint
383         }
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
390                 }
391                 (ProjectionElem::Field(f1, _), ProjectionElem::Field(f2, _)) => {
392                     if f1 == f2 {
393                         // same field (e.g. `a.y` vs. `a.y`) - recur.
394                         debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD");
395                         Overlap::EqualOrDisjoint
396                     } else {
397                         let ty = pi1.base.ty(mir, tcx).to_ty(tcx);
398                         match ty.sty {
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");
402                                 Overlap::Arbitrary
403                             }
404                             _ => {
405                                 // Different fields of a struct (`a.x` vs. `a.y`). Disjoint!
406                                 debug!("place_element_conflict: DISJOINT-FIELD");
407                                 Overlap::Disjoint
408                             }
409                         }
410                     }
411                 }
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.
417                     //
418                     // Note that this is different from unions - we *do* allow
419                     // this code to compile:
420                     //
421                     // ```
422                     // fn foo(x: &mut Result<i32, i32>) {
423                     //     let mut v = None;
424                     //     if let Ok(ref mut a) = *x {
425                     //         v = Some(a);
426                     //     }
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 {
432                     //         v = Some(a);
433                     //     }
434                     //     drop(v);
435                     // }
436                     // ```
437                     if v1 == v2 {
438                         debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD");
439                         Overlap::EqualOrDisjoint
440                     } else {
441                         debug!("place_element_conflict: DISJOINT-FIELD");
442                         Overlap::Disjoint
443                     }
444                 }
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
455                 }
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                     elem1,
529                     elem2
530                 ),
531             }
532         }
533         (Place::Projection(_), _) | (_, Place::Projection(_)) => bug!(
534             "unexpected elements in place_element_conflict: {:?} and {:?}",
535             elem1,
536             elem2
537         ),
538     }
539 }