rustc_borrowck/places_conflict.rs
1//! The borrowck rules for proving disjointness are applied from the "root" of the
2//! borrow forwards, iterating over "similar" projections in lockstep until
3//! we can prove overlap one way or another. Essentially, we treat `Overlap` as
4//! a monoid and report a conflict if the product ends up not being `Disjoint`.
5//!
6//! On each step, if we didn't run out of borrow or place, we know that our elements
7//! have the same type, and that they only overlap if they are the identical.
8//!
9//! For example, if we are comparing these:
10//! ```text
11//! BORROW: (*x1[2].y).z.a
12//! ACCESS: (*x1[i].y).w.b
13//! ```
14//!
15//! Then our steps are:
16//! ```text
17//! x1 | x1 -- places are the same
18//! x1[2] | x1[i] -- equal or disjoint (disjoint if indexes differ)
19//! x1[2].y | x1[i].y -- equal or disjoint
20//! *x1[2].y | *x1[i].y -- equal or disjoint
21//! (*x1[2].y).z | (*x1[i].y).w -- we are disjoint and don't need to check more!
22//! ```
23//!
24//! Because `zip` does potentially bad things to the iterator inside, this loop
25//! also handles the case where the access might be a *prefix* of the borrow, e.g.
26//!
27//! ```text
28//! BORROW: (*x1[2].y).z.a
29//! ACCESS: x1[i].y
30//! ```
31//!
32//! Then our steps are:
33//! ```text
34//! x1 | x1 -- places are the same
35//! x1[2] | x1[i] -- equal or disjoint (disjoint if indexes differ)
36//! x1[2].y | x1[i].y -- equal or disjoint
37//! ```
38//!
39//! -- here we run out of access - the borrow can access a part of it. If this
40//! is a full deep access, then we *know* the borrow conflicts with it. However,
41//! if the access is shallow, then we can proceed:
42//!
43//! ```text
44//! x1[2].y | (*x1[i].y) -- a deref! the access can't get past this, so we
45//! are disjoint
46//! ```
47//!
48//! Our invariant is, that at each step of the iteration:
49//! - If we didn't run out of access to match, our borrow and access are comparable
50//! and either equal or disjoint.
51//! - If we did run out of access, the borrow can access a part of it.
52
53use std::cmp::max;
54use std::iter;
55
56use rustc_hir as hir;
57use rustc_middle::bug;
58use rustc_middle::mir::{
59 Body, BorrowKind, FakeBorrowKind, MutBorrowKind, Place, PlaceElem, PlaceRef, ProjectionElem,
60};
61use rustc_middle::ty::{self, TyCtxt};
62use tracing::{debug, instrument};
63
64use crate::{AccessDepth, ArtificialField, Deep, Overlap, Shallow};
65
66/// When checking if a place conflicts with another place, this enum is used to influence decisions
67/// where a place might be equal or disjoint with another place, such as if `a[i] == a[j]`.
68/// `PlaceConflictBias::Overlap` would bias toward assuming that `i` might equal `j` and that these
69/// places overlap. `PlaceConflictBias::NoOverlap` assumes that for the purposes of the predicate
70/// being run in the calling context, the conservative choice is to assume the compared indices
71/// are disjoint (and therefore, do not overlap).
72#[derive(Copy, Clone, Debug, Eq, PartialEq)]
73pub enum PlaceConflictBias {
74 Overlap,
75 NoOverlap,
76}
77
78/// Helper function for checking if places conflict with a mutable borrow and deep access depth.
79/// This is used to check for places conflicting outside of the borrow checking code (such as in
80/// dataflow).
81pub fn places_conflict<'tcx>(
82 tcx: TyCtxt<'tcx>,
83 body: &Body<'tcx>,
84 borrow_place: Place<'tcx>,
85 access_place: Place<'tcx>,
86 bias: PlaceConflictBias,
87) -> bool {
88 borrow_conflicts_with_place(
89 tcx,
90 body,
91 borrow_place,
92 BorrowKind::Mut { kind: MutBorrowKind::TwoPhaseBorrow },
93 access_place.as_ref(),
94 AccessDepth::Deep,
95 bias,
96 )
97}
98
99/// Checks whether the `borrow_place` conflicts with the `access_place` given a borrow kind and
100/// access depth. The `bias` parameter is used to determine how the unknowable (comparing runtime
101/// array indices, for example) should be interpreted - this depends on what the caller wants in
102/// order to make the conservative choice and preserve soundness.
103#[inline]
104pub(super) fn borrow_conflicts_with_place<'tcx>(
105 tcx: TyCtxt<'tcx>,
106 body: &Body<'tcx>,
107 borrow_place: Place<'tcx>,
108 borrow_kind: BorrowKind,
109 access_place: PlaceRef<'tcx>,
110 access: AccessDepth,
111 bias: PlaceConflictBias,
112) -> bool {
113 let borrow_local = borrow_place.local;
114 let access_local = access_place.local;
115
116 if borrow_local != access_local {
117 // We have proven the borrow disjoint - further projections will remain disjoint.
118 return false;
119 }
120
121 // This Local/Local case is handled by the more general code below, but
122 // it's so common that it's a speed win to check for it first.
123 if borrow_place.projection.is_empty() && access_place.projection.is_empty() {
124 return true;
125 }
126
127 place_components_conflict(tcx, body, borrow_place, borrow_kind, access_place, access, bias)
128}
129
130#[instrument(level = "debug", skip(tcx, body))]
131fn place_components_conflict<'tcx>(
132 tcx: TyCtxt<'tcx>,
133 body: &Body<'tcx>,
134 borrow_place: Place<'tcx>,
135 borrow_kind: BorrowKind,
136 access_place: PlaceRef<'tcx>,
137 access: AccessDepth,
138 bias: PlaceConflictBias,
139) -> bool {
140 let borrow_local = borrow_place.local;
141 let access_local = access_place.local;
142 // borrow_conflicts_with_place should have checked that.
143 assert_eq!(borrow_local, access_local);
144
145 // loop invariant: borrow_c is always either equal to access_c or disjoint from it.
146 for ((borrow_place, borrow_c), &access_c) in
147 iter::zip(borrow_place.iter_projections(), access_place.projection)
148 {
149 debug!(?borrow_c, ?access_c);
150
151 // Borrow and access path both have more components.
152 //
153 // Examples:
154 //
155 // - borrow of `a.(...)`, access to `a.(...)`
156 // - borrow of `a.(...)`, access to `b.(...)`
157 //
158 // Here we only see the components we have checked so
159 // far (in our examples, just the first component). We
160 // check whether the components being borrowed vs
161 // accessed are disjoint (as in the second example,
162 // but not the first).
163 match place_projection_conflict(tcx, body, borrow_place, borrow_c, access_c, bias) {
164 Overlap::Arbitrary => {
165 // We have encountered different fields of potentially
166 // the same union - the borrow now partially overlaps.
167 //
168 // There is no *easy* way of comparing the fields
169 // further on, because they might have different types
170 // (e.g., borrows of `u.a.0` and `u.b.y` where `.0` and
171 // `.y` come from different structs).
172 //
173 // We could try to do some things here - e.g., count
174 // dereferences - but that's probably not a good
175 // idea, at least for now, so just give up and
176 // report a conflict. This is unsafe code anyway so
177 // the user could always use raw pointers.
178 debug!("arbitrary -> conflict");
179 return true;
180 }
181 Overlap::EqualOrDisjoint => {
182 // This is the recursive case - proceed to the next element.
183 }
184 Overlap::Disjoint => {
185 // We have proven the borrow disjoint - further
186 // projections will remain disjoint.
187 debug!("disjoint");
188 return false;
189 }
190 }
191 }
192
193 if borrow_place.projection.len() > access_place.projection.len() {
194 for (base, elem) in borrow_place.iter_projections().skip(access_place.projection.len()) {
195 // Borrow path is longer than the access path. Examples:
196 //
197 // - borrow of `a.b.c`, access to `a.b`
198 //
199 // Here, we know that the borrow can access a part of
200 // our place. This is a conflict if that is a part our
201 // access cares about.
202
203 let base_ty = base.ty(body, tcx).ty;
204
205 match (elem, base_ty.kind(), access) {
206 (_, _, Shallow(Some(ArtificialField::ArrayLength)))
207 | (_, _, Shallow(Some(ArtificialField::FakeBorrow))) => {
208 // The array length is like additional fields on the
209 // type; it does not overlap any existing data there.
210 // Furthermore, if cannot actually be a prefix of any
211 // borrowed place (at least in MIR as it is currently.)
212 //
213 // e.g., a (mutable) borrow of `a[5]` while we read the
214 // array length of `a`.
215 debug!("borrow_conflicts_with_place: implicit field");
216 return false;
217 }
218
219 (ProjectionElem::Deref, _, Shallow(None)) => {
220 // e.g., a borrow of `*x.y` while we shallowly access `x.y` or some
221 // prefix thereof - the shallow access can't touch anything behind
222 // the pointer.
223 debug!("borrow_conflicts_with_place: shallow access behind ptr");
224 return false;
225 }
226 (ProjectionElem::Deref, ty::Ref(_, _, hir::Mutability::Not), _) => {
227 // Shouldn't be tracked
228 bug!("Tracking borrow behind shared reference.");
229 }
230 (ProjectionElem::Deref, ty::Ref(_, _, hir::Mutability::Mut), AccessDepth::Drop) => {
231 // Values behind a mutable reference are not access either by dropping a
232 // value, or by StorageDead
233 debug!("borrow_conflicts_with_place: drop access behind ptr");
234 return false;
235 }
236
237 (ProjectionElem::Field { .. }, ty::Adt(def, _), AccessDepth::Drop) => {
238 // Drop can read/write arbitrary projections, so places
239 // conflict regardless of further projections.
240 if def.has_dtor(tcx) {
241 return true;
242 }
243 }
244
245 (ProjectionElem::Deref, _, Deep)
246 | (ProjectionElem::Deref, _, AccessDepth::Drop)
247 | (ProjectionElem::Field { .. }, _, _)
248 | (ProjectionElem::Index { .. }, _, _)
249 | (ProjectionElem::ConstantIndex { .. }, _, _)
250 | (ProjectionElem::Subslice { .. }, _, _)
251 | (ProjectionElem::OpaqueCast { .. }, _, _)
252 | (ProjectionElem::Downcast { .. }, _, _)
253 | (ProjectionElem::UnwrapUnsafeBinder(_), _, _) => {
254 // Recursive case. This can still be disjoint on a
255 // further iteration if this a shallow access and
256 // there's a deref later on, e.g., a borrow
257 // of `*x.y` while accessing `x`.
258 }
259 }
260 }
261 }
262
263 // Borrow path ran out but access path may not
264 // have. Examples:
265 //
266 // - borrow of `a.b`, access to `a.b.c`
267 // - borrow of `a.b`, access to `a.b`
268 //
269 // In the first example, where we didn't run out of
270 // access, the borrow can access all of our place, so we
271 // have a conflict.
272 //
273 // If the second example, where we did, then we still know
274 // that the borrow can access a *part* of our place that
275 // our access cares about, so we still have a conflict.
276 if borrow_kind == BorrowKind::Fake(FakeBorrowKind::Shallow)
277 && borrow_place.projection.len() < access_place.projection.len()
278 {
279 debug!("borrow_conflicts_with_place: shallow borrow");
280 false
281 } else {
282 debug!("borrow_conflicts_with_place: full borrow, CONFLICT");
283 true
284 }
285}
286
287// Given that the bases of `elem1` and `elem2` are always either equal
288// or disjoint (and have the same type!), return the overlap situation
289// between `elem1` and `elem2`.
290fn place_projection_conflict<'tcx>(
291 tcx: TyCtxt<'tcx>,
292 body: &Body<'tcx>,
293 pi1: PlaceRef<'tcx>,
294 pi1_elem: PlaceElem<'tcx>,
295 pi2_elem: PlaceElem<'tcx>,
296 bias: PlaceConflictBias,
297) -> Overlap {
298 match (pi1_elem, pi2_elem) {
299 (ProjectionElem::Deref, ProjectionElem::Deref) => {
300 // derefs (e.g., `*x` vs. `*x`) - recur.
301 debug!("place_element_conflict: DISJOINT-OR-EQ-DEREF");
302 Overlap::EqualOrDisjoint
303 }
304 (ProjectionElem::OpaqueCast(_), ProjectionElem::OpaqueCast(_)) => {
305 // casts to other types may always conflict irrespective of the type being cast to.
306 debug!("place_element_conflict: DISJOINT-OR-EQ-OPAQUE");
307 Overlap::EqualOrDisjoint
308 }
309 (ProjectionElem::Field(f1, _), ProjectionElem::Field(f2, _)) => {
310 if f1 == f2 {
311 // same field (e.g., `a.y` vs. `a.y`) - recur.
312 debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD");
313 Overlap::EqualOrDisjoint
314 } else {
315 let ty = pi1.ty(body, tcx).ty;
316 if ty.is_union() {
317 // Different fields of a union, we are basically stuck.
318 debug!("place_element_conflict: STUCK-UNION");
319 Overlap::Arbitrary
320 } else {
321 // Different fields of a struct (`a.x` vs. `a.y`). Disjoint!
322 debug!("place_element_conflict: DISJOINT-FIELD");
323 Overlap::Disjoint
324 }
325 }
326 }
327 (ProjectionElem::Downcast(_, v1), ProjectionElem::Downcast(_, v2)) => {
328 // different variants are treated as having disjoint fields,
329 // even if they occupy the same "space", because it's
330 // impossible for 2 variants of the same enum to exist
331 // (and therefore, to be borrowed) at the same time.
332 //
333 // Note that this is different from unions - we *do* allow
334 // this code to compile:
335 //
336 // ```
337 // fn foo(x: &mut Result<i32, i32>) {
338 // let mut v = None;
339 // if let Ok(ref mut a) = *x {
340 // v = Some(a);
341 // }
342 // // here, you would *think* that the
343 // // *entirety* of `x` would be borrowed,
344 // // but in fact only the `Ok` variant is,
345 // // so the `Err` variant is *entirely free*:
346 // if let Err(ref mut a) = *x {
347 // v = Some(a);
348 // }
349 // drop(v);
350 // }
351 // ```
352 if v1 == v2 {
353 debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD");
354 Overlap::EqualOrDisjoint
355 } else {
356 debug!("place_element_conflict: DISJOINT-FIELD");
357 Overlap::Disjoint
358 }
359 }
360 (
361 ProjectionElem::Index(..),
362 ProjectionElem::Index(..)
363 | ProjectionElem::ConstantIndex { .. }
364 | ProjectionElem::Subslice { .. },
365 )
366 | (
367 ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. },
368 ProjectionElem::Index(..),
369 ) => {
370 // Array indexes (`a[0]` vs. `a[i]`). These can either be disjoint
371 // (if the indexes differ) or equal (if they are the same).
372 match bias {
373 PlaceConflictBias::Overlap => {
374 // If we are biased towards overlapping, then this is the recursive
375 // case that gives "equal *or* disjoint" its meaning.
376 debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-INDEX");
377 Overlap::EqualOrDisjoint
378 }
379 PlaceConflictBias::NoOverlap => {
380 // If we are biased towards no overlapping, then this is disjoint.
381 debug!("place_element_conflict: DISJOINT-ARRAY-INDEX");
382 Overlap::Disjoint
383 }
384 }
385 }
386 (
387 ProjectionElem::ConstantIndex { offset: o1, min_length: _, from_end: false },
388 ProjectionElem::ConstantIndex { offset: o2, min_length: _, from_end: false },
389 )
390 | (
391 ProjectionElem::ConstantIndex { offset: o1, min_length: _, from_end: true },
392 ProjectionElem::ConstantIndex { offset: o2, min_length: _, from_end: true },
393 ) => {
394 if o1 == o2 {
395 debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX");
396 Overlap::EqualOrDisjoint
397 } else {
398 debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX");
399 Overlap::Disjoint
400 }
401 }
402 (
403 ProjectionElem::ConstantIndex {
404 offset: offset_from_begin,
405 min_length: min_length1,
406 from_end: false,
407 },
408 ProjectionElem::ConstantIndex {
409 offset: offset_from_end,
410 min_length: min_length2,
411 from_end: true,
412 },
413 )
414 | (
415 ProjectionElem::ConstantIndex {
416 offset: offset_from_end,
417 min_length: min_length1,
418 from_end: true,
419 },
420 ProjectionElem::ConstantIndex {
421 offset: offset_from_begin,
422 min_length: min_length2,
423 from_end: false,
424 },
425 ) => {
426 // both patterns matched so it must be at least the greater of the two
427 let min_length = max(min_length1, min_length2);
428 // `offset_from_end` can be in range `[1..min_length]`, 1 indicates the last
429 // element (like -1 in Python) and `min_length` the first.
430 // Therefore, `min_length - offset_from_end` gives the minimal possible
431 // offset from the beginning
432 if offset_from_begin >= min_length - offset_from_end {
433 debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-FE");
434 Overlap::EqualOrDisjoint
435 } else {
436 debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-FE");
437 Overlap::Disjoint
438 }
439 }
440 (
441 ProjectionElem::ConstantIndex { offset, min_length: _, from_end: false },
442 ProjectionElem::Subslice { from, to, from_end: false },
443 )
444 | (
445 ProjectionElem::Subslice { from, to, from_end: false },
446 ProjectionElem::ConstantIndex { offset, min_length: _, from_end: false },
447 ) => {
448 if (from..to).contains(&offset) {
449 debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-SUBSLICE");
450 Overlap::EqualOrDisjoint
451 } else {
452 debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-SUBSLICE");
453 Overlap::Disjoint
454 }
455 }
456 (
457 ProjectionElem::ConstantIndex { offset, min_length: _, from_end: false },
458 ProjectionElem::Subslice { from, .. },
459 )
460 | (
461 ProjectionElem::Subslice { from, .. },
462 ProjectionElem::ConstantIndex { offset, min_length: _, from_end: false },
463 ) => {
464 if offset >= from {
465 debug!("place_element_conflict: DISJOINT-OR-EQ-SLICE-CONSTANT-INDEX-SUBSLICE");
466 Overlap::EqualOrDisjoint
467 } else {
468 debug!("place_element_conflict: DISJOINT-SLICE-CONSTANT-INDEX-SUBSLICE");
469 Overlap::Disjoint
470 }
471 }
472 (
473 ProjectionElem::ConstantIndex { offset, min_length: _, from_end: true },
474 ProjectionElem::Subslice { to, from_end: true, .. },
475 )
476 | (
477 ProjectionElem::Subslice { to, from_end: true, .. },
478 ProjectionElem::ConstantIndex { offset, min_length: _, from_end: true },
479 ) => {
480 if offset > to {
481 debug!(
482 "place_element_conflict: \
483 DISJOINT-OR-EQ-SLICE-CONSTANT-INDEX-SUBSLICE-FE"
484 );
485 Overlap::EqualOrDisjoint
486 } else {
487 debug!("place_element_conflict: DISJOINT-SLICE-CONSTANT-INDEX-SUBSLICE-FE");
488 Overlap::Disjoint
489 }
490 }
491 (
492 ProjectionElem::Subslice { from: f1, to: t1, from_end: false },
493 ProjectionElem::Subslice { from: f2, to: t2, from_end: false },
494 ) => {
495 if f2 >= t1 || f1 >= t2 {
496 debug!("place_element_conflict: DISJOINT-ARRAY-SUBSLICES");
497 Overlap::Disjoint
498 } else {
499 debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-SUBSLICES");
500 Overlap::EqualOrDisjoint
501 }
502 }
503 (ProjectionElem::Subslice { .. }, ProjectionElem::Subslice { .. }) => {
504 debug!("place_element_conflict: DISJOINT-OR-EQ-SLICE-SUBSLICES");
505 Overlap::EqualOrDisjoint
506 }
507 (
508 ProjectionElem::Deref
509 | ProjectionElem::Field(..)
510 | ProjectionElem::Index(..)
511 | ProjectionElem::ConstantIndex { .. }
512 | ProjectionElem::OpaqueCast { .. }
513 | ProjectionElem::Subslice { .. }
514 | ProjectionElem::Downcast(..),
515 _,
516 ) => bug!(
517 "mismatched projections in place_element_conflict: {:?} and {:?}",
518 pi1_elem,
519 pi2_elem
520 ),
521
522 (ProjectionElem::UnwrapUnsafeBinder(_), _) => {
523 todo!()
524 }
525 }
526}