1
Fork 0

Add "Shallow" borrow kind

This allows treating the "fake" match borrows differently from shared
borrows.
This commit is contained in:
Matthew Jasper 2018-09-10 22:33:45 +01:00
parent a072d1bca6
commit ced5c2d08a
11 changed files with 102 additions and 22 deletions

View file

@ -46,6 +46,7 @@ for mir::BorrowKind {
match *self {
mir::BorrowKind::Shared |
mir::BorrowKind::Shallow |
mir::BorrowKind::Unique => {}
mir::BorrowKind::Mut { allow_two_phase_borrow } => {
allow_two_phase_borrow.hash_stable(hcx, hasher);

View file

@ -456,6 +456,27 @@ pub enum BorrowKind {
/// Data must be immutable and is aliasable.
Shared,
/// The immediately borrowed place must be immutable, but projections from
/// it don't need to be. For example, a shallow borrow of `a.b` doesn't
/// conflict with a mutable borrow of `a.b.c`.
///
/// This is used when lowering matches: when matching on a place we want to
/// ensure that place have the same value from the start of the match until
/// an arm is selected. This prevents this code from compiling:
///
/// let mut x = &Some(0);
/// match *x {
/// None => (),
/// Some(_) if { x = &None; false } => (),
/// Some(_) => (),
/// }
///
/// This can't be a shared borrow because mutably borrowing (*x as Some).0
/// should not prevent `if let None = x { ... }`, for example, becase the
/// mutating `(*x as Some).0` can't affect the discriminant of `x`.
/// We can also report errors with this kind of borrow differently.
Shallow,
/// Data must be immutable but not aliasable. This kind of borrow
/// cannot currently be expressed by the user and is used only in
/// implicit closure bindings. It is needed when the closure is
@ -504,7 +525,7 @@ pub enum BorrowKind {
impl BorrowKind {
pub fn allows_two_phase_borrow(&self) -> bool {
match *self {
BorrowKind::Shared | BorrowKind::Unique => false,
BorrowKind::Shared | BorrowKind::Shallow | BorrowKind::Unique => false,
BorrowKind::Mut {
allow_two_phase_borrow,
} => allow_two_phase_borrow,
@ -2198,6 +2219,7 @@ impl<'tcx> Debug for Rvalue<'tcx> {
Ref(region, borrow_kind, ref place) => {
let kind_str = match borrow_kind {
BorrowKind::Shared => "",
BorrowKind::Shallow => "shallow ",
BorrowKind::Mut { .. } | BorrowKind::Unique => "mut ",
};

View file

@ -287,6 +287,10 @@ impl BorrowKind {
// use `&mut`. It gives all the capabilities of an `&uniq`
// and hence is a safe "over approximation".
BorrowKind::Unique => hir::MutMutable,
// We have no type corresponding to a shallow borrow, so use
// `&` as an approximation.
BorrowKind::Shallow => hir::MutImmutable,
}
}
}

View file

@ -963,6 +963,7 @@ impl<'tcx> PlaceContext<'tcx> {
PlaceContext::Inspect |
PlaceContext::Borrow { kind: BorrowKind::Shared, .. } |
PlaceContext::Borrow { kind: BorrowKind::Shallow, .. } |
PlaceContext::Borrow { kind: BorrowKind::Unique, .. } |
PlaceContext::Projection(Mutability::Not) |
PlaceContext::Copy | PlaceContext::Move |
@ -974,7 +975,9 @@ impl<'tcx> PlaceContext<'tcx> {
/// Returns true if this place context represents a use that does not change the value.
pub fn is_nonmutating_use(&self) -> bool {
match *self {
PlaceContext::Inspect | PlaceContext::Borrow { kind: BorrowKind::Shared, .. } |
PlaceContext::Inspect |
PlaceContext::Borrow { kind: BorrowKind::Shared, .. } |
PlaceContext::Borrow { kind: BorrowKind::Shallow, .. } |
PlaceContext::Borrow { kind: BorrowKind::Unique, .. } |
PlaceContext::Projection(Mutability::Not) |
PlaceContext::Copy | PlaceContext::Move => true,

View file

@ -87,6 +87,7 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
let kind = match self.kind {
mir::BorrowKind::Shared => "",
mir::BorrowKind::Shallow => "shallow ",
mir::BorrowKind::Unique => "uniq ",
mir::BorrowKind::Mut { .. } => "mut ",
};
@ -287,7 +288,8 @@ impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> {
borrow_data.activation_location = match context {
// The use of TMP in a shared borrow does not
// count as an actual activation.
PlaceContext::Borrow { kind: mir::BorrowKind::Shared, .. } => {
PlaceContext::Borrow { kind: mir::BorrowKind::Shared, .. }
| PlaceContext::Borrow { kind: mir::BorrowKind::Shallow, .. } => {
TwoPhaseActivation::NotActivated
}
_ => {

View file

@ -333,6 +333,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
Origin::Mir,
),
(BorrowKind::Mut { .. }, _, _, BorrowKind::Shallow, _, _)
| (BorrowKind::Unique, _, _, BorrowKind::Shallow, _, _) => {
return;
}
(BorrowKind::Unique, _, _, _, _, _) => tcx.cannot_uniquely_borrow_by_one_closure(
span,
&desc_place,

View file

@ -755,6 +755,7 @@ use self::AccessDepth::{Deep, Shallow};
enum ArtificialField {
Discriminant,
ArrayLength,
ShallowBorrow,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
@ -972,7 +973,13 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
Control::Continue
}
(Read(_), BorrowKind::Shared) | (Reservation(..), BorrowKind::Shared) => {
(Read(_), BorrowKind::Shared) | (Reservation(..), BorrowKind::Shared)
| (Read(_), BorrowKind::Shallow) | (Reservation(..), BorrowKind::Shallow) => {
Control::Continue
}
(Write(WriteKind::Move), BorrowKind::Shallow) => {
// Handled by initialization checks.
Control::Continue
}
@ -1108,6 +1115,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
match *rvalue {
Rvalue::Ref(_ /*rgn*/, bk, ref place) => {
let access_kind = match bk {
BorrowKind::Shallow => {
(Shallow(Some(ArtificialField::ShallowBorrow)), Read(ReadKind::Borrow(bk)))
},
BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))),
BorrowKind::Unique | BorrowKind::Mut { .. } => {
let wk = WriteKind::MutableBorrow(bk);
@ -1315,11 +1325,16 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
return;
}
// FIXME: replace this with a proper borrow_conflicts_with_place when
// that is merged.
let sd = if might_be_alive { Deep } else { Shallow(None) };
if places_conflict::places_conflict(self.infcx.tcx, self.mir, place, root_place, sd) {
if places_conflict::borrow_conflicts_with_place(
self.infcx.tcx,
self.mir,
place,
borrow.kind,
root_place,
sd
) {
debug!("check_for_invalidation_at_exit({:?}): INVALID", place);
// FIXME: should be talking about the region lifetime instead
// of just a span here.
@ -1369,7 +1384,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
// only mutable borrows should be 2-phase
assert!(match borrow.kind {
BorrowKind::Shared => false,
BorrowKind::Shared | BorrowKind::Shallow => false,
BorrowKind::Unique | BorrowKind::Mut { .. } => true,
});
@ -1669,7 +1684,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
let is_local_mutation_allowed = match borrow_kind {
BorrowKind::Unique => LocalMutationIsAllowed::Yes,
BorrowKind::Mut { .. } => is_local_mutation_allowed,
BorrowKind::Shared => unreachable!(),
BorrowKind::Shared | BorrowKind::Shallow => unreachable!(),
};
match self.is_mutable(place, is_local_mutation_allowed) {
Ok(root_place) => {
@ -1699,8 +1714,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
| Write(wk @ WriteKind::Move)
| Reservation(wk @ WriteKind::StorageDeadOrDrop)
| Reservation(wk @ WriteKind::MutableBorrow(BorrowKind::Shared))
| Reservation(wk @ WriteKind::MutableBorrow(BorrowKind::Shallow))
| Write(wk @ WriteKind::StorageDeadOrDrop)
| Write(wk @ WriteKind::MutableBorrow(BorrowKind::Shared)) => {
| Write(wk @ WriteKind::MutableBorrow(BorrowKind::Shared))
| Write(wk @ WriteKind::MutableBorrow(BorrowKind::Shallow)) => {
if let Err(_place_err) = self.is_mutable(place, is_local_mutation_allowed) {
if self.infcx.tcx.migrate_borrowck() {
// rust-lang/rust#46908: In pure NLL mode this
@ -1743,6 +1760,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
Read(ReadKind::Borrow(BorrowKind::Unique))
| Read(ReadKind::Borrow(BorrowKind::Mut { .. }))
| Read(ReadKind::Borrow(BorrowKind::Shared))
| Read(ReadKind::Borrow(BorrowKind::Shallow))
| Read(ReadKind::Copy) => {
// Access authorized
return false;

View file

@ -329,6 +329,9 @@ impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cx, 'tcx, 'gcx> {
match *rvalue {
Rvalue::Ref(_ /*rgn*/, bk, ref place) => {
let access_kind = match bk {
BorrowKind::Shallow => {
(Shallow(Some(ArtificialField::ShallowBorrow)), Read(ReadKind::Borrow(bk)))
},
BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))),
BorrowKind::Unique | BorrowKind::Mut { .. } => {
let wk = WriteKind::MutableBorrow(bk);
@ -439,8 +442,9 @@ impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cx, 'tcx, 'gcx> {
// have already taken the reservation
}
(Read(_), BorrowKind::Shared) | (Reservation(..), BorrowKind::Shared) => {
// Reads/reservations don't invalidate shared borrows
(Read(_), BorrowKind::Shallow) | (Reservation(..), BorrowKind::Shallow)
| (Read(_), BorrowKind::Shared) | (Reservation(..), BorrowKind::Shared) => {
// Reads/reservations don't invalidate shared or shallow borrows
}
(Read(_), BorrowKind::Unique) | (Read(_), BorrowKind::Mut { .. }) => {

View file

@ -61,7 +61,14 @@ pub(super) fn each_borrow_involving_path<'a, 'tcx, 'gcx: 'tcx, F, I, S> (
for i in candidates {
let borrowed = &borrow_set[i];
if places_conflict::places_conflict(tcx, mir, &borrowed.borrowed_place, place, access) {
if places_conflict::places_conflict(
tcx,
mir,
&borrowed.borrowed_place,
borrowed.kind,
place,
access,
) {
debug!(
"each_borrow_involving_path: {:?} @ {:?} vs. {:?}/{:?}",
i, borrowed, place, access

View file

@ -12,7 +12,7 @@ use borrow_check::ArtificialField;
use borrow_check::Overlap;
use borrow_check::{Deep, Shallow, AccessDepth};
use rustc::hir;
use rustc::mir::{Mir, Place};
use rustc::mir::{BorrowKind, Mir, Place};
use rustc::mir::{Projection, ProjectionElem};
use rustc::ty::{self, TyCtxt};
use std::cmp::max;
@ -21,6 +21,7 @@ pub(super) fn places_conflict<'gcx, 'tcx>(
tcx: TyCtxt<'_, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
borrow_place: &Place<'tcx>,
borrow_kind: BorrowKind,
access_place: &Place<'tcx>,
access: AccessDepth,
) -> bool {
@ -39,7 +40,14 @@ pub(super) fn places_conflict<'gcx, 'tcx>(
unroll_place(borrow_place, None, |borrow_components| {
unroll_place(access_place, None, |access_components| {
place_components_conflict(tcx, mir, borrow_components, access_components, access)
place_components_conflict(
tcx,
mir,
borrow_components,
borrow_kind,
access_components,
access
)
})
})
}
@ -48,6 +56,7 @@ fn place_components_conflict<'gcx, 'tcx>(
tcx: TyCtxt<'_, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
mut borrow_components: PlaceComponentsIter<'_, 'tcx>,
borrow_kind: BorrowKind,
mut access_components: PlaceComponentsIter<'_, 'tcx>,
access: AccessDepth,
) -> bool {
@ -157,7 +166,8 @@ fn place_components_conflict<'gcx, 'tcx>(
match (elem, &base_ty.sty, access) {
(_, _, Shallow(Some(ArtificialField::Discriminant)))
| (_, _, Shallow(Some(ArtificialField::ArrayLength))) => {
| (_, _, Shallow(Some(ArtificialField::ArrayLength)))
| (_, _, Shallow(Some(ArtificialField::ShallowBorrow))) => {
// The discriminant and array length are like
// additional fields on the type; they do not
// overlap any existing data there. Furthermore,
@ -225,11 +235,13 @@ fn place_components_conflict<'gcx, 'tcx>(
// If the second example, where we did, then we still know
// that the borrow can access a *part* of our place that
// our access cares about, so we still have a conflict.
//
// FIXME: Differs from AST-borrowck; includes drive-by fix
// to #38899. Will probably need back-compat mode flag.
debug!("places_conflict: full borrow, CONFLICT");
return true;
if borrow_kind == BorrowKind::Shallow && access_components.next().is_some() {
debug!("places_conflict: shallow borrow");
return false;
} else {
debug!("places_conflict: full borrow, CONFLICT");
return true;
}
}
}
}

View file

@ -1363,7 +1363,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// borrow of the whole match input. See additional
// discussion on rust-lang/rust#49870.
let borrow_kind = match borrow_kind {
BorrowKind::Shared | BorrowKind::Unique => borrow_kind,
BorrowKind::Shared
| BorrowKind::Shallow
| BorrowKind::Unique => borrow_kind,
BorrowKind::Mut { .. } => BorrowKind::Mut {
allow_two_phase_borrow: true,
},