]>
Commit | Line | Data |
---|---|---|
dfeec247 | 1 | use crate::borrow_check::borrow_set::{BorrowData, BorrowSet, TwoPhaseActivation}; |
9fa01778 | 2 | use crate::borrow_check::places_conflict; |
9fa01778 | 3 | use crate::borrow_check::AccessDepth; |
f035d41b | 4 | use crate::borrow_check::Upvar; |
9fa01778 | 5 | use crate::dataflow::indexes::BorrowIndex; |
8faf50e0 | 6 | use rustc_data_structures::graph::dominators::Dominators; |
ba9703b0 | 7 | use rustc_middle::mir::BorrowKind; |
f035d41b | 8 | use rustc_middle::mir::{BasicBlock, Body, Field, Location, Place, PlaceRef, ProjectionElem}; |
ba9703b0 | 9 | use rustc_middle::ty::TyCtxt; |
94b46f34 | 10 | |
9fa01778 | 11 | /// Returns `true` if the borrow represented by `kind` is |
8faf50e0 XL |
12 | /// allowed to be split into separate Reservation and |
13 | /// Activation phases. | |
dc9dc135 | 14 | pub(super) fn allow_two_phase_borrow(kind: BorrowKind) -> bool { |
48663c56 | 15 | kind.allows_two_phase_borrow() |
94b46f34 XL |
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 | |
dc9dc135 | 26 | pub(super) fn each_borrow_involving_path<'tcx, F, I, S>( |
94b46f34 | 27 | s: &mut S, |
dc9dc135 XL |
28 | tcx: TyCtxt<'tcx>, |
29 | body: &Body<'tcx>, | |
48663c56 | 30 | _location: Location, |
ba9703b0 | 31 | access_place: (AccessDepth, Place<'tcx>), |
94b46f34 XL |
32 | borrow_set: &BorrowSet<'tcx>, |
33 | candidates: I, | |
34 | mut op: F, | |
35 | ) where | |
36 | F: FnMut(&mut S, BorrowIndex, &BorrowData<'tcx>) -> Control, | |
dc9dc135 | 37 | I: Iterator<Item = BorrowIndex>, |
94b46f34 XL |
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 | ||
0bf4aa26 XL |
49 | if places_conflict::borrow_conflicts_with_place( |
50 | tcx, | |
dc9dc135 | 51 | body, |
ba9703b0 | 52 | borrowed.borrowed_place, |
0bf4aa26 | 53 | borrowed.kind, |
416331ca | 54 | place.as_ref(), |
0bf4aa26 | 55 | access, |
0731742a | 56 | places_conflict::PlaceConflictBias::Overlap, |
0bf4aa26 | 57 | ) { |
94b46f34 XL |
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 | ||
94b46f34 XL |
70 | pub(super) fn is_active<'tcx>( |
71 | dominators: &Dominators<BasicBlock>, | |
72 | borrow_data: &BorrowData<'tcx>, | |
dfeec247 | 73 | location: Location, |
94b46f34 XL |
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. | |
8faf50e0 | 79 | TwoPhaseActivation::NotTwoPhase => return true, |
94b46f34 | 80 | // And if the unique 2-phase use is not an activation, then it is *never* active. |
8faf50e0 XL |
81 | TwoPhaseActivation::NotActivated => return false, |
82 | // Otherwise, we derive info from the activation point `loc`: | |
83 | TwoPhaseActivation::ActivatedAt(loc) => loc, | |
94b46f34 XL |
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 | |
dfeec247 | 133 | /// This is called for all Yield expressions on movable generators |
ba9703b0 | 134 | pub(super) fn borrow_of_local_data(place: Place<'_>) -> bool { |
dfeec247 XL |
135 | // Reborrow of already borrowed data is ignored |
136 | // Any errors will be caught on the initial borrow | |
137 | !place.is_indirect() | |
94b46f34 | 138 | } |
f035d41b XL |
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. | |
144 | pub(crate) fn is_upvar_field_projection( | |
145 | tcx: TyCtxt<'tcx>, | |
146 | upvars: &[Upvar], | |
147 | place_ref: PlaceRef<'tcx>, | |
148 | body: &Body<'tcx>, | |
149 | ) -> Option<Field> { | |
150 | let mut place_projection = place_ref.projection; | |
151 | let mut by_ref = false; | |
152 | ||
153 | if let [proj_base @ .., ProjectionElem::Deref] = place_projection { | |
154 | place_projection = proj_base; | |
155 | by_ref = true; | |
156 | } | |
157 | ||
158 | match place_projection { | |
159 | [base @ .., ProjectionElem::Field(field, _ty)] => { | |
160 | let base_ty = Place::ty_from(place_ref.local, base, body, tcx).ty; | |
161 | ||
162 | if (base_ty.is_closure() || base_ty.is_generator()) | |
163 | && (!by_ref || upvars[field.index()].by_ref) | |
164 | { | |
165 | Some(*field) | |
166 | } else { | |
167 | None | |
168 | } | |
169 | } | |
170 | ||
171 | _ => None, | |
172 | } | |
173 | } |