Skip to content

Commit

Permalink
Kill borrows from a projection after assignment.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
davidtwco committed Dec 17, 2018
1 parent 1149c58 commit db635fc
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 81 deletions.
5 changes: 3 additions & 2 deletions src/librustc_mir/borrow_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ mod move_errors;
mod mutability_errors;
mod path_utils;
crate mod place_ext;
mod places_conflict;
crate mod places_conflict;
mod prefixes;
mod used_muts;

Expand Down Expand Up @@ -1370,7 +1370,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
place,
borrow.kind,
root_place,
sd
sd,
places_conflict::PlaceConflictBias::Overlap,
) {
debug!("check_for_invalidation_at_exit({:?}): INVALID", place);
// FIXME: should be talking about the region lifetime instead
Expand Down
1 change: 1 addition & 0 deletions src/librustc_mir/borrow_check/path_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ pub(super) fn each_borrow_involving_path<'a, 'tcx, 'gcx: 'tcx, F, I, S> (
borrowed.kind,
place,
access,
places_conflict::PlaceConflictBias::Overlap,
) {
debug!(
"each_borrow_involving_path: {:?} @ {:?} vs. {:?}/{:?}",
Expand Down
69 changes: 60 additions & 9 deletions src/librustc_mir/borrow_check/places_conflict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,55 @@ use rustc::mir::{Projection, ProjectionElem};
use rustc::ty::{self, TyCtxt};
use std::cmp::max;

/// When checking if a place conflicts with another place, this enum is used to influence decisions
/// where a place might be equal or disjoint with another place, such as if `a[i] == a[j]`.
/// `PlaceConflictBias::Overlap` would bias toward assuming that `i` might equal `j` and that these
/// places overlap. `PlaceConflictBias::NoOverlap` assumes that for the purposes of the predicate
/// being run in the calling context, the conservative choice is to assume the compared indices
/// are disjoint (and therefore, do not overlap).
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
crate enum PlaceConflictBias {
Overlap,
NoOverlap,
}

/// Helper function for checking if places conflict with a mutable borrow and deep access depth.
/// This is used to check for places conflicting outside of the borrow checking code (such as in
/// dataflow).
crate fn places_conflict<'gcx, 'tcx>(
tcx: TyCtxt<'_, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
borrow_place: &Place<'tcx>,
access_place: &Place<'tcx>,
bias: PlaceConflictBias,
) -> bool {
borrow_conflicts_with_place(
tcx,
mir,
borrow_place,
BorrowKind::Mut { allow_two_phase_borrow: true },
access_place,
AccessDepth::Deep,
bias,
)
}

/// Checks whether the `borrow_place` conflicts with the `access_place` given a borrow kind and
/// access depth. The `bias` parameter is used to determine how the unknowable (comparing runtime
/// array indices, for example) should be interpreted - this depends on what the caller wants in
/// order to make the conservative choice and preserve soundness.
pub(super) fn borrow_conflicts_with_place<'gcx, 'tcx>(
tcx: TyCtxt<'_, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
borrow_place: &Place<'tcx>,
borrow_kind: BorrowKind,
access_place: &Place<'tcx>,
access: AccessDepth,
bias: PlaceConflictBias,
) -> bool {
debug!(
"borrow_conflicts_with_place({:?},{:?},{:?})",
borrow_place, access_place, access
"borrow_conflicts_with_place({:?}, {:?}, {:?}, {:?})",
borrow_place, access_place, access, bias,
);

// This Local/Local case is handled by the more general code below, but
Expand All @@ -46,7 +84,8 @@ pub(super) fn borrow_conflicts_with_place<'gcx, 'tcx>(
borrow_components,
borrow_kind,
access_components,
access
access,
bias,
)
})
})
Expand All @@ -59,6 +98,7 @@ fn place_components_conflict<'gcx, 'tcx>(
borrow_kind: BorrowKind,
mut access_components: PlaceComponentsIter<'_, 'tcx>,
access: AccessDepth,
bias: PlaceConflictBias,
) -> bool {
// The borrowck rules for proving disjointness are applied from the "root" of the
// borrow forwards, iterating over "similar" projections in lockstep until
Expand Down Expand Up @@ -121,7 +161,7 @@ fn place_components_conflict<'gcx, 'tcx>(
// check whether the components being borrowed vs
// accessed are disjoint (as in the second example,
// but not the first).
match place_element_conflict(tcx, mir, borrow_c, access_c) {
match place_element_conflict(tcx, mir, borrow_c, access_c, bias) {
Overlap::Arbitrary => {
// We have encountered different fields of potentially
// the same union - the borrow now partially overlaps.
Expand Down Expand Up @@ -193,7 +233,7 @@ fn place_components_conflict<'gcx, 'tcx>(
bug!("Tracking borrow behind shared reference.");
}
(ProjectionElem::Deref, ty::Ref(_, _, hir::MutMutable), AccessDepth::Drop) => {
// Values behind a mutatble reference are not access either by Dropping a
// Values behind a mutable reference are not access either by dropping a
// value, or by StorageDead
debug!("borrow_conflicts_with_place: drop access behind ptr");
return false;
Expand Down Expand Up @@ -331,6 +371,7 @@ fn place_element_conflict<'a, 'gcx: 'tcx, 'tcx>(
mir: &Mir<'tcx>,
elem1: &Place<'tcx>,
elem2: &Place<'tcx>,
bias: PlaceConflictBias,
) -> Overlap {
match (elem1, elem2) {
(Place::Local(l1), Place::Local(l2)) => {
Expand Down Expand Up @@ -448,10 +489,20 @@ fn place_element_conflict<'a, 'gcx: 'tcx, 'tcx>(
| (ProjectionElem::ConstantIndex { .. }, ProjectionElem::Index(..))
| (ProjectionElem::Subslice { .. }, ProjectionElem::Index(..)) => {
// Array indexes (`a[0]` vs. `a[i]`). These can either be disjoint
// (if the indexes differ) or equal (if they are the same), so this
// is the recursive case that gives "equal *or* disjoint" its meaning.
debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-INDEX");
Overlap::EqualOrDisjoint
// (if the indexes differ) or equal (if they are the same).
match bias {
PlaceConflictBias::Overlap => {
// If we are biased towards overlapping, then this is the recursive
// case that gives "equal *or* disjoint" its meaning.
debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-INDEX");
Overlap::EqualOrDisjoint
}
PlaceConflictBias::NoOverlap => {
// If we are biased towards no overlapping, then this is disjoint.
debug!("place_element_conflict: DISJOINT-ARRAY-INDEX");
Overlap::Disjoint
}
}
}
(ProjectionElem::ConstantIndex { offset: o1, min_length: _, from_end: false },
ProjectionElem::ConstantIndex { offset: o2, min_length: _, from_end: false })
Expand Down
77 changes: 54 additions & 23 deletions src/librustc_mir/dataflow/impls/borrows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
use borrow_check::borrow_set::{BorrowSet, BorrowData};
use borrow_check::place_ext::PlaceExt;

use rustc;
use rustc::mir::{self, Location, Place, Mir};
use rustc::ty::TyCtxt;
use rustc::ty::RegionVid;
Expand All @@ -24,6 +23,7 @@ use dataflow::{BitDenotation, BlockSets, InitialFlow};
pub use dataflow::indexes::BorrowIndex;
use borrow_check::nll::region_infer::RegionInferenceContext;
use borrow_check::nll::ToRegionVid;
use borrow_check::places_conflict;

use std::rc::Rc;

Expand Down Expand Up @@ -191,12 +191,54 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
}
}

fn kill_borrows_on_local(&self,
sets: &mut BlockSets<BorrowIndex>,
local: &rustc::mir::Local)
{
if let Some(borrow_indexes) = self.borrow_set.local_map.get(local) {
sets.kill_all(borrow_indexes);
/// Kill any borrows that conflict with `place`.
fn kill_borrows_on_place(
&self,
sets: &mut BlockSets<BorrowIndex>,
location: Location,
place: &Place<'tcx>
) {
debug!("kill_borrows_on_place: location={:?} place={:?}", location, place);
// Handle the `Place::Local(..)` case first and exit early.
if let Place::Local(local) = place {
if let Some(borrow_indexes) = self.borrow_set.local_map.get(&local) {
debug!(
"kill_borrows_on_place: local={:?} borrow_indexes={:?}",
local, borrow_indexes,
);
sets.kill_all(borrow_indexes);
return;
}
}

// Otherwise, look at all borrows that are live and if they conflict with the assignment
// into our place then we can kill them.
let mut borrows = sets.on_entry.clone();
let _ = borrows.union(sets.gen_set);
for borrow_index in borrows.iter() {
let borrow_data = &self.borrows()[borrow_index];
debug!(
"kill_borrows_on_place: borrow_index={:?} borrow_data={:?}",
borrow_index, borrow_data,
);

// By passing `PlaceConflictBias::NoOverlap`, we conservatively assume that any given
// pair of array indices are unequal, so that when `places_conflict` returns true, we
// will be assured that two places being compared definitely denotes the same sets of
// locations.
if places_conflict::places_conflict(
self.tcx,
self.mir,
place,
&borrow_data.borrowed_place,
places_conflict::PlaceConflictBias::NoOverlap,
) {
debug!(
"kill_borrows_on_place: (kill) place={:?} borrow_index={:?} borrow_data={:?}",
place, borrow_index, borrow_data,
);
sets.kill(borrow_index);
}
}
}
}
Expand All @@ -222,7 +264,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
}

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

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

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

// NOTE: if/when the Assign case is revised to inspect
// the assigned_place here, make sure to also
// re-consider the current implementations of the
// propagate_call_return method.

if let mir::Rvalue::Ref(_, _, ref place) = **rhs {
if place.ignore_borrow(
self.tcx,
Expand Down Expand Up @@ -279,19 +317,13 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
mir::StatementKind::StorageDead(local) => {
// Make sure there are no remaining borrows for locals that
// are gone out of scope.
self.kill_borrows_on_local(sets, &local)
self.kill_borrows_on_place(sets, location, &Place::Local(local));
}

mir::StatementKind::InlineAsm { ref outputs, ref asm, .. } => {
for (output, kind) in outputs.iter().zip(&asm.outputs) {
if !kind.is_indirect && !kind.is_rw {
// Make sure there are no remaining borrows for direct
// output variables.
if let Place::Local(ref local) = *output {
// FIXME: Handle the case in which we're assigning over
// a projection (`foo.bar`).
self.kill_borrows_on_local(sets, local);
}
self.kill_borrows_on_place(sets, location, output);
}
}
}
Expand Down Expand Up @@ -342,4 +374,3 @@ impl<'a, 'gcx, 'tcx> InitialFlow for Borrows<'a, 'gcx, 'tcx> {
false // bottom = nothing is reserved or activated yet
}
}

1 change: 0 additions & 1 deletion src/test/ui/nll/issue-46589.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ impl Foo {
};

let c = other;
//~^ ERROR cannot move out of `other` because it is borrowed [E0505]
}
}

Expand Down
17 changes: 2 additions & 15 deletions src/test/ui/nll/issue-46589.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,6 @@ LL | None => (*other).new_self()
| second mutable borrow occurs here
| first borrow later used here

error[E0505]: cannot move out of `other` because it is borrowed
--> $DIR/issue-46589.rs:33:17
|
LL | *other = match (*other).get_self() {
| -------- borrow of `**other` occurs here
...
LL | let c = other;
| ^^^^^
| |
| move out of `other` occurs here
| borrow later used here

error: aborting due to 2 previous errors
error: aborting due to previous error

Some errors occurred: E0499, E0505.
For more information about an error, try `rustc --explain E0499`.
For more information about this error, try `rustc --explain E0499`.
2 changes: 0 additions & 2 deletions src/test/ui/nll/loan_ends_mid_block_pair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,8 @@ fn nll_fail() {
//~| ERROR (Mir) [E0506]
data.0 = 'f';
//~^ ERROR (Ast) [E0506]
//~| ERROR (Mir) [E0506]
data.0 = 'g';
//~^ ERROR (Ast) [E0506]
//~| ERROR (Mir) [E0506]
capitalize(c);
}

Expand Down
34 changes: 5 additions & 29 deletions src/test/ui/nll/loan_ends_mid_block_pair.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ LL | data.0 = 'f';
| ^^^^^^^^^^^^ assignment to borrowed `data.0` occurs here

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

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

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

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

error[E0506]: cannot assign to `data.0` because it is borrowed (Mir)
--> $DIR/loan_ends_mid_block_pair.rs:28:5
|
LL | let c = &mut data.0;
| ----------- borrow of `data.0` occurs here
...
LL | data.0 = 'f';
| ^^^^^^^^^^^^ assignment to borrowed `data.0` occurs here
...
LL | capitalize(c);
| - borrow later used here

error[E0506]: cannot assign to `data.0` because it is borrowed (Mir)
--> $DIR/loan_ends_mid_block_pair.rs:31:5
|
LL | let c = &mut data.0;
| ----------- borrow of `data.0` occurs here
...
LL | data.0 = 'g';
| ^^^^^^^^^^^^ assignment to borrowed `data.0` occurs here
...
LL | capitalize(c);
| - borrow later used here

error: aborting due to 9 previous errors
error: aborting due to 7 previous errors

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

0 comments on commit db635fc

Please sign in to comment.