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);
/// 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
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,
Ref(region, borrow_kind, ref place) => {
let kind_str = match borrow_kind {
BorrowKind::Shared => "",
+ BorrowKind::Shallow => "shallow ",
BorrowKind::Mut { .. } | BorrowKind::Unique => "mut ",
};
// 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,
}
}
}
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 |
/// 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,
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 ",
};
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
}
_ => {
Origin::Mir,
),
+ (BorrowKind::Mut { .. }, _, _, BorrowKind::Shallow, _, _)
+ | (BorrowKind::Unique, _, _, BorrowKind::Shallow, _, _) => {
+ return;
+ }
+
(BorrowKind::Unique, _, _, _, _, _) => tcx.cannot_uniquely_borrow_by_one_closure(
span,
&desc_place,
enum ArtificialField {
Discriminant,
ArrayLength,
+ ShallowBorrow,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
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
}
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);
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.
// only mutable borrows should be 2-phase
assert!(match borrow.kind {
- BorrowKind::Shared => false,
+ BorrowKind::Shared | BorrowKind::Shallow => false,
BorrowKind::Unique | BorrowKind::Mut { .. } => true,
});
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) => {
| 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
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;
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);
// 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 { .. }) => {
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
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;
tcx: TyCtxt<'_, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
borrow_place: &Place<'tcx>,
+ borrow_kind: BorrowKind,
access_place: &Place<'tcx>,
access: AccessDepth,
) -> bool {
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
+ )
})
})
}
tcx: TyCtxt<'_, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
mut borrow_components: PlaceComponentsIter<'_, 'tcx>,
+ borrow_kind: BorrowKind,
mut access_components: PlaceComponentsIter<'_, 'tcx>,
access: AccessDepth,
) -> bool {
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,
// 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;
+ }
}
}
}
// 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,
},