]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_borrowck/src/path_utils.rs
Rollup merge of #106640 - lcnr:update-test, r=jackh726
[rust.git] / compiler / rustc_borrowck / src / path_utils.rs
1 #![deny(rustc::untranslatable_diagnostic)]
2 #![deny(rustc::diagnostic_outside_of_impl)]
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;
8 use rustc_data_structures::graph::dominators::Dominators;
9 use rustc_middle::mir::BorrowKind;
10 use rustc_middle::mir::{BasicBlock, Body, Field, Location, Place, PlaceRef, ProjectionElem};
11 use rustc_middle::ty::TyCtxt;
12
13 /// Returns `true` if the borrow represented by `kind` is
14 /// allowed to be split into separate Reservation and
15 /// Activation phases.
16 pub(super) fn allow_two_phase_borrow(kind: BorrowKind) -> bool {
17     kind.allows_two_phase_borrow()
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
28 pub(super) fn each_borrow_involving_path<'tcx, F, I, S>(
29     s: &mut S,
30     tcx: TyCtxt<'tcx>,
31     body: &Body<'tcx>,
32     _location: Location,
33     access_place: (AccessDepth, Place<'tcx>),
34     borrow_set: &BorrowSet<'tcx>,
35     candidates: I,
36     mut op: F,
37 ) where
38     F: FnMut(&mut S, BorrowIndex, &BorrowData<'tcx>) -> Control,
39     I: Iterator<Item = BorrowIndex>,
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
51         if places_conflict::borrow_conflicts_with_place(
52             tcx,
53             body,
54             borrowed.borrowed_place,
55             borrowed.kind,
56             place.as_ref(),
57             access,
58             places_conflict::PlaceConflictBias::Overlap,
59         ) {
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
72 pub(super) fn is_active<'tcx>(
73     dominators: &Dominators<BasicBlock>,
74     borrow_data: &BorrowData<'tcx>,
75     location: Location,
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.
81         TwoPhaseActivation::NotTwoPhase => return true,
82         // And if the unique 2-phase use is not an activation, then it is *never* active.
83         TwoPhaseActivation::NotActivated => return false,
84         // Otherwise, we derive info from the activation point `loc`:
85         TwoPhaseActivation::ActivatedAt(loc) => loc,
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
135 /// This is called for all Yield expressions on movable generators
136 pub(super) fn borrow_of_local_data(place: Place<'_>) -> bool {
137     // Reborrow of already borrowed data is ignored
138     // Any errors will be caught on the initial borrow
139     !place.is_indirect()
140 }
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.
146 pub(crate) fn is_upvar_field_projection<'tcx>(
147     tcx: TyCtxt<'tcx>,
148     upvars: &[Upvar<'tcx>],
149     place_ref: PlaceRef<'tcx>,
150     body: &Body<'tcx>,
151 ) -> Option<Field> {
152     let mut place_ref = place_ref;
153     let mut by_ref = false;
154
155     if let Some((place_base, ProjectionElem::Deref)) = place_ref.last_projection() {
156         place_ref = place_base;
157         by_ref = true;
158     }
159
160     match place_ref.last_projection() {
161         Some((place_base, ProjectionElem::Field(field, _ty))) => {
162             let base_ty = place_base.ty(body, tcx).ty;
163             if (base_ty.is_closure() || base_ty.is_generator())
164                 && (!by_ref || upvars[field.index()].by_ref)
165             {
166                 Some(field)
167             } else {
168                 None
169             }
170         }
171         _ => None,
172     }
173 }