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