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