]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_mir/src/borrow_check/path_utils.rs
New upstream version 1.48.0~beta.8+dfsg1
[rustc.git] / compiler / rustc_mir / src / borrow_check / path_utils.rs
1 use crate::borrow_check::borrow_set::{BorrowData, BorrowSet, TwoPhaseActivation};
2 use crate::borrow_check::places_conflict;
3 use crate::borrow_check::AccessDepth;
4 use crate::borrow_check::Upvar;
5 use crate::dataflow::indexes::BorrowIndex;
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.
14 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
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
70 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
134 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.
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 }