1 use crate::borrow_set::{BorrowData, BorrowSet, TwoPhaseActivation};
2 use crate::places_conflict;
3 use crate::AccessDepth;
4 use crate::BorrowIndex;
5 use crate::Upvar;
6 use rustc_data_structures::graph::dominators::Dominators;
7 use rustc_middle::mir::BorrowKind;
8 use rustc_middle::mir::{BasicBlock, Body, Field, Location, Place, PlaceRef, ProjectionElem};
9 use rustc_middle::ty::TyCtxt;
10 
11 /// Returns `true` if the borrow represented by `kind` is
12 /// allowed to be split into separate Reservation and
13 /// Activation phases.
allow_two_phase_borrow(kind: BorrowKind) -> bool14 pub(super) fn allow_two_phase_borrow(kind: BorrowKind) -> bool {
15     kind.allows_two_phase_borrow()
16 }
17 
18 /// Control for the path borrow checking code
19 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
20 pub(super) enum Control {
21     Continue,
22     Break,
23 }
24 
25 /// Encapsulates the idea of iterating over every borrow that involves a particular path
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>,26 pub(super) fn each_borrow_involving_path<'tcx, F, I, S>(
27     s: &mut S,
28     tcx: TyCtxt<'tcx>,
29     body: &Body<'tcx>,
30     _location: Location,
31     access_place: (AccessDepth, Place<'tcx>),
32     borrow_set: &BorrowSet<'tcx>,
33     candidates: I,
34     mut op: F,
35 ) where
36     F: FnMut(&mut S, BorrowIndex, &BorrowData<'tcx>) -> Control,
37     I: Iterator<Item = BorrowIndex>,
38 {
39     let (access, place) = access_place;
40 
41     // FIXME: analogous code in check_loans first maps `place` to
42     // its base_path.
43 
44     // check for loan restricting path P being used. Accounts for
45     // borrows of P, P.a.b, etc.
46     for i in candidates {
47         let borrowed = &borrow_set[i];
48 
49         if places_conflict::borrow_conflicts_with_place(
50             tcx,
51             body,
52             borrowed.borrowed_place,
53             borrowed.kind,
54             place.as_ref(),
55             access,
56             places_conflict::PlaceConflictBias::Overlap,
57         ) {
58             debug!(
59                 "each_borrow_involving_path: {:?} @ {:?} vs. {:?}/{:?}",
60                 i, borrowed, place, access
61             );
62             let ctrl = op(s, i, borrowed);
63             if ctrl == Control::Break {
64                 return;
65             }
66         }
67     }
68 }
69 
is_active<'tcx>( dominators: &Dominators<BasicBlock>, borrow_data: &BorrowData<'tcx>, location: Location, ) -> bool70 pub(super) fn is_active<'tcx>(
71     dominators: &Dominators<BasicBlock>,
72     borrow_data: &BorrowData<'tcx>,
73     location: Location,
74 ) -> bool {
75     debug!("is_active(borrow_data={:?}, location={:?})", borrow_data, location);
76 
77     let activation_location = match borrow_data.activation_location {
78         // If this is not a 2-phase borrow, it is always active.
79         TwoPhaseActivation::NotTwoPhase => return true,
80         // And if the unique 2-phase use is not an activation, then it is *never* active.
81         TwoPhaseActivation::NotActivated => return false,
82         // Otherwise, we derive info from the activation point `loc`:
83         TwoPhaseActivation::ActivatedAt(loc) => loc,
84     };
85 
86     // Otherwise, it is active for every location *except* in between
87     // the reservation and the activation:
88     //
89     //       X
90     //      /
91     //     R      <--+ Except for this
92     //    / \        | diamond
93     //    \ /        |
94     //     A  <------+
95     //     |
96     //     Z
97     //
98     // Note that we assume that:
99     // - the reservation R dominates the activation A
100     // - the activation A post-dominates the reservation R (ignoring unwinding edges).
101     //
102     // This means that there can't be an edge that leaves A and
103     // comes back into that diamond unless it passes through R.
104     //
105     // Suboptimal: In some cases, this code walks the dominator
106     // tree twice when it only has to be walked once. I am
107     // lazy. -nmatsakis
108 
109     // If dominated by the activation A, then it is active. The
110     // activation occurs upon entering the point A, so this is
111     // also true if location == activation_location.
112     if activation_location.dominates(location, dominators) {
113         return true;
114     }
115 
116     // The reservation starts *on exiting* the reservation block,
117     // so check if the location is dominated by R.successor. If so,
118     // this point falls in between the reservation and location.
119     let reserve_location = borrow_data.reserve_location.successor_within_block();
120     if reserve_location.dominates(location, dominators) {
121         false
122     } else {
123         // Otherwise, this point is outside the diamond, so
124         // consider the borrow active. This could happen for
125         // example if the borrow remains active around a loop (in
126         // which case it would be active also for the point R,
127         // which would generate an error).
128         true
129     }
130 }
131 
132 /// Determines if a given borrow is borrowing local data
133 /// This is called for all Yield expressions on movable generators
borrow_of_local_data(place: Place<'_>) -> bool134 pub(super) fn borrow_of_local_data(place: Place<'_>) -> bool {
135     // Reborrow of already borrowed data is ignored
136     // Any errors will be caught on the initial borrow
137     !place.is_indirect()
138 }
139 
140 /// If `place` is a field projection, and the field is being projected from a closure type,
141 /// then returns the index of the field being projected. Note that this closure will always
142 /// be `self` in the current MIR, because that is the only time we directly access the fields
143 /// of a closure type.
is_upvar_field_projection( tcx: TyCtxt<'tcx>, upvars: &[Upvar<'tcx>], place_ref: PlaceRef<'tcx>, body: &Body<'tcx>, ) -> Option<Field>144 pub(crate) fn is_upvar_field_projection(
145     tcx: TyCtxt<'tcx>,
146     upvars: &[Upvar<'tcx>],
147     place_ref: PlaceRef<'tcx>,
148     body: &Body<'tcx>,
149 ) -> Option<Field> {
150     let mut place_ref = place_ref;
151     let mut by_ref = false;
152 
153     if let Some((place_base, ProjectionElem::Deref)) = place_ref.last_projection() {
154         place_ref = place_base;
155         by_ref = true;
156     }
157 
158     match place_ref.last_projection() {
159         Some((place_base, ProjectionElem::Field(field, _ty))) => {
160             let base_ty = place_base.ty(body, tcx).ty;
161             if (base_ty.is_closure() || base_ty.is_generator())
162                 && (!by_ref || upvars[field.index()].by_ref)
163             {
164                 Some(field)
165             } else {
166                 None
167             }
168         }
169         _ => None,
170     }
171 }
172