]>
Commit | Line | Data |
---|---|---|
3b2f2976 XL |
1 | // Copyright 2012-2017 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 | ||
ff7c6d11 XL |
11 | use rustc; |
12 | use rustc::hir; | |
13 | use rustc::hir::def_id::DefId; | |
14 | use rustc::middle::region; | |
15 | use rustc::mir::{self, Location, Place, Mir}; | |
16 | use rustc::mir::visit::{PlaceContext, Visitor}; | |
abe05a73 | 17 | use rustc::ty::{self, Region, TyCtxt}; |
ea8adc8c | 18 | use rustc::ty::RegionKind; |
3b2f2976 XL |
19 | use rustc::ty::RegionKind::ReScope; |
20 | use rustc::util::nodemap::{FxHashMap, FxHashSet}; | |
21 | ||
22 | use rustc_data_structures::bitslice::{BitwiseOperator}; | |
23 | use rustc_data_structures::indexed_set::{IdxSet}; | |
ff7c6d11 | 24 | use rustc_data_structures::indexed_vec::{Idx, IndexVec}; |
0531ce1d | 25 | use rustc_data_structures::sync::Lrc; |
3b2f2976 | 26 | |
ff7c6d11 XL |
27 | use dataflow::{BitDenotation, BlockSets, InitialFlow}; |
28 | pub use dataflow::indexes::{BorrowIndex, ReserveOrActivateIndex}; | |
29 | use borrow_check::nll::region_infer::RegionInferenceContext; | |
30 | use borrow_check::nll::ToRegionVid; | |
3b2f2976 | 31 | |
ea8adc8c XL |
32 | use syntax_pos::Span; |
33 | ||
3b2f2976 | 34 | use std::fmt; |
ff7c6d11 XL |
35 | use std::hash::Hash; |
36 | use std::rc::Rc; | |
3b2f2976 | 37 | |
ff7c6d11 XL |
38 | /// `Borrows` stores the data used in the analyses that track the flow |
39 | /// of borrows. | |
40 | /// | |
41 | /// It uniquely identifies every borrow (`Rvalue::Ref`) by a | |
42 | /// `BorrowIndex`, and maps each such index to a `BorrowData` | |
43 | /// describing the borrow. These indexes are used for representing the | |
44 | /// borrows in compact bitvectors. | |
abe05a73 XL |
45 | pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> { |
46 | tcx: TyCtxt<'a, 'gcx, 'tcx>, | |
3b2f2976 | 47 | mir: &'a Mir<'tcx>, |
0531ce1d | 48 | scope_tree: Lrc<region::ScopeTree>, |
ff7c6d11 XL |
49 | root_scope: Option<region::Scope>, |
50 | ||
51 | /// The fundamental map relating bitvector indexes to the borrows | |
52 | /// in the MIR. | |
3b2f2976 | 53 | borrows: IndexVec<BorrowIndex, BorrowData<'tcx>>, |
ff7c6d11 XL |
54 | |
55 | /// Each borrow is also uniquely identified in the MIR by the | |
56 | /// `Location` of the assignment statement in which it appears on | |
57 | /// the right hand side; we map each such location to the | |
58 | /// corresponding `BorrowIndex`. | |
3b2f2976 | 59 | location_map: FxHashMap<Location, BorrowIndex>, |
ff7c6d11 XL |
60 | |
61 | /// Every borrow in MIR is immediately stored into a place via an | |
62 | /// assignment statement. This maps each such assigned place back | |
63 | /// to its borrow-indexes. | |
64 | assigned_map: FxHashMap<Place<'tcx>, FxHashSet<BorrowIndex>>, | |
65 | ||
0531ce1d XL |
66 | /// Locations which activate borrows. |
67 | /// NOTE: A given location may activate more than one borrow in the future | |
68 | /// when more general two-phase borrow support is introduced, but for now we | |
69 | /// only need to store one borrow index | |
70 | activation_map: FxHashMap<Location, BorrowIndex>, | |
71 | ||
ff7c6d11 XL |
72 | /// Every borrow has a region; this maps each such regions back to |
73 | /// its borrow-indexes. | |
3b2f2976 | 74 | region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>, |
0531ce1d XL |
75 | |
76 | /// Map from local to all the borrows on that local | |
ff7c6d11 | 77 | local_map: FxHashMap<mir::Local, FxHashSet<BorrowIndex>>, |
ff7c6d11 | 78 | |
0531ce1d XL |
79 | /// Maps regions to their corresponding source spans |
80 | /// Only contains ReScope()s as keys | |
81 | region_span_map: FxHashMap<RegionKind, Span>, | |
ff7c6d11 | 82 | |
0531ce1d XL |
83 | /// NLL region inference context with which NLL queries should be resolved |
84 | nonlexical_regioncx: Option<Rc<RegionInferenceContext<'tcx>>>, | |
3b2f2976 XL |
85 | } |
86 | ||
87 | // temporarily allow some dead fields: `kind` and `region` will be | |
ff7c6d11 | 88 | // needed by borrowck; `borrowed_place` will probably be a MovePathIndex when |
3b2f2976 XL |
89 | // that is extended to include borrowed data paths. |
90 | #[allow(dead_code)] | |
91 | #[derive(Debug)] | |
92 | pub struct BorrowData<'tcx> { | |
0531ce1d XL |
93 | /// Location where the borrow reservation starts. |
94 | /// In many cases, this will be equal to the activation location but not always. | |
95 | pub(crate) reserve_location: Location, | |
96 | /// What kind of borrow this is | |
3b2f2976 | 97 | pub(crate) kind: mir::BorrowKind, |
0531ce1d | 98 | /// The region for which this borrow is live |
3b2f2976 | 99 | pub(crate) region: Region<'tcx>, |
0531ce1d | 100 | /// Place from which we are borrowing |
ff7c6d11 | 101 | pub(crate) borrowed_place: mir::Place<'tcx>, |
0531ce1d | 102 | /// Place to which the borrow was stored |
ff7c6d11 | 103 | pub(crate) assigned_place: mir::Place<'tcx>, |
3b2f2976 XL |
104 | } |
105 | ||
106 | impl<'tcx> fmt::Display for BorrowData<'tcx> { | |
107 | fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { | |
108 | let kind = match self.kind { | |
109 | mir::BorrowKind::Shared => "", | |
110 | mir::BorrowKind::Unique => "uniq ", | |
2c00a5a8 | 111 | mir::BorrowKind::Mut { .. } => "mut ", |
3b2f2976 XL |
112 | }; |
113 | let region = format!("{}", self.region); | |
114 | let region = if region.len() > 0 { format!("{} ", region) } else { region }; | |
ff7c6d11 XL |
115 | write!(w, "&{}{}{:?}", region, kind, self.borrowed_place) |
116 | } | |
117 | } | |
118 | ||
119 | impl ReserveOrActivateIndex { | |
2c00a5a8 | 120 | fn reserved(i: BorrowIndex) -> Self { ReserveOrActivateIndex::new(i.index() * 2) } |
ff7c6d11 XL |
121 | fn active(i: BorrowIndex) -> Self { ReserveOrActivateIndex::new((i.index() * 2) + 1) } |
122 | ||
123 | pub(crate) fn is_reservation(self) -> bool { self.index() % 2 == 0 } | |
124 | pub(crate) fn is_activation(self) -> bool { self.index() % 2 == 1} | |
125 | ||
126 | pub(crate) fn kind(self) -> &'static str { | |
127 | if self.is_reservation() { "reserved" } else { "active" } | |
128 | } | |
129 | pub(crate) fn borrow_index(self) -> BorrowIndex { | |
130 | BorrowIndex::new(self.index() / 2) | |
3b2f2976 XL |
131 | } |
132 | } | |
133 | ||
abe05a73 XL |
134 | impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> { |
135 | pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, | |
136 | mir: &'a Mir<'tcx>, | |
ff7c6d11 XL |
137 | nonlexical_regioncx: Option<Rc<RegionInferenceContext<'tcx>>>, |
138 | def_id: DefId, | |
139 | body_id: Option<hir::BodyId>) | |
abe05a73 | 140 | -> Self { |
ff7c6d11 XL |
141 | let scope_tree = tcx.region_scope_tree(def_id); |
142 | let root_scope = body_id.map(|body_id| { | |
143 | region::Scope::CallSite(tcx.hir.body(body_id).value.hir_id.local_id) | |
144 | }); | |
abe05a73 XL |
145 | let mut visitor = GatherBorrows { |
146 | tcx, | |
147 | mir, | |
148 | idx_vec: IndexVec::new(), | |
149 | location_map: FxHashMap(), | |
ff7c6d11 | 150 | assigned_map: FxHashMap(), |
0531ce1d | 151 | activation_map: FxHashMap(), |
abe05a73 | 152 | region_map: FxHashMap(), |
ff7c6d11 | 153 | local_map: FxHashMap(), |
0531ce1d XL |
154 | region_span_map: FxHashMap(), |
155 | nonlexical_regioncx: nonlexical_regioncx.clone() | |
abe05a73 | 156 | }; |
3b2f2976 XL |
157 | visitor.visit_mir(mir); |
158 | return Borrows { tcx: tcx, | |
159 | mir: mir, | |
160 | borrows: visitor.idx_vec, | |
ff7c6d11 XL |
161 | scope_tree, |
162 | root_scope, | |
3b2f2976 | 163 | location_map: visitor.location_map, |
ff7c6d11 | 164 | assigned_map: visitor.assigned_map, |
0531ce1d | 165 | activation_map: visitor.activation_map, |
ea8adc8c | 166 | region_map: visitor.region_map, |
ff7c6d11 | 167 | local_map: visitor.local_map, |
abe05a73 XL |
168 | region_span_map: visitor.region_span_map, |
169 | nonlexical_regioncx }; | |
3b2f2976 | 170 | |
abe05a73 XL |
171 | struct GatherBorrows<'a, 'gcx: 'tcx, 'tcx: 'a> { |
172 | tcx: TyCtxt<'a, 'gcx, 'tcx>, | |
173 | mir: &'a Mir<'tcx>, | |
3b2f2976 XL |
174 | idx_vec: IndexVec<BorrowIndex, BorrowData<'tcx>>, |
175 | location_map: FxHashMap<Location, BorrowIndex>, | |
ff7c6d11 | 176 | assigned_map: FxHashMap<Place<'tcx>, FxHashSet<BorrowIndex>>, |
0531ce1d | 177 | activation_map: FxHashMap<Location, BorrowIndex>, |
3b2f2976 | 178 | region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>, |
ff7c6d11 | 179 | local_map: FxHashMap<mir::Local, FxHashSet<BorrowIndex>>, |
ea8adc8c | 180 | region_span_map: FxHashMap<RegionKind, Span>, |
0531ce1d | 181 | nonlexical_regioncx: Option<Rc<RegionInferenceContext<'tcx>>>, |
3b2f2976 | 182 | } |
abe05a73 XL |
183 | |
184 | impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> { | |
ff7c6d11 XL |
185 | fn visit_assign(&mut self, |
186 | block: mir::BasicBlock, | |
187 | assigned_place: &mir::Place<'tcx>, | |
3b2f2976 XL |
188 | rvalue: &mir::Rvalue<'tcx>, |
189 | location: mir::Location) { | |
ff7c6d11 XL |
190 | fn root_local(mut p: &mir::Place<'_>) -> Option<mir::Local> { |
191 | loop { match p { | |
192 | mir::Place::Projection(pi) => p = &pi.base, | |
193 | mir::Place::Static(_) => return None, | |
194 | mir::Place::Local(l) => return Some(*l) | |
195 | }} | |
196 | } | |
197 | ||
198 | if let mir::Rvalue::Ref(region, kind, ref borrowed_place) = *rvalue { | |
199 | if is_unsafe_place(self.tcx, self.mir, borrowed_place) { return; } | |
abe05a73 | 200 | |
0531ce1d XL |
201 | let activate_location = self.compute_activation_location(location, |
202 | &assigned_place, | |
203 | region, | |
204 | kind); | |
3b2f2976 | 205 | let borrow = BorrowData { |
0531ce1d XL |
206 | kind, region, |
207 | reserve_location: location, | |
ff7c6d11 XL |
208 | borrowed_place: borrowed_place.clone(), |
209 | assigned_place: assigned_place.clone(), | |
3b2f2976 XL |
210 | }; |
211 | let idx = self.idx_vec.push(borrow); | |
212 | self.location_map.insert(location, idx); | |
ff7c6d11 | 213 | |
0531ce1d XL |
214 | // This assert is a good sanity check until more general 2-phase borrow |
215 | // support is introduced. See NOTE on the activation_map field for more | |
216 | assert!(!self.activation_map.contains_key(&activate_location), | |
217 | "More than one activation introduced at the same location."); | |
218 | self.activation_map.insert(activate_location, idx); | |
219 | ||
ff7c6d11 XL |
220 | insert(&mut self.assigned_map, assigned_place, idx); |
221 | insert(&mut self.region_map, ®ion, idx); | |
222 | if let Some(local) = root_local(borrowed_place) { | |
223 | insert(&mut self.local_map, &local, idx); | |
224 | } | |
225 | } | |
226 | ||
227 | return self.super_assign(block, assigned_place, rvalue, location); | |
228 | ||
229 | fn insert<'a, K, V>(map: &'a mut FxHashMap<K, FxHashSet<V>>, | |
230 | k: &K, | |
231 | v: V) | |
232 | where K: Clone+Eq+Hash, V: Eq+Hash | |
233 | { | |
234 | map.entry(k.clone()) | |
235 | .or_insert(FxHashSet()) | |
236 | .insert(v); | |
3b2f2976 XL |
237 | } |
238 | } | |
ea8adc8c | 239 | |
ff7c6d11 XL |
240 | fn visit_rvalue(&mut self, |
241 | rvalue: &mir::Rvalue<'tcx>, | |
242 | location: mir::Location) { | |
243 | if let mir::Rvalue::Ref(region, kind, ref place) = *rvalue { | |
244 | // double-check that we already registered a BorrowData for this | |
245 | ||
246 | let mut found_it = false; | |
247 | for idx in &self.region_map[region] { | |
248 | let bd = &self.idx_vec[*idx]; | |
0531ce1d | 249 | if bd.reserve_location == location && |
ff7c6d11 XL |
250 | bd.kind == kind && |
251 | bd.region == region && | |
252 | bd.borrowed_place == *place | |
253 | { | |
254 | found_it = true; | |
255 | break; | |
256 | } | |
257 | } | |
258 | assert!(found_it, "Ref {:?} at {:?} missing BorrowData", rvalue, location); | |
259 | } | |
260 | ||
261 | return self.super_rvalue(rvalue, location); | |
262 | } | |
263 | ||
ea8adc8c XL |
264 | fn visit_statement(&mut self, |
265 | block: mir::BasicBlock, | |
266 | statement: &mir::Statement<'tcx>, | |
267 | location: Location) { | |
268 | if let mir::StatementKind::EndRegion(region_scope) = statement.kind { | |
269 | self.region_span_map.insert(ReScope(region_scope), statement.source_info.span); | |
270 | } | |
ff7c6d11 | 271 | return self.super_statement(block, statement, location); |
ea8adc8c | 272 | } |
3b2f2976 | 273 | } |
0531ce1d XL |
274 | |
275 | /// A MIR visitor that determines if a specific place is used in a two-phase activating | |
276 | /// manner in a given chunk of MIR. | |
277 | struct ContainsUseOfPlace<'b, 'tcx: 'b> { | |
278 | target: &'b Place<'tcx>, | |
279 | use_found: bool, | |
280 | } | |
281 | ||
282 | impl<'b, 'tcx: 'b> ContainsUseOfPlace<'b, 'tcx> { | |
283 | fn new(place: &'b Place<'tcx>) -> Self { | |
284 | Self { target: place, use_found: false } | |
285 | } | |
286 | ||
287 | /// return whether `context` should be considered a "use" of a | |
288 | /// place found in that context. "Uses" activate associated | |
289 | /// borrows (at least when such uses occur while the borrow also | |
290 | /// has a reservation at the time). | |
291 | fn is_potential_use(context: PlaceContext) -> bool { | |
292 | match context { | |
293 | // storage effects on a place do not activate it | |
294 | PlaceContext::StorageLive | PlaceContext::StorageDead => false, | |
295 | ||
296 | // validation effects do not activate a place | |
297 | // | |
298 | // FIXME: Should they? Is it just another read? Or can we | |
299 | // guarantee it won't dereference the stored address? How | |
300 | // "deep" does validation go? | |
301 | PlaceContext::Validate => false, | |
302 | ||
303 | // FIXME: This is here to not change behaviour from before | |
304 | // AsmOutput existed, but it's not necessarily a pure overwrite. | |
305 | // so it's possible this should activate the place. | |
306 | PlaceContext::AsmOutput | | |
307 | // pure overwrites of a place do not activate it. (note | |
308 | // PlaceContext::Call is solely about dest place) | |
309 | PlaceContext::Store | PlaceContext::Call => false, | |
310 | ||
311 | // reads of a place *do* activate it | |
312 | PlaceContext::Move | | |
313 | PlaceContext::Copy | | |
314 | PlaceContext::Drop | | |
315 | PlaceContext::Inspect | | |
316 | PlaceContext::Borrow { .. } | | |
317 | PlaceContext::Projection(..) => true, | |
318 | } | |
319 | } | |
320 | } | |
321 | ||
322 | impl<'b, 'tcx: 'b> Visitor<'tcx> for ContainsUseOfPlace<'b, 'tcx> { | |
323 | fn visit_place(&mut self, | |
324 | place: &mir::Place<'tcx>, | |
325 | context: PlaceContext<'tcx>, | |
326 | location: Location) { | |
327 | if Self::is_potential_use(context) && place == self.target { | |
328 | self.use_found = true; | |
329 | return; | |
330 | // There is no need to keep checking the statement, we already found a use | |
331 | } | |
332 | ||
333 | self.super_place(place, context, location); | |
334 | } | |
335 | } | |
336 | ||
337 | impl<'a, 'gcx, 'tcx> GatherBorrows<'a, 'gcx, 'tcx> { | |
338 | /// Returns true if the borrow represented by `kind` is | |
339 | /// allowed to be split into separate Reservation and | |
340 | /// Activation phases. | |
341 | fn allow_two_phase_borrow(&self, kind: mir::BorrowKind) -> bool { | |
342 | self.tcx.two_phase_borrows() && | |
343 | (kind.allows_two_phase_borrow() || | |
344 | self.tcx.sess.opts.debugging_opts.two_phase_beyond_autoref) | |
345 | } | |
346 | ||
347 | /// Returns true if the given location contains an NLL-activating use of the given place | |
348 | fn location_contains_use(&self, location: Location, place: &Place) -> bool { | |
349 | let mut use_checker = ContainsUseOfPlace::new(place); | |
350 | let block = &self.mir.basic_blocks().get(location.block).unwrap_or_else(|| { | |
351 | panic!("could not find block at location {:?}", location); | |
352 | }); | |
353 | if location.statement_index != block.statements.len() { | |
354 | // This is a statement | |
355 | let stmt = block.statements.get(location.statement_index).unwrap_or_else(|| { | |
356 | panic!("could not find statement at location {:?}"); | |
357 | }); | |
358 | use_checker.visit_statement(location.block, stmt, location); | |
359 | } else { | |
360 | // This is a terminator | |
361 | match block.terminator { | |
362 | Some(ref term) => { | |
363 | use_checker.visit_terminator(location.block, term, location); | |
364 | } | |
365 | None => { | |
366 | // There is no way for Place to be used by the terminator if there is no | |
367 | // terminator | |
368 | } | |
369 | } | |
370 | } | |
371 | ||
372 | use_checker.use_found | |
373 | } | |
374 | ||
375 | /// Determines if the provided region is terminated after the provided location. | |
376 | /// EndRegion statements terminate their enclosed region::Scope. | |
377 | /// We also consult with the NLL region inference engine, should one be available | |
378 | fn region_terminated_after(&self, region: Region<'tcx>, location: Location) -> bool { | |
379 | let block_data = &self.mir[location.block]; | |
380 | if location.statement_index != block_data.statements.len() { | |
381 | let stmt = &block_data.statements[location.statement_index]; | |
382 | if let mir::StatementKind::EndRegion(region_scope) = stmt.kind { | |
383 | if &ReScope(region_scope) == region { | |
384 | // We encountered an EndRegion statement that terminates the provided | |
385 | // region | |
386 | return true; | |
387 | } | |
388 | } | |
389 | } | |
390 | if let Some(ref regioncx) = self.nonlexical_regioncx { | |
391 | if !regioncx.region_contains_point(region, location) { | |
392 | // NLL says the region has ended already | |
393 | return true; | |
394 | } | |
395 | } | |
396 | ||
397 | false | |
398 | } | |
399 | ||
400 | /// Computes the activation location of a borrow. | |
401 | /// The general idea is to start at the beginning of the region and perform a DFS | |
402 | /// until we exit the region, either via an explicit EndRegion or because NLL tells | |
403 | /// us so. If we find more than one valid activation point, we currently panic the | |
404 | /// compiler since two-phase borrows are only currently supported for compiler- | |
405 | /// generated code. More precisely, we only allow two-phase borrows for: | |
406 | /// - Function calls (fn some_func(&mut self, ....)) | |
407 | /// - *Assign operators (a += b -> fn add_assign(&mut self, other: Self)) | |
408 | /// See | |
409 | /// - https://github.com/rust-lang/rust/issues/48431 | |
410 | /// for detailed design notes. | |
411 | /// See the FIXME in the body of the function for notes on extending support to more | |
412 | /// general two-phased borrows. | |
413 | fn compute_activation_location(&self, | |
414 | start_location: Location, | |
415 | assigned_place: &mir::Place<'tcx>, | |
416 | region: Region<'tcx>, | |
417 | kind: mir::BorrowKind) -> Location { | |
418 | debug!("Borrows::compute_activation_location({:?}, {:?}, {:?})", | |
419 | start_location, | |
420 | assigned_place, | |
421 | region); | |
422 | if !self.allow_two_phase_borrow(kind) { | |
423 | debug!(" -> {:?}", start_location); | |
424 | return start_location; | |
425 | } | |
426 | ||
427 | // Perform the DFS. | |
428 | // `stack` is the stack of locations still under consideration | |
429 | // `visited` is the set of points we have already visited | |
430 | // `found_use` is an Option that becomes Some when we find a use | |
431 | let mut stack = vec![start_location]; | |
432 | let mut visited = FxHashSet(); | |
433 | let mut found_use = None; | |
434 | while let Some(curr_loc) = stack.pop() { | |
435 | let block_data = &self.mir.basic_blocks() | |
436 | .get(curr_loc.block) | |
437 | .unwrap_or_else(|| { | |
438 | panic!("could not find block at location {:?}", curr_loc); | |
439 | }); | |
440 | ||
441 | if self.region_terminated_after(region, curr_loc) { | |
442 | // No need to process this statement. | |
443 | // It's either an EndRegion (and thus couldn't use assigned_place) or not | |
444 | // contained in the NLL region and thus a use would be invalid | |
445 | continue; | |
446 | } | |
447 | ||
448 | if !visited.insert(curr_loc) { | |
449 | debug!(" Already visited {:?}", curr_loc); | |
450 | continue; | |
451 | } | |
452 | ||
453 | if self.location_contains_use(curr_loc, assigned_place) { | |
454 | // FIXME: Handle this case a little more gracefully. Perhaps collect | |
455 | // all uses in a vector, and find the point in the CFG that dominates | |
456 | // all of them? | |
457 | // Right now this is sufficient though since there should only be exactly | |
458 | // one borrow-activating use of the borrow. | |
459 | assert!(found_use.is_none(), "Found secondary use of place"); | |
460 | found_use = Some(curr_loc); | |
461 | } | |
462 | ||
463 | // Push the points we should consider next. | |
464 | if curr_loc.statement_index < block_data.statements.len() { | |
465 | stack.push(curr_loc.successor_within_block()); | |
466 | } else { | |
467 | stack.extend(block_data.terminator().successors().iter().map( | |
468 | |&basic_block| { | |
469 | Location { | |
470 | statement_index: 0, | |
471 | block: basic_block | |
472 | } | |
473 | } | |
474 | )) | |
475 | } | |
476 | } | |
477 | ||
478 | let found_use = found_use.expect("Did not find use of two-phase place"); | |
479 | debug!(" -> {:?}", found_use); | |
480 | found_use | |
481 | } | |
482 | } | |
483 | } | |
484 | ||
485 | /// Returns the span for the "end point" given region. This will | |
486 | /// return `None` if NLL is enabled, since that concept has no | |
487 | /// meaning there. Otherwise, return region span if it exists and | |
488 | /// span for end of the function if it doesn't exist. | |
489 | pub(crate) fn opt_region_end_span(&self, region: &Region) -> Option<Span> { | |
490 | match self.nonlexical_regioncx { | |
491 | Some(_) => None, | |
492 | None => { | |
493 | match self.region_span_map.get(region) { | |
494 | Some(span) => Some(self.tcx.sess.codemap().end_point(*span)), | |
495 | None => Some(self.tcx.sess.codemap().end_point(self.mir.span)) | |
496 | } | |
497 | } | |
498 | } | |
3b2f2976 XL |
499 | } |
500 | ||
501 | pub fn borrows(&self) -> &IndexVec<BorrowIndex, BorrowData<'tcx>> { &self.borrows } | |
502 | ||
0531ce1d | 503 | pub fn scope_tree(&self) -> &Lrc<region::ScopeTree> { &self.scope_tree } |
ff7c6d11 | 504 | |
3b2f2976 | 505 | pub fn location(&self, idx: BorrowIndex) -> &Location { |
0531ce1d | 506 | &self.borrows[idx].reserve_location |
3b2f2976 | 507 | } |
ea8adc8c | 508 | |
abe05a73 | 509 | /// Add all borrows to the kill set, if those borrows are out of scope at `location`. |
0531ce1d XL |
510 | /// That means either they went out of either a nonlexical scope, if we care about those |
511 | /// at the moment, or the location represents a lexical EndRegion | |
abe05a73 | 512 | fn kill_loans_out_of_scope_at_location(&self, |
ff7c6d11 | 513 | sets: &mut BlockSets<ReserveOrActivateIndex>, |
0531ce1d | 514 | location: Location) { |
ff7c6d11 XL |
515 | if let Some(ref regioncx) = self.nonlexical_regioncx { |
516 | // NOTE: The state associated with a given `location` | |
517 | // reflects the dataflow on entry to the statement. If it | |
518 | // does not contain `borrow_region`, then then that means | |
519 | // that the statement at `location` kills the borrow. | |
520 | // | |
521 | // We are careful always to call this function *before* we | |
522 | // set up the gen-bits for the statement or | |
523 | // termanator. That way, if the effect of the statement or | |
524 | // terminator *does* introduce a new loan of the same | |
525 | // region, then setting that gen-bit will override any | |
526 | // potential kill introduced here. | |
abe05a73 XL |
527 | for (borrow_index, borrow_data) in self.borrows.iter_enumerated() { |
528 | let borrow_region = borrow_data.region.to_region_vid(); | |
529 | if !regioncx.region_contains_point(borrow_region, location) { | |
ff7c6d11 | 530 | sets.kill(&ReserveOrActivateIndex::reserved(borrow_index)); |
0531ce1d | 531 | sets.kill(&ReserveOrActivateIndex::active(borrow_index)); |
abe05a73 XL |
532 | } |
533 | } | |
534 | } | |
ea8adc8c | 535 | } |
3b2f2976 | 536 | |
0531ce1d XL |
537 | fn kill_borrows_on_local(&self, |
538 | sets: &mut BlockSets<ReserveOrActivateIndex>, | |
539 | local: &rustc::mir::Local) | |
540 | { | |
541 | if let Some(borrow_indexes) = self.local_map.get(local) { | |
542 | sets.kill_all(borrow_indexes.iter() | |
543 | .map(|b| ReserveOrActivateIndex::reserved(*b))); | |
544 | sets.kill_all(borrow_indexes.iter() | |
545 | .map(|b| ReserveOrActivateIndex::active(*b))); | |
546 | } | |
547 | } | |
548 | ||
549 | /// Performs the activations for a given location | |
550 | fn perform_activations_at_location(&self, | |
551 | sets: &mut BlockSets<ReserveOrActivateIndex>, | |
552 | location: Location) { | |
553 | // Handle activations | |
554 | match self.activation_map.get(&location) { | |
555 | Some(&activated) => { | |
556 | debug!("activating borrow {:?}", activated); | |
557 | sets.gen(&ReserveOrActivateIndex::active(activated)) | |
558 | } | |
559 | None => {} | |
560 | } | |
561 | } | |
562 | } | |
563 | ||
564 | impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> { | |
565 | type Idx = ReserveOrActivateIndex; | |
566 | fn name() -> &'static str { "borrows" } | |
567 | fn bits_per_block(&self) -> usize { | |
568 | self.borrows.len() * 2 | |
569 | } | |
570 | ||
571 | fn start_block_effect(&self, _entry_set: &mut IdxSet<ReserveOrActivateIndex>) { | |
572 | // no borrows of code region_scopes have been taken prior to | |
573 | // function execution, so this method has no effect on | |
574 | // `_sets`. | |
575 | } | |
576 | ||
577 | fn before_statement_effect(&self, | |
578 | sets: &mut BlockSets<ReserveOrActivateIndex>, | |
579 | location: Location) { | |
580 | debug!("Borrows::before_statement_effect sets: {:?} location: {:?}", sets, location); | |
581 | self.kill_loans_out_of_scope_at_location(sets, location); | |
582 | } | |
583 | ||
584 | fn statement_effect(&self, sets: &mut BlockSets<ReserveOrActivateIndex>, location: Location) { | |
585 | debug!("Borrows::statement_effect sets: {:?} location: {:?}", sets, location); | |
586 | ||
3b2f2976 XL |
587 | let block = &self.mir.basic_blocks().get(location.block).unwrap_or_else(|| { |
588 | panic!("could not find block at location {:?}", location); | |
589 | }); | |
590 | let stmt = block.statements.get(location.statement_index).unwrap_or_else(|| { | |
591 | panic!("could not find statement at location {:?}"); | |
592 | }); | |
ff7c6d11 | 593 | |
0531ce1d XL |
594 | self.perform_activations_at_location(sets, location); |
595 | self.kill_loans_out_of_scope_at_location(sets, location); | |
ff7c6d11 | 596 | |
3b2f2976 | 597 | match stmt.kind { |
ff7c6d11 | 598 | // EndRegion kills any borrows (reservations and active borrows both) |
ea8adc8c XL |
599 | mir::StatementKind::EndRegion(region_scope) => { |
600 | if let Some(borrow_indexes) = self.region_map.get(&ReScope(region_scope)) { | |
abe05a73 | 601 | assert!(self.nonlexical_regioncx.is_none()); |
ff7c6d11 XL |
602 | for idx in borrow_indexes { |
603 | sets.kill(&ReserveOrActivateIndex::reserved(*idx)); | |
0531ce1d | 604 | sets.kill(&ReserveOrActivateIndex::active(*idx)); |
ff7c6d11 | 605 | } |
ea8adc8c XL |
606 | } else { |
607 | // (if there is no entry, then there are no borrows to be tracked) | |
608 | } | |
3b2f2976 XL |
609 | } |
610 | ||
ff7c6d11 XL |
611 | mir::StatementKind::Assign(ref lhs, ref rhs) => { |
612 | // Make sure there are no remaining borrows for variables | |
613 | // that are assigned over. | |
614 | if let Place::Local(ref local) = *lhs { | |
615 | // FIXME: Handle the case in which we're assigning over | |
616 | // a projection (`foo.bar`). | |
0531ce1d | 617 | self.kill_borrows_on_local(sets, local); |
ff7c6d11 XL |
618 | } |
619 | ||
620 | // NOTE: if/when the Assign case is revised to inspect | |
621 | // the assigned_place here, make sure to also | |
622 | // re-consider the current implementations of the | |
623 | // propagate_call_return method. | |
624 | ||
625 | if let mir::Rvalue::Ref(region, _, ref place) = *rhs { | |
626 | if is_unsafe_place(self.tcx, self.mir, place) { return; } | |
0531ce1d XL |
627 | let index = self.location_map.get(&location).unwrap_or_else(|| { |
628 | panic!("could not find BorrowIndex for location {:?}", location); | |
629 | }); | |
630 | ||
ff7c6d11 | 631 | if let RegionKind::ReEmpty = region { |
0531ce1d XL |
632 | // If the borrowed value dies before the borrow is used, the region for |
633 | // the borrow can be empty. Don't track the borrow in that case. | |
634 | sets.kill(&ReserveOrActivateIndex::active(*index)); | |
ff7c6d11 XL |
635 | return |
636 | } | |
637 | ||
3b2f2976 XL |
638 | assert!(self.region_map.get(region).unwrap_or_else(|| { |
639 | panic!("could not find BorrowIndexs for region {:?}", region); | |
640 | }).contains(&index)); | |
ff7c6d11 XL |
641 | sets.gen(&ReserveOrActivateIndex::reserved(*index)); |
642 | ||
0531ce1d XL |
643 | // Issue #46746: Two-phase borrows handles |
644 | // stmts of form `Tmp = &mut Borrow` ... | |
645 | match lhs { | |
646 | Place::Local(..) | Place::Static(..) => {} // okay | |
647 | Place::Projection(..) => { | |
648 | // ... can assign into projections, | |
649 | // e.g. `box (&mut _)`. Current | |
650 | // conservative solution: force | |
651 | // immediate activation here. | |
652 | sets.gen(&ReserveOrActivateIndex::active(*index)); | |
ff7c6d11 XL |
653 | } |
654 | } | |
655 | } | |
656 | } | |
657 | ||
658 | mir::StatementKind::StorageDead(local) => { | |
659 | // Make sure there are no remaining borrows for locals that | |
660 | // are gone out of scope. | |
0531ce1d | 661 | self.kill_borrows_on_local(sets, &local) |
ff7c6d11 XL |
662 | } |
663 | ||
664 | mir::StatementKind::InlineAsm { ref outputs, ref asm, .. } => { | |
665 | for (output, kind) in outputs.iter().zip(&asm.outputs) { | |
666 | if !kind.is_indirect && !kind.is_rw { | |
667 | // Make sure there are no remaining borrows for direct | |
668 | // output variables. | |
669 | if let Place::Local(ref local) = *output { | |
670 | // FIXME: Handle the case in which we're assigning over | |
671 | // a projection (`foo.bar`). | |
0531ce1d | 672 | self.kill_borrows_on_local(sets, local); |
ff7c6d11 XL |
673 | } |
674 | } | |
3b2f2976 XL |
675 | } |
676 | } | |
677 | ||
3b2f2976 XL |
678 | mir::StatementKind::SetDiscriminant { .. } | |
679 | mir::StatementKind::StorageLive(..) | | |
3b2f2976 | 680 | mir::StatementKind::Validate(..) | |
0531ce1d | 681 | mir::StatementKind::UserAssertTy(..) | |
3b2f2976 XL |
682 | mir::StatementKind::Nop => {} |
683 | ||
684 | } | |
ff7c6d11 | 685 | } |
abe05a73 | 686 | |
0531ce1d XL |
687 | fn before_terminator_effect(&self, |
688 | sets: &mut BlockSets<ReserveOrActivateIndex>, | |
689 | location: Location) { | |
690 | debug!("Borrows::before_terminator_effect sets: {:?} location: {:?}", sets, location); | |
691 | self.kill_loans_out_of_scope_at_location(sets, location); | |
ff7c6d11 XL |
692 | } |
693 | ||
0531ce1d XL |
694 | fn terminator_effect(&self, sets: &mut BlockSets<ReserveOrActivateIndex>, location: Location) { |
695 | debug!("Borrows::terminator_effect sets: {:?} location: {:?}", sets, location); | |
696 | ||
ff7c6d11 XL |
697 | let block = &self.mir.basic_blocks().get(location.block).unwrap_or_else(|| { |
698 | panic!("could not find block at location {:?}", location); | |
699 | }); | |
700 | ||
ff7c6d11 | 701 | let term = block.terminator(); |
0531ce1d XL |
702 | self.perform_activations_at_location(sets, location); |
703 | self.kill_loans_out_of_scope_at_location(sets, location); | |
704 | ||
ff7c6d11 XL |
705 | |
706 | match term.kind { | |
707 | mir::TerminatorKind::Resume | | |
708 | mir::TerminatorKind::Return | | |
709 | mir::TerminatorKind::GeneratorDrop => { | |
710 | // When we return from the function, then all `ReScope`-style regions | |
711 | // are guaranteed to have ended. | |
712 | // Normally, there would be `EndRegion` statements that come before, | |
713 | // and hence most of these loans will already be dead -- but, in some cases | |
714 | // like unwind paths, we do not always emit `EndRegion` statements, so we | |
715 | // add some kills here as a "backup" and to avoid spurious error messages. | |
716 | for (borrow_index, borrow_data) in self.borrows.iter_enumerated() { | |
717 | if let ReScope(scope) = borrow_data.region { | |
718 | // Check that the scope is not actually a scope from a function that is | |
719 | // a parent of our closure. Note that the CallSite scope itself is | |
720 | // *outside* of the closure, for some weird reason. | |
721 | if let Some(root_scope) = self.root_scope { | |
722 | if *scope != root_scope && | |
723 | self.scope_tree.is_subscope_of(*scope, root_scope) | |
724 | { | |
725 | sets.kill(&ReserveOrActivateIndex::reserved(borrow_index)); | |
0531ce1d | 726 | sets.kill(&ReserveOrActivateIndex::active(borrow_index)); |
ff7c6d11 XL |
727 | } |
728 | } | |
729 | } | |
730 | } | |
731 | } | |
732 | mir::TerminatorKind::Abort | | |
733 | mir::TerminatorKind::SwitchInt {..} | | |
734 | mir::TerminatorKind::Drop {..} | | |
735 | mir::TerminatorKind::DropAndReplace {..} | | |
736 | mir::TerminatorKind::Call {..} | | |
737 | mir::TerminatorKind::Assert {..} | | |
738 | mir::TerminatorKind::Yield {..} | | |
739 | mir::TerminatorKind::Goto {..} | | |
740 | mir::TerminatorKind::FalseEdges {..} | | |
2c00a5a8 | 741 | mir::TerminatorKind::FalseUnwind {..} | |
ff7c6d11 XL |
742 | mir::TerminatorKind::Unreachable => {} |
743 | } | |
744 | } | |
ff7c6d11 XL |
745 | |
746 | fn propagate_call_return(&self, | |
747 | _in_out: &mut IdxSet<ReserveOrActivateIndex>, | |
748 | _call_bb: mir::BasicBlock, | |
749 | _dest_bb: mir::BasicBlock, | |
750 | _dest_place: &mir::Place) { | |
751 | // there are no effects on borrows from method call return... | |
752 | // | |
753 | // ... but if overwriting a place can affect flow state, then | |
754 | // latter is not true; see NOTE on Assign case in | |
755 | // statement_effect_on_borrows. | |
756 | } | |
757 | } | |
758 | ||
0531ce1d | 759 | impl<'a, 'gcx, 'tcx> BitwiseOperator for Borrows<'a, 'gcx, 'tcx> { |
ff7c6d11 XL |
760 | #[inline] |
761 | fn join(&self, pred1: usize, pred2: usize) -> usize { | |
762 | pred1 | pred2 // union effects of preds when computing reservations | |
3b2f2976 XL |
763 | } |
764 | } | |
765 | ||
0531ce1d | 766 | impl<'a, 'gcx, 'tcx> InitialFlow for Borrows<'a, 'gcx, 'tcx> { |
3b2f2976 XL |
767 | #[inline] |
768 | fn bottom_value() -> bool { | |
0531ce1d | 769 | false // bottom = nothing is reserved or activated yet |
3b2f2976 XL |
770 | } |
771 | } | |
abe05a73 | 772 | |
ff7c6d11 | 773 | fn is_unsafe_place<'a, 'gcx: 'tcx, 'tcx: 'a>( |
abe05a73 XL |
774 | tcx: TyCtxt<'a, 'gcx, 'tcx>, |
775 | mir: &'a Mir<'tcx>, | |
ff7c6d11 | 776 | place: &mir::Place<'tcx> |
abe05a73 | 777 | ) -> bool { |
ff7c6d11 | 778 | use self::mir::Place::*; |
abe05a73 XL |
779 | use self::mir::ProjectionElem; |
780 | ||
ff7c6d11 | 781 | match *place { |
abe05a73 | 782 | Local(_) => false, |
0531ce1d | 783 | Static(ref static_) => tcx.is_static(static_.def_id) == Some(hir::Mutability::MutMutable), |
abe05a73 XL |
784 | Projection(ref proj) => { |
785 | match proj.elem { | |
786 | ProjectionElem::Field(..) | | |
787 | ProjectionElem::Downcast(..) | | |
788 | ProjectionElem::Subslice { .. } | | |
789 | ProjectionElem::ConstantIndex { .. } | | |
790 | ProjectionElem::Index(_) => { | |
ff7c6d11 | 791 | is_unsafe_place(tcx, mir, &proj.base) |
abe05a73 XL |
792 | } |
793 | ProjectionElem::Deref => { | |
794 | let ty = proj.base.ty(mir, tcx).to_ty(tcx); | |
795 | match ty.sty { | |
796 | ty::TyRawPtr(..) => true, | |
ff7c6d11 | 797 | _ => is_unsafe_place(tcx, mir, &proj.base), |
abe05a73 XL |
798 | } |
799 | } | |
800 | } | |
801 | } | |
802 | } | |
803 | } |