]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/borrow_check/path_utils.rs
23b4799643a6cfb6282b167ee692cb3c6425c6ab
[rust.git] / src / librustc_mir / 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::dataflow::indexes::BorrowIndex;
5 use rustc::mir::BorrowKind;
6 use rustc::mir::{BasicBlock, Body, Location, Place, PlaceBase};
7 use rustc::ty::{self, TyCtxt};
8 use rustc_data_structures::graph::dominators::Dominators;
9
10 /// Returns `true` if the borrow represented by `kind` is
11 /// allowed to be split into separate Reservation and
12 /// Activation phases.
13 pub(super) fn allow_two_phase_borrow(kind: BorrowKind) -> bool {
14     kind.allows_two_phase_borrow()
15 }
16
17 /// Control for the path borrow checking code
18 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
19 pub(super) enum Control {
20     Continue,
21     Break,
22 }
23
24 /// Encapsulates the idea of iterating over every borrow that involves a particular path
25 pub(super) fn each_borrow_involving_path<'tcx, F, I, S>(
26     s: &mut S,
27     tcx: TyCtxt<'tcx>,
28     param_env: ty::ParamEnv<'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             param_env,
52             body,
53             &borrowed.borrowed_place,
54             borrowed.kind,
55             place.as_ref(),
56             access,
57             places_conflict::PlaceConflictBias::Overlap,
58         ) {
59             debug!(
60                 "each_borrow_involving_path: {:?} @ {:?} vs. {:?}/{:?}",
61                 i, borrowed, place, access
62             );
63             let ctrl = op(s, i, borrowed);
64             if ctrl == Control::Break {
65                 return;
66             }
67         }
68     }
69 }
70
71 pub(super) fn is_active<'tcx>(
72     dominators: &Dominators<BasicBlock>,
73     borrow_data: &BorrowData<'tcx>,
74     location: Location,
75 ) -> bool {
76     debug!("is_active(borrow_data={:?}, location={:?})", borrow_data, location);
77
78     let activation_location = match borrow_data.activation_location {
79         // If this is not a 2-phase borrow, it is always active.
80         TwoPhaseActivation::NotTwoPhase => return true,
81         // And if the unique 2-phase use is not an activation, then it is *never* active.
82         TwoPhaseActivation::NotActivated => return false,
83         // Otherwise, we derive info from the activation point `loc`:
84         TwoPhaseActivation::ActivatedAt(loc) => loc,
85     };
86
87     // Otherwise, it is active for every location *except* in between
88     // the reservation and the activation:
89     //
90     //       X
91     //      /
92     //     R      <--+ Except for this
93     //    / \        | diamond
94     //    \ /        |
95     //     A  <------+
96     //     |
97     //     Z
98     //
99     // Note that we assume that:
100     // - the reservation R dominates the activation A
101     // - the activation A post-dominates the reservation R (ignoring unwinding edges).
102     //
103     // This means that there can't be an edge that leaves A and
104     // comes back into that diamond unless it passes through R.
105     //
106     // Suboptimal: In some cases, this code walks the dominator
107     // tree twice when it only has to be walked once. I am
108     // lazy. -nmatsakis
109
110     // If dominated by the activation A, then it is active. The
111     // activation occurs upon entering the point A, so this is
112     // also true if location == activation_location.
113     if activation_location.dominates(location, dominators) {
114         return true;
115     }
116
117     // The reservation starts *on exiting* the reservation block,
118     // so check if the location is dominated by R.successor. If so,
119     // this point falls in between the reservation and location.
120     let reserve_location = borrow_data.reserve_location.successor_within_block();
121     if reserve_location.dominates(location, dominators) {
122         false
123     } else {
124         // Otherwise, this point is outside the diamond, so
125         // consider the borrow active. This could happen for
126         // example if the borrow remains active around a loop (in
127         // which case it would be active also for the point R,
128         // which would generate an error).
129         true
130     }
131 }
132
133 /// Determines if a given borrow is borrowing local data
134 /// This is called for all Yield expressions on movable generators
135 pub(super) fn borrow_of_local_data(place: &Place<'_>) -> bool {
136     match place.base {
137         PlaceBase::Static(_) => false,
138
139         // Reborrow of already borrowed data is ignored
140         // Any errors will be caught on the initial borrow
141         PlaceBase::Local(_) => !place.is_indirect(),
142     }
143 }