summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_borrowck/src/path_utils.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_borrowck/src/path_utils.rs')
-rw-r--r--compiler/rustc_borrowck/src/path_utils.rs171
1 files changed, 171 insertions, 0 deletions
diff --git a/compiler/rustc_borrowck/src/path_utils.rs b/compiler/rustc_borrowck/src/path_utils.rs
new file mode 100644
index 000000000..b2c8dfc82
--- /dev/null
+++ b/compiler/rustc_borrowck/src/path_utils.rs
@@ -0,0 +1,171 @@
+use crate::borrow_set::{BorrowData, BorrowSet, TwoPhaseActivation};
+use crate::places_conflict;
+use crate::AccessDepth;
+use crate::BorrowIndex;
+use crate::Upvar;
+use rustc_data_structures::graph::dominators::Dominators;
+use rustc_middle::mir::BorrowKind;
+use rustc_middle::mir::{BasicBlock, Body, Field, Location, Place, PlaceRef, ProjectionElem};
+use rustc_middle::ty::TyCtxt;
+
+/// Returns `true` if the borrow represented by `kind` is
+/// allowed to be split into separate Reservation and
+/// Activation phases.
+pub(super) fn allow_two_phase_borrow(kind: BorrowKind) -> bool {
+ kind.allows_two_phase_borrow()
+}
+
+/// Control for the path borrow checking code
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub(super) enum Control {
+ Continue,
+ Break,
+}
+
+/// Encapsulates the idea of iterating over every borrow that involves a particular path
+pub(super) fn each_borrow_involving_path<'tcx, F, I, S>(
+ s: &mut S,
+ tcx: TyCtxt<'tcx>,
+ body: &Body<'tcx>,
+ _location: Location,
+ access_place: (AccessDepth, Place<'tcx>),
+ borrow_set: &BorrowSet<'tcx>,
+ candidates: I,
+ mut op: F,
+) where
+ F: FnMut(&mut S, BorrowIndex, &BorrowData<'tcx>) -> Control,
+ I: Iterator<Item = BorrowIndex>,
+{
+ let (access, place) = access_place;
+
+ // FIXME: analogous code in check_loans first maps `place` to
+ // its base_path.
+
+ // check for loan restricting path P being used. Accounts for
+ // borrows of P, P.a.b, etc.
+ for i in candidates {
+ let borrowed = &borrow_set[i];
+
+ if places_conflict::borrow_conflicts_with_place(
+ tcx,
+ body,
+ borrowed.borrowed_place,
+ borrowed.kind,
+ place.as_ref(),
+ access,
+ places_conflict::PlaceConflictBias::Overlap,
+ ) {
+ debug!(
+ "each_borrow_involving_path: {:?} @ {:?} vs. {:?}/{:?}",
+ i, borrowed, place, access
+ );
+ let ctrl = op(s, i, borrowed);
+ if ctrl == Control::Break {
+ return;
+ }
+ }
+ }
+}
+
+pub(super) fn is_active<'tcx>(
+ dominators: &Dominators<BasicBlock>,
+ borrow_data: &BorrowData<'tcx>,
+ location: Location,
+) -> bool {
+ debug!("is_active(borrow_data={:?}, location={:?})", borrow_data, location);
+
+ let activation_location = match borrow_data.activation_location {
+ // If this is not a 2-phase borrow, it is always active.
+ TwoPhaseActivation::NotTwoPhase => return true,
+ // And if the unique 2-phase use is not an activation, then it is *never* active.
+ TwoPhaseActivation::NotActivated => return false,
+ // Otherwise, we derive info from the activation point `loc`:
+ TwoPhaseActivation::ActivatedAt(loc) => loc,
+ };
+
+ // Otherwise, it is active for every location *except* in between
+ // the reservation and the activation:
+ //
+ // X
+ // /
+ // R <--+ Except for this
+ // / \ | diamond
+ // \ / |
+ // A <------+
+ // |
+ // Z
+ //
+ // Note that we assume that:
+ // - the reservation R dominates the activation A
+ // - the activation A post-dominates the reservation R (ignoring unwinding edges).
+ //
+ // This means that there can't be an edge that leaves A and
+ // comes back into that diamond unless it passes through R.
+ //
+ // Suboptimal: In some cases, this code walks the dominator
+ // tree twice when it only has to be walked once. I am
+ // lazy. -nmatsakis
+
+ // If dominated by the activation A, then it is active. The
+ // activation occurs upon entering the point A, so this is
+ // also true if location == activation_location.
+ if activation_location.dominates(location, dominators) {
+ return true;
+ }
+
+ // The reservation starts *on exiting* the reservation block,
+ // so check if the location is dominated by R.successor. If so,
+ // this point falls in between the reservation and location.
+ let reserve_location = borrow_data.reserve_location.successor_within_block();
+ if reserve_location.dominates(location, dominators) {
+ false
+ } else {
+ // Otherwise, this point is outside the diamond, so
+ // consider the borrow active. This could happen for
+ // example if the borrow remains active around a loop (in
+ // which case it would be active also for the point R,
+ // which would generate an error).
+ true
+ }
+}
+
+/// Determines if a given borrow is borrowing local data
+/// This is called for all Yield expressions on movable generators
+pub(super) fn borrow_of_local_data(place: Place<'_>) -> bool {
+ // Reborrow of already borrowed data is ignored
+ // Any errors will be caught on the initial borrow
+ !place.is_indirect()
+}
+
+/// If `place` is a field projection, and the field is being projected from a closure type,
+/// then returns the index of the field being projected. Note that this closure will always
+/// be `self` in the current MIR, because that is the only time we directly access the fields
+/// of a closure type.
+pub(crate) fn is_upvar_field_projection<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ upvars: &[Upvar<'tcx>],
+ place_ref: PlaceRef<'tcx>,
+ body: &Body<'tcx>,
+) -> Option<Field> {
+ let mut place_ref = place_ref;
+ let mut by_ref = false;
+
+ if let Some((place_base, ProjectionElem::Deref)) = place_ref.last_projection() {
+ place_ref = place_base;
+ by_ref = true;
+ }
+
+ match place_ref.last_projection() {
+ Some((place_base, ProjectionElem::Field(field, _ty))) => {
+ let base_ty = place_base.ty(body, tcx).ty;
+ if (base_ty.is_closure() || base_ty.is_generator())
+ && (!by_ref || upvars[field.index()].by_ref)
+ {
+ Some(field)
+ } else {
+ None
+ }
+ }
+ _ => None,
+ }
+}