]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/borrow_check/path_utils.rs
Rename places_conflict to borrow_conflicts_with_place
[rust.git] / src / librustc_mir / borrow_check / path_utils.rs
1 // Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use borrow_check::borrow_set::{BorrowSet, BorrowData, TwoPhaseActivation};
12 use borrow_check::places_conflict;
13 use borrow_check::Context;
14 use borrow_check::AccessDepth;
15 use dataflow::indexes::BorrowIndex;
16 use rustc::mir::{BasicBlock, Location, Mir, Place};
17 use rustc::mir::{ProjectionElem, BorrowKind};
18 use rustc::ty::TyCtxt;
19 use rustc_data_structures::graph::dominators::Dominators;
20
21 /// Returns true if the borrow represented by `kind` is
22 /// allowed to be split into separate Reservation and
23 /// Activation phases.
24 pub(super) fn allow_two_phase_borrow<'a, 'tcx, 'gcx: 'tcx>(
25     tcx: &TyCtxt<'a, 'gcx, 'tcx>,
26     kind: BorrowKind
27 ) -> bool {
28     tcx.two_phase_borrows()
29         && (kind.allows_two_phase_borrow()
30             || tcx.sess.opts.debugging_opts.two_phase_beyond_autoref)
31 }
32
33 /// Control for the path borrow checking code
34 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
35 pub(super) enum Control {
36     Continue,
37     Break,
38 }
39
40 /// Encapsulates the idea of iterating over every borrow that involves a particular path
41 pub(super) fn each_borrow_involving_path<'a, 'tcx, 'gcx: 'tcx, F, I, S> (
42     s: &mut S,
43     tcx: TyCtxt<'a, 'gcx, 'tcx>,
44     mir: &Mir<'tcx>,
45     _context: Context,
46     access_place: (AccessDepth, &Place<'tcx>),
47     borrow_set: &BorrowSet<'tcx>,
48     candidates: I,
49     mut op: F,
50 ) where
51     F: FnMut(&mut S, BorrowIndex, &BorrowData<'tcx>) -> Control,
52     I: Iterator<Item=BorrowIndex>
53 {
54     let (access, place) = access_place;
55
56     // FIXME: analogous code in check_loans first maps `place` to
57     // its base_path.
58
59     // check for loan restricting path P being used. Accounts for
60     // borrows of P, P.a.b, etc.
61     for i in candidates {
62         let borrowed = &borrow_set[i];
63
64         if places_conflict::borrow_conflicts_with_place(
65             tcx,
66             mir,
67             &borrowed.borrowed_place,
68             borrowed.kind,
69             place,
70             access,
71         ) {
72             debug!(
73                 "each_borrow_involving_path: {:?} @ {:?} vs. {:?}/{:?}",
74                 i, borrowed, place, access
75             );
76             let ctrl = op(s, i, borrowed);
77             if ctrl == Control::Break {
78                 return;
79             }
80         }
81     }
82 }
83
84 pub(super) fn is_active<'tcx>(
85     dominators: &Dominators<BasicBlock>,
86     borrow_data: &BorrowData<'tcx>,
87     location: Location
88 ) -> bool {
89     debug!("is_active(borrow_data={:?}, location={:?})", borrow_data, location);
90
91     let activation_location = match borrow_data.activation_location {
92         // If this is not a 2-phase borrow, it is always active.
93         TwoPhaseActivation::NotTwoPhase => return true,
94         // And if the unique 2-phase use is not an activation, then it is *never* active.
95         TwoPhaseActivation::NotActivated => return false,
96         // Otherwise, we derive info from the activation point `loc`:
97         TwoPhaseActivation::ActivatedAt(loc) => loc,
98     };
99
100     // Otherwise, it is active for every location *except* in between
101     // the reservation and the activation:
102     //
103     //       X
104     //      /
105     //     R      <--+ Except for this
106     //    / \        | diamond
107     //    \ /        |
108     //     A  <------+
109     //     |
110     //     Z
111     //
112     // Note that we assume that:
113     // - the reservation R dominates the activation A
114     // - the activation A post-dominates the reservation R (ignoring unwinding edges).
115     //
116     // This means that there can't be an edge that leaves A and
117     // comes back into that diamond unless it passes through R.
118     //
119     // Suboptimal: In some cases, this code walks the dominator
120     // tree twice when it only has to be walked once. I am
121     // lazy. -nmatsakis
122
123     // If dominated by the activation A, then it is active. The
124     // activation occurs upon entering the point A, so this is
125     // also true if location == activation_location.
126     if activation_location.dominates(location, dominators) {
127         return true;
128     }
129
130     // The reservation starts *on exiting* the reservation block,
131     // so check if the location is dominated by R.successor. If so,
132     // this point falls in between the reservation and location.
133     let reserve_location = borrow_data.reserve_location.successor_within_block();
134     if reserve_location.dominates(location, dominators) {
135         false
136     } else {
137         // Otherwise, this point is outside the diamond, so
138         // consider the borrow active. This could happen for
139         // example if the borrow remains active around a loop (in
140         // which case it would be active also for the point R,
141         // which would generate an error).
142         true
143     }
144 }
145
146 /// Determines if a given borrow is borrowing local data
147 /// This is called for all Yield statements on movable generators
148 pub(super) fn borrow_of_local_data<'tcx>(place: &Place<'tcx>) -> bool {
149     match place {
150         Place::Promoted(_) |
151         Place::Static(..) => false,
152         Place::Local(..) => true,
153         Place::Projection(box proj) => {
154             match proj.elem {
155                 // Reborrow of already borrowed data is ignored
156                 // Any errors will be caught on the initial borrow
157                 ProjectionElem::Deref => false,
158
159                 // For interior references and downcasts, find out if the base is local
160                 ProjectionElem::Field(..)
161                     | ProjectionElem::Index(..)
162                     | ProjectionElem::ConstantIndex { .. }
163                 | ProjectionElem::Subslice { .. }
164                 | ProjectionElem::Downcast(..) => borrow_of_local_data(&proj.base),
165             }
166         }
167     }
168 }