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