1 // Copyright 2012-2016 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.
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.
11 //! Dataflow analyses are built upon some interpretation of the
12 //! bitvectors attached to each basic block, represented via a
13 //! zero-sized structure.
15 use rustc::ty::TyCtxt;
16 use rustc::mir::{self, Mir, Location};
17 use rustc_data_structures::bit_set::{BitSet, BitSetOperator};
18 use rustc_data_structures::indexed_vec::Idx;
20 use super::MoveDataParamEnv;
22 use util::elaborate_drops::DropFlagState;
24 use super::move_paths::{HasMoveData, MoveData, MovePathIndex, InitIndex};
25 use super::move_paths::{LookupResult, InitKind};
26 use super::{BitDenotation, BlockSets, InitialFlow};
28 use super::drop_flag_effects_for_function_entry;
29 use super::drop_flag_effects_for_location;
30 use super::on_lookup_result_bits;
34 pub use self::storage_liveness::*;
38 pub use self::borrowed_locals::*;
40 pub(super) mod borrows;
42 /// `MaybeInitializedPlaces` tracks all places that might be
43 /// initialized upon reaching a particular point in the control flow
46 /// For example, in code like the following, we have corresponding
47 /// dataflow information shown in the right-hand comments.
51 /// fn foo(pred: bool) { // maybe-init:
53 /// let a = S; let b = S; let c; let d; // {a, b}
65 /// c = S; // {a, b, c, d}
69 /// To determine whether a place *must* be initialized at a
70 /// particular control-flow point, one can take the set-difference
71 /// between this data and the data from `MaybeUninitializedPlaces` at the
72 /// corresponding control-flow point.
74 /// Similarly, at a given `drop` statement, the set-intersection
75 /// between this data and `MaybeUninitializedPlaces` yields the set of
76 /// places that would require a dynamic drop-flag at that statement.
77 pub struct MaybeInitializedPlaces<'a, 'gcx: 'tcx, 'tcx: 'a> {
78 tcx: TyCtxt<'a, 'gcx, 'tcx>,
80 mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
83 impl<'a, 'gcx: 'tcx, 'tcx> MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
84 pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
86 mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
89 MaybeInitializedPlaces { tcx: tcx, mir: mir, mdpe: mdpe }
93 impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
94 fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
97 /// `MaybeUninitializedPlaces` tracks all places that might be
98 /// uninitialized upon reaching a particular point in the control flow
101 /// For example, in code like the following, we have corresponding
102 /// dataflow information shown in the right-hand comments.
106 /// fn foo(pred: bool) { // maybe-uninit:
108 /// let a = S; let b = S; let c; let d; // { c, d}
111 /// drop(a); // {a, c, d}
112 /// b = S; // {a, c, d}
115 /// drop(b); // { b, c, d}
116 /// d = S; // { b, c }
118 /// } // {a, b, c, d}
120 /// c = S; // {a, b, d}
124 /// To determine whether a place *must* be uninitialized at a
125 /// particular control-flow point, one can take the set-difference
126 /// between this data and the data from `MaybeInitializedPlaces` at the
127 /// corresponding control-flow point.
129 /// Similarly, at a given `drop` statement, the set-intersection
130 /// between this data and `MaybeInitializedPlaces` yields the set of
131 /// places that would require a dynamic drop-flag at that statement.
132 pub struct MaybeUninitializedPlaces<'a, 'gcx: 'tcx, 'tcx: 'a> {
133 tcx: TyCtxt<'a, 'gcx, 'tcx>,
135 mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
138 impl<'a, 'gcx, 'tcx> MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
139 pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
141 mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
144 MaybeUninitializedPlaces { tcx: tcx, mir: mir, mdpe: mdpe }
148 impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
149 fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
152 /// `DefinitelyInitializedPlaces` tracks all places that are definitely
153 /// initialized upon reaching a particular point in the control flow
156 /// FIXME: Note that once flow-analysis is complete, this should be
157 /// the set-complement of MaybeUninitializedPlaces; thus we can get rid
158 /// of one or the other of these two. I'm inclined to get rid of
159 /// MaybeUninitializedPlaces, simply because the sets will tend to be
160 /// smaller in this analysis and thus easier for humans to process
163 /// For example, in code like the following, we have corresponding
164 /// dataflow information shown in the right-hand comments.
168 /// fn foo(pred: bool) { // definite-init:
170 /// let a = S; let b = S; let c; let d; // {a, b }
173 /// drop(a); // { b, }
177 /// drop(b); // {a, }
186 /// To determine whether a place *may* be uninitialized at a
187 /// particular control-flow point, one can take the set-complement
190 /// Similarly, at a given `drop` statement, the set-difference between
191 /// this data and `MaybeInitializedPlaces` yields the set of places
192 /// that would require a dynamic drop-flag at that statement.
193 pub struct DefinitelyInitializedPlaces<'a, 'gcx: 'tcx, 'tcx: 'a> {
194 tcx: TyCtxt<'a, 'gcx, 'tcx>,
196 mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
199 impl<'a, 'gcx, 'tcx: 'a> DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
200 pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
202 mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
205 DefinitelyInitializedPlaces { tcx: tcx, mir: mir, mdpe: mdpe }
209 impl<'a, 'gcx, 'tcx: 'a> HasMoveData<'tcx> for DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
210 fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
213 /// `EverInitializedPlaces` tracks all places that might have ever been
214 /// initialized upon reaching a particular point in the control flow
215 /// for a function, without an intervening `Storage Dead`.
217 /// This dataflow is used to determine if an immutable local variable may
220 /// For example, in code like the following, we have corresponding
221 /// dataflow information shown in the right-hand comments.
225 /// fn foo(pred: bool) { // ever-init:
227 /// let a = S; let b = S; let c; let d; // {a, b }
230 /// drop(a); // {a, b, }
231 /// b = S; // {a, b, }
234 /// drop(b); // {a, b, }
235 /// d = S; // {a, b, d }
239 /// c = S; // {a, b, c, d }
242 pub struct EverInitializedPlaces<'a, 'gcx: 'tcx, 'tcx: 'a> {
243 tcx: TyCtxt<'a, 'gcx, 'tcx>,
245 mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
248 impl<'a, 'gcx: 'tcx, 'tcx: 'a> EverInitializedPlaces<'a, 'gcx, 'tcx> {
249 pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
251 mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
254 EverInitializedPlaces { tcx: tcx, mir: mir, mdpe: mdpe }
258 impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'a, 'gcx, 'tcx> {
259 fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
263 impl<'a, 'gcx, 'tcx> MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
264 fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
265 state: DropFlagState)
268 DropFlagState::Absent => sets.kill(path),
269 DropFlagState::Present => sets.gen(path),
274 impl<'a, 'gcx, 'tcx> MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
275 fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
276 state: DropFlagState)
279 DropFlagState::Absent => sets.gen(path),
280 DropFlagState::Present => sets.kill(path),
285 impl<'a, 'gcx, 'tcx> DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
286 fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
287 state: DropFlagState)
290 DropFlagState::Absent => sets.kill(path),
291 DropFlagState::Present => sets.gen(path),
296 impl<'a, 'gcx, 'tcx> BitDenotation for MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
297 type Idx = MovePathIndex;
298 fn name() -> &'static str { "maybe_init" }
299 fn bits_per_block(&self) -> usize {
300 self.move_data().move_paths.len()
303 fn start_block_effect(&self, entry_set: &mut BitSet<MovePathIndex>) {
304 drop_flag_effects_for_function_entry(
305 self.tcx, self.mir, self.mdpe,
307 assert!(s == DropFlagState::Present);
308 entry_set.insert(path);
312 fn statement_effect(&self,
313 sets: &mut BlockSets<MovePathIndex>,
316 drop_flag_effects_for_location(
317 self.tcx, self.mir, self.mdpe,
319 |path, s| Self::update_bits(sets, path, s)
323 fn terminator_effect(&self,
324 sets: &mut BlockSets<MovePathIndex>,
327 drop_flag_effects_for_location(
328 self.tcx, self.mir, self.mdpe,
330 |path, s| Self::update_bits(sets, path, s)
334 fn propagate_call_return(&self,
335 in_out: &mut BitSet<MovePathIndex>,
336 _call_bb: mir::BasicBlock,
337 _dest_bb: mir::BasicBlock,
338 dest_place: &mir::Place) {
339 // when a call returns successfully, that means we need to set
340 // the bits for that dest_place to 1 (initialized).
341 on_lookup_result_bits(self.tcx, self.mir, self.move_data(),
342 self.move_data().rev_lookup.find(dest_place),
343 |mpi| { in_out.insert(mpi); });
347 impl<'a, 'gcx, 'tcx> BitDenotation for MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
348 type Idx = MovePathIndex;
349 fn name() -> &'static str { "maybe_uninit" }
350 fn bits_per_block(&self) -> usize {
351 self.move_data().move_paths.len()
354 // sets on_entry bits for Arg places
355 fn start_block_effect(&self, entry_set: &mut BitSet<MovePathIndex>) {
356 // set all bits to 1 (uninit) before gathering counterevidence
357 assert!(self.bits_per_block() == entry_set.domain_size());
358 entry_set.insert_all();
360 drop_flag_effects_for_function_entry(
361 self.tcx, self.mir, self.mdpe,
363 assert!(s == DropFlagState::Present);
364 entry_set.remove(path);
368 fn statement_effect(&self,
369 sets: &mut BlockSets<MovePathIndex>,
372 drop_flag_effects_for_location(
373 self.tcx, self.mir, self.mdpe,
375 |path, s| Self::update_bits(sets, path, s)
379 fn terminator_effect(&self,
380 sets: &mut BlockSets<MovePathIndex>,
383 drop_flag_effects_for_location(
384 self.tcx, self.mir, self.mdpe,
386 |path, s| Self::update_bits(sets, path, s)
390 fn propagate_call_return(&self,
391 in_out: &mut BitSet<MovePathIndex>,
392 _call_bb: mir::BasicBlock,
393 _dest_bb: mir::BasicBlock,
394 dest_place: &mir::Place) {
395 // when a call returns successfully, that means we need to set
396 // the bits for that dest_place to 0 (initialized).
397 on_lookup_result_bits(self.tcx, self.mir, self.move_data(),
398 self.move_data().rev_lookup.find(dest_place),
399 |mpi| { in_out.remove(mpi); });
403 impl<'a, 'gcx, 'tcx> BitDenotation for DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
404 type Idx = MovePathIndex;
405 fn name() -> &'static str { "definite_init" }
406 fn bits_per_block(&self) -> usize {
407 self.move_data().move_paths.len()
410 // sets on_entry bits for Arg places
411 fn start_block_effect(&self, entry_set: &mut BitSet<MovePathIndex>) {
414 drop_flag_effects_for_function_entry(
415 self.tcx, self.mir, self.mdpe,
417 assert!(s == DropFlagState::Present);
418 entry_set.insert(path);
422 fn statement_effect(&self,
423 sets: &mut BlockSets<MovePathIndex>,
426 drop_flag_effects_for_location(
427 self.tcx, self.mir, self.mdpe,
429 |path, s| Self::update_bits(sets, path, s)
433 fn terminator_effect(&self,
434 sets: &mut BlockSets<MovePathIndex>,
437 drop_flag_effects_for_location(
438 self.tcx, self.mir, self.mdpe,
440 |path, s| Self::update_bits(sets, path, s)
444 fn propagate_call_return(&self,
445 in_out: &mut BitSet<MovePathIndex>,
446 _call_bb: mir::BasicBlock,
447 _dest_bb: mir::BasicBlock,
448 dest_place: &mir::Place) {
449 // when a call returns successfully, that means we need to set
450 // the bits for that dest_place to 1 (initialized).
451 on_lookup_result_bits(self.tcx, self.mir, self.move_data(),
452 self.move_data().rev_lookup.find(dest_place),
453 |mpi| { in_out.insert(mpi); });
457 impl<'a, 'gcx, 'tcx> BitDenotation for EverInitializedPlaces<'a, 'gcx, 'tcx> {
458 type Idx = InitIndex;
459 fn name() -> &'static str { "ever_init" }
460 fn bits_per_block(&self) -> usize {
461 self.move_data().inits.len()
464 fn start_block_effect(&self, entry_set: &mut BitSet<InitIndex>) {
465 for arg_init in 0..self.mir.arg_count {
466 entry_set.insert(InitIndex::new(arg_init));
470 fn statement_effect(&self,
471 sets: &mut BlockSets<InitIndex>,
472 location: Location) {
473 let (_, mir, move_data) = (self.tcx, self.mir, self.move_data());
474 let stmt = &mir[location.block].statements[location.statement_index];
475 let init_path_map = &move_data.init_path_map;
476 let init_loc_map = &move_data.init_loc_map;
477 let rev_lookup = &move_data.rev_lookup;
479 debug!("statement {:?} at loc {:?} initializes move_indexes {:?}",
480 stmt, location, &init_loc_map[location]);
481 sets.gen_all(&init_loc_map[location]);
484 mir::StatementKind::StorageDead(local) |
485 mir::StatementKind::StorageLive(local) => {
486 // End inits for StorageDead and StorageLive, so that an immutable
487 // variable can be reinitialized on the next iteration of the loop.
489 // FIXME(#46525): We *need* to do this for StorageLive as well as
490 // StorageDead, because lifetimes of match bindings with guards are
491 // weird - i.e., this code
497 // if { println!("a={}", a); false } => {}
503 // runs the guard twice, using the same binding for `a`, and only
504 // storagedeads after everything ends, so if we don't regard the
505 // storagelive as killing storage, we would have a multiple assignment
506 // to immutable data error.
507 if let LookupResult::Exact(mpi) = rev_lookup.find(&mir::Place::Local(local)) {
508 debug!("stmt {:?} at loc {:?} clears the ever initialized status of {:?}",
509 stmt, location, &init_path_map[mpi]);
510 sets.kill_all(&init_path_map[mpi]);
517 fn terminator_effect(&self,
518 sets: &mut BlockSets<InitIndex>,
521 let (mir, move_data) = (self.mir, self.move_data());
522 let term = mir[location.block].terminator();
523 let init_loc_map = &move_data.init_loc_map;
524 debug!("terminator {:?} at loc {:?} initializes move_indexes {:?}",
525 term, location, &init_loc_map[location]);
527 init_loc_map[location].iter().filter(|init_index| {
528 move_data.inits[**init_index].kind != InitKind::NonPanicPathOnly
533 fn propagate_call_return(&self,
534 in_out: &mut BitSet<InitIndex>,
535 call_bb: mir::BasicBlock,
536 _dest_bb: mir::BasicBlock,
537 _dest_place: &mir::Place) {
538 let move_data = self.move_data();
539 let bits_per_block = self.bits_per_block();
540 let init_loc_map = &move_data.init_loc_map;
542 let call_loc = Location {
544 statement_index: self.mir[call_bb].statements.len(),
546 for init_index in &init_loc_map[call_loc] {
547 assert!(init_index.index() < bits_per_block);
548 in_out.insert(*init_index);
553 impl<'a, 'gcx, 'tcx> BitSetOperator for MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
555 fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
556 inout_set.union(in_set) // "maybe" means we union effects of both preds
560 impl<'a, 'gcx, 'tcx> BitSetOperator for MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
562 fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
563 inout_set.union(in_set) // "maybe" means we union effects of both preds
567 impl<'a, 'gcx, 'tcx> BitSetOperator for DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
569 fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
570 inout_set.intersect(in_set) // "definitely" means we intersect effects of both preds
574 impl<'a, 'gcx, 'tcx> BitSetOperator for EverInitializedPlaces<'a, 'gcx, 'tcx> {
576 fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
577 inout_set.union(in_set) // inits from both preds are in scope
581 // The way that dataflow fixed point iteration works, you want to
582 // start at bottom and work your way to a fixed point. Control-flow
583 // merges will apply the `join` operator to each block entry's current
584 // state (which starts at that bottom value).
586 // This means, for propagation across the graph, that you either want
587 // to start at all-zeroes and then use Union as your merge when
588 // propagating, or you start at all-ones and then use Intersect as
589 // your merge when propagating.
591 impl<'a, 'gcx, 'tcx> InitialFlow for MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
593 fn bottom_value() -> bool {
594 false // bottom = uninitialized
598 impl<'a, 'gcx, 'tcx> InitialFlow for MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
600 fn bottom_value() -> bool {
601 false // bottom = initialized (start_block_effect counters this at outset)
605 impl<'a, 'gcx, 'tcx> InitialFlow for DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
607 fn bottom_value() -> bool {
608 true // bottom = initialized (start_block_effect counters this at outset)
612 impl<'a, 'gcx, 'tcx> InitialFlow for EverInitializedPlaces<'a, 'gcx, 'tcx> {
614 fn bottom_value() -> bool {
615 false // bottom = no initialized variables by default