Skip to content

Commit db635fc

Browse files
committed
Kill borrows from a projection after assignment.
This commit extends previous work to kill borrows from a local after assignment into that local to kill borrows from a projection after assignment into a prefix of that place.
1 parent 1149c58 commit db635fc

File tree

8 files changed

+125
-81
lines changed

8 files changed

+125
-81
lines changed

src/librustc_mir/borrow_check/mod.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ mod move_errors;
6363
mod mutability_errors;
6464
mod path_utils;
6565
crate mod place_ext;
66-
mod places_conflict;
66+
crate mod places_conflict;
6767
mod prefixes;
6868
mod used_muts;
6969

@@ -1370,7 +1370,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
13701370
place,
13711371
borrow.kind,
13721372
root_place,
1373-
sd
1373+
sd,
1374+
places_conflict::PlaceConflictBias::Overlap,
13741375
) {
13751376
debug!("check_for_invalidation_at_exit({:?}): INVALID", place);
13761377
// FIXME: should be talking about the region lifetime instead

src/librustc_mir/borrow_check/path_utils.rs

+1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ pub(super) fn each_borrow_involving_path<'a, 'tcx, 'gcx: 'tcx, F, I, S> (
6868
borrowed.kind,
6969
place,
7070
access,
71+
places_conflict::PlaceConflictBias::Overlap,
7172
) {
7273
debug!(
7374
"each_borrow_involving_path: {:?} @ {:?} vs. {:?}/{:?}",

src/librustc_mir/borrow_check/places_conflict.rs

+60-9
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,55 @@ use rustc::mir::{Projection, ProjectionElem};
1717
use rustc::ty::{self, TyCtxt};
1818
use std::cmp::max;
1919

20+
/// When checking if a place conflicts with another place, this enum is used to influence decisions
21+
/// where a place might be equal or disjoint with another place, such as if `a[i] == a[j]`.
22+
/// `PlaceConflictBias::Overlap` would bias toward assuming that `i` might equal `j` and that these
23+
/// places overlap. `PlaceConflictBias::NoOverlap` assumes that for the purposes of the predicate
24+
/// being run in the calling context, the conservative choice is to assume the compared indices
25+
/// are disjoint (and therefore, do not overlap).
26+
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
27+
crate enum PlaceConflictBias {
28+
Overlap,
29+
NoOverlap,
30+
}
31+
32+
/// Helper function for checking if places conflict with a mutable borrow and deep access depth.
33+
/// This is used to check for places conflicting outside of the borrow checking code (such as in
34+
/// dataflow).
35+
crate fn places_conflict<'gcx, 'tcx>(
36+
tcx: TyCtxt<'_, 'gcx, 'tcx>,
37+
mir: &Mir<'tcx>,
38+
borrow_place: &Place<'tcx>,
39+
access_place: &Place<'tcx>,
40+
bias: PlaceConflictBias,
41+
) -> bool {
42+
borrow_conflicts_with_place(
43+
tcx,
44+
mir,
45+
borrow_place,
46+
BorrowKind::Mut { allow_two_phase_borrow: true },
47+
access_place,
48+
AccessDepth::Deep,
49+
bias,
50+
)
51+
}
52+
53+
/// Checks whether the `borrow_place` conflicts with the `access_place` given a borrow kind and
54+
/// access depth. The `bias` parameter is used to determine how the unknowable (comparing runtime
55+
/// array indices, for example) should be interpreted - this depends on what the caller wants in
56+
/// order to make the conservative choice and preserve soundness.
2057
pub(super) fn borrow_conflicts_with_place<'gcx, 'tcx>(
2158
tcx: TyCtxt<'_, 'gcx, 'tcx>,
2259
mir: &Mir<'tcx>,
2360
borrow_place: &Place<'tcx>,
2461
borrow_kind: BorrowKind,
2562
access_place: &Place<'tcx>,
2663
access: AccessDepth,
64+
bias: PlaceConflictBias,
2765
) -> bool {
2866
debug!(
29-
"borrow_conflicts_with_place({:?},{:?},{:?})",
30-
borrow_place, access_place, access
67+
"borrow_conflicts_with_place({:?}, {:?}, {:?}, {:?})",
68+
borrow_place, access_place, access, bias,
3169
);
3270

3371
// This Local/Local case is handled by the more general code below, but
@@ -46,7 +84,8 @@ pub(super) fn borrow_conflicts_with_place<'gcx, 'tcx>(
4684
borrow_components,
4785
borrow_kind,
4886
access_components,
49-
access
87+
access,
88+
bias,
5089
)
5190
})
5291
})
@@ -59,6 +98,7 @@ fn place_components_conflict<'gcx, 'tcx>(
5998
borrow_kind: BorrowKind,
6099
mut access_components: PlaceComponentsIter<'_, 'tcx>,
61100
access: AccessDepth,
101+
bias: PlaceConflictBias,
62102
) -> bool {
63103
// The borrowck rules for proving disjointness are applied from the "root" of the
64104
// borrow forwards, iterating over "similar" projections in lockstep until
@@ -121,7 +161,7 @@ fn place_components_conflict<'gcx, 'tcx>(
121161
// check whether the components being borrowed vs
122162
// accessed are disjoint (as in the second example,
123163
// but not the first).
124-
match place_element_conflict(tcx, mir, borrow_c, access_c) {
164+
match place_element_conflict(tcx, mir, borrow_c, access_c, bias) {
125165
Overlap::Arbitrary => {
126166
// We have encountered different fields of potentially
127167
// the same union - the borrow now partially overlaps.
@@ -193,7 +233,7 @@ fn place_components_conflict<'gcx, 'tcx>(
193233
bug!("Tracking borrow behind shared reference.");
194234
}
195235
(ProjectionElem::Deref, ty::Ref(_, _, hir::MutMutable), AccessDepth::Drop) => {
196-
// Values behind a mutatble reference are not access either by Dropping a
236+
// Values behind a mutable reference are not access either by dropping a
197237
// value, or by StorageDead
198238
debug!("borrow_conflicts_with_place: drop access behind ptr");
199239
return false;
@@ -331,6 +371,7 @@ fn place_element_conflict<'a, 'gcx: 'tcx, 'tcx>(
331371
mir: &Mir<'tcx>,
332372
elem1: &Place<'tcx>,
333373
elem2: &Place<'tcx>,
374+
bias: PlaceConflictBias,
334375
) -> Overlap {
335376
match (elem1, elem2) {
336377
(Place::Local(l1), Place::Local(l2)) => {
@@ -448,10 +489,20 @@ fn place_element_conflict<'a, 'gcx: 'tcx, 'tcx>(
448489
| (ProjectionElem::ConstantIndex { .. }, ProjectionElem::Index(..))
449490
| (ProjectionElem::Subslice { .. }, ProjectionElem::Index(..)) => {
450491
// 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
492+
// (if the indexes differ) or equal (if they are the same).
493+
match bias {
494+
PlaceConflictBias::Overlap => {
495+
// If we are biased towards overlapping, then this is the recursive
496+
// case that gives "equal *or* disjoint" its meaning.
497+
debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-INDEX");
498+
Overlap::EqualOrDisjoint
499+
}
500+
PlaceConflictBias::NoOverlap => {
501+
// If we are biased towards no overlapping, then this is disjoint.
502+
debug!("place_element_conflict: DISJOINT-ARRAY-INDEX");
503+
Overlap::Disjoint
504+
}
505+
}
455506
}
456507
(ProjectionElem::ConstantIndex { offset: o1, min_length: _, from_end: false },
457508
ProjectionElem::ConstantIndex { offset: o2, min_length: _, from_end: false })

src/librustc_mir/dataflow/impls/borrows.rs

+54-23
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
use borrow_check::borrow_set::{BorrowSet, BorrowData};
1212
use borrow_check::place_ext::PlaceExt;
1313

14-
use rustc;
1514
use rustc::mir::{self, Location, Place, Mir};
1615
use rustc::ty::TyCtxt;
1716
use rustc::ty::RegionVid;
@@ -24,6 +23,7 @@ use dataflow::{BitDenotation, BlockSets, InitialFlow};
2423
pub use dataflow::indexes::BorrowIndex;
2524
use borrow_check::nll::region_infer::RegionInferenceContext;
2625
use borrow_check::nll::ToRegionVid;
26+
use borrow_check::places_conflict;
2727

2828
use std::rc::Rc;
2929

@@ -191,12 +191,54 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
191191
}
192192
}
193193

194-
fn kill_borrows_on_local(&self,
195-
sets: &mut BlockSets<BorrowIndex>,
196-
local: &rustc::mir::Local)
197-
{
198-
if let Some(borrow_indexes) = self.borrow_set.local_map.get(local) {
199-
sets.kill_all(borrow_indexes);
194+
/// Kill any borrows that conflict with `place`.
195+
fn kill_borrows_on_place(
196+
&self,
197+
sets: &mut BlockSets<BorrowIndex>,
198+
location: Location,
199+
place: &Place<'tcx>
200+
) {
201+
debug!("kill_borrows_on_place: location={:?} place={:?}", location, place);
202+
// Handle the `Place::Local(..)` case first and exit early.
203+
if let Place::Local(local) = place {
204+
if let Some(borrow_indexes) = self.borrow_set.local_map.get(&local) {
205+
debug!(
206+
"kill_borrows_on_place: local={:?} borrow_indexes={:?}",
207+
local, borrow_indexes,
208+
);
209+
sets.kill_all(borrow_indexes);
210+
return;
211+
}
212+
}
213+
214+
// Otherwise, look at all borrows that are live and if they conflict with the assignment
215+
// into our place then we can kill them.
216+
let mut borrows = sets.on_entry.clone();
217+
let _ = borrows.union(sets.gen_set);
218+
for borrow_index in borrows.iter() {
219+
let borrow_data = &self.borrows()[borrow_index];
220+
debug!(
221+
"kill_borrows_on_place: borrow_index={:?} borrow_data={:?}",
222+
borrow_index, borrow_data,
223+
);
224+
225+
// By passing `PlaceConflictBias::NoOverlap`, we conservatively assume that any given
226+
// pair of array indices are unequal, so that when `places_conflict` returns true, we
227+
// will be assured that two places being compared definitely denotes the same sets of
228+
// locations.
229+
if places_conflict::places_conflict(
230+
self.tcx,
231+
self.mir,
232+
place,
233+
&borrow_data.borrowed_place,
234+
places_conflict::PlaceConflictBias::NoOverlap,
235+
) {
236+
debug!(
237+
"kill_borrows_on_place: (kill) place={:?} borrow_index={:?} borrow_data={:?}",
238+
place, borrow_index, borrow_data,
239+
);
240+
sets.kill(borrow_index);
241+
}
200242
}
201243
}
202244
}
@@ -222,7 +264,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
222264
}
223265

224266
fn statement_effect(&self, sets: &mut BlockSets<BorrowIndex>, location: Location) {
225-
debug!("Borrows::statement_effect sets: {:?} location: {:?}", sets, location);
267+
debug!("Borrows::statement_effect: sets={:?} location={:?}", sets, location);
226268

227269
let block = &self.mir.basic_blocks().get(location.block).unwrap_or_else(|| {
228270
panic!("could not find block at location {:?}", location);
@@ -231,21 +273,17 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
231273
panic!("could not find statement at location {:?}");
232274
});
233275

276+
debug!("Borrows::statement_effect: stmt={:?}", stmt);
234277
match stmt.kind {
235278
mir::StatementKind::Assign(ref lhs, ref rhs) => {
236279
// Make sure there are no remaining borrows for variables
237280
// that are assigned over.
238-
if let Place::Local(ref local) = *lhs {
239-
// FIXME: Handle the case in which we're assigning over
240-
// a projection (`foo.bar`).
241-
self.kill_borrows_on_local(sets, local);
242-
}
281+
self.kill_borrows_on_place(sets, location, lhs);
243282

244283
// NOTE: if/when the Assign case is revised to inspect
245284
// the assigned_place here, make sure to also
246285
// re-consider the current implementations of the
247286
// propagate_call_return method.
248-
249287
if let mir::Rvalue::Ref(_, _, ref place) = **rhs {
250288
if place.ignore_borrow(
251289
self.tcx,
@@ -279,19 +317,13 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
279317
mir::StatementKind::StorageDead(local) => {
280318
// Make sure there are no remaining borrows for locals that
281319
// are gone out of scope.
282-
self.kill_borrows_on_local(sets, &local)
320+
self.kill_borrows_on_place(sets, location, &Place::Local(local));
283321
}
284322

285323
mir::StatementKind::InlineAsm { ref outputs, ref asm, .. } => {
286324
for (output, kind) in outputs.iter().zip(&asm.outputs) {
287325
if !kind.is_indirect && !kind.is_rw {
288-
// Make sure there are no remaining borrows for direct
289-
// output variables.
290-
if let Place::Local(ref local) = *output {
291-
// FIXME: Handle the case in which we're assigning over
292-
// a projection (`foo.bar`).
293-
self.kill_borrows_on_local(sets, local);
294-
}
326+
self.kill_borrows_on_place(sets, location, output);
295327
}
296328
}
297329
}
@@ -342,4 +374,3 @@ impl<'a, 'gcx, 'tcx> InitialFlow for Borrows<'a, 'gcx, 'tcx> {
342374
false // bottom = nothing is reserved or activated yet
343375
}
344376
}
345-

src/test/ui/nll/issue-46589.rs

-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ impl Foo {
3131
};
3232

3333
let c = other;
34-
//~^ ERROR cannot move out of `other` because it is borrowed [E0505]
3534
}
3635
}
3736

src/test/ui/nll/issue-46589.stderr

+2-15
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,6 @@ LL | None => (*other).new_self()
1010
| second mutable borrow occurs here
1111
| first borrow later used here
1212

13-
error[E0505]: cannot move out of `other` because it is borrowed
14-
--> $DIR/issue-46589.rs:33:17
15-
|
16-
LL | *other = match (*other).get_self() {
17-
| -------- borrow of `**other` occurs here
18-
...
19-
LL | let c = other;
20-
| ^^^^^
21-
| |
22-
| move out of `other` occurs here
23-
| borrow later used here
24-
25-
error: aborting due to 2 previous errors
13+
error: aborting due to previous error
2614

27-
Some errors occurred: E0499, E0505.
28-
For more information about an error, try `rustc --explain E0499`.
15+
For more information about this error, try `rustc --explain E0499`.

src/test/ui/nll/loan_ends_mid_block_pair.rs

-2
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,8 @@ fn nll_fail() {
2727
//~| ERROR (Mir) [E0506]
2828
data.0 = 'f';
2929
//~^ ERROR (Ast) [E0506]
30-
//~| ERROR (Mir) [E0506]
3130
data.0 = 'g';
3231
//~^ ERROR (Ast) [E0506]
33-
//~| ERROR (Mir) [E0506]
3432
capitalize(c);
3533
}
3634

src/test/ui/nll/loan_ends_mid_block_pair.stderr

+5-29
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ LL | data.0 = 'f';
1717
| ^^^^^^^^^^^^ assignment to borrowed `data.0` occurs here
1818

1919
error[E0506]: cannot assign to `data.0` because it is borrowed (Ast)
20-
--> $DIR/loan_ends_mid_block_pair.rs:31:5
20+
--> $DIR/loan_ends_mid_block_pair.rs:30:5
2121
|
2222
LL | let c = &mut data.0;
2323
| ------ borrow of `data.0` occurs here
@@ -26,7 +26,7 @@ LL | data.0 = 'g';
2626
| ^^^^^^^^^^^^ assignment to borrowed `data.0` occurs here
2727

2828
error[E0506]: cannot assign to `data.0` because it is borrowed (Ast)
29-
--> $DIR/loan_ends_mid_block_pair.rs:41:5
29+
--> $DIR/loan_ends_mid_block_pair.rs:39:5
3030
|
3131
LL | let c = &mut data.0;
3232
| ------ borrow of `data.0` occurs here
@@ -35,7 +35,7 @@ LL | data.0 = 'e';
3535
| ^^^^^^^^^^^^ assignment to borrowed `data.0` occurs here
3636

3737
error[E0506]: cannot assign to `data.0` because it is borrowed (Ast)
38-
--> $DIR/loan_ends_mid_block_pair.rs:43:5
38+
--> $DIR/loan_ends_mid_block_pair.rs:41:5
3939
|
4040
LL | let c = &mut data.0;
4141
| ------ borrow of `data.0` occurs here
@@ -44,7 +44,7 @@ LL | data.0 = 'f';
4444
| ^^^^^^^^^^^^ assignment to borrowed `data.0` occurs here
4545

4646
error[E0506]: cannot assign to `data.0` because it is borrowed (Ast)
47-
--> $DIR/loan_ends_mid_block_pair.rs:45:5
47+
--> $DIR/loan_ends_mid_block_pair.rs:43:5
4848
|
4949
LL | let c = &mut data.0;
5050
| ------ borrow of `data.0` occurs here
@@ -64,30 +64,6 @@ LL | data.0 = 'e';
6464
LL | capitalize(c);
6565
| - borrow later used here
6666

67-
error[E0506]: cannot assign to `data.0` because it is borrowed (Mir)
68-
--> $DIR/loan_ends_mid_block_pair.rs:28:5
69-
|
70-
LL | let c = &mut data.0;
71-
| ----------- borrow of `data.0` occurs here
72-
...
73-
LL | data.0 = 'f';
74-
| ^^^^^^^^^^^^ assignment to borrowed `data.0` occurs here
75-
...
76-
LL | capitalize(c);
77-
| - borrow later used here
78-
79-
error[E0506]: cannot assign to `data.0` because it is borrowed (Mir)
80-
--> $DIR/loan_ends_mid_block_pair.rs:31:5
81-
|
82-
LL | let c = &mut data.0;
83-
| ----------- borrow of `data.0` occurs here
84-
...
85-
LL | data.0 = 'g';
86-
| ^^^^^^^^^^^^ assignment to borrowed `data.0` occurs here
87-
...
88-
LL | capitalize(c);
89-
| - borrow later used here
90-
91-
error: aborting due to 9 previous errors
67+
error: aborting due to 7 previous errors
9268

9369
For more information about this error, try `rustc --explain E0506`.

0 commit comments

Comments
 (0)