]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/dataflow/impls/mod.rs
Various minor/cosmetic improvements to code
[rust.git] / src / librustc_mir / dataflow / impls / mod.rs
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.
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 //! Dataflow analyses are built upon some interpretation of the
12 //! bitvectors attached to each basic block, represented via a
13 //! zero-sized structure.
14
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;
19
20 use super::MoveDataParamEnv;
21
22 use util::elaborate_drops::DropFlagState;
23
24 use super::move_paths::{HasMoveData, MoveData, MovePathIndex, InitIndex};
25 use super::move_paths::{LookupResult, InitKind};
26 use super::{BitDenotation, BlockSets, InitialFlow};
27
28 use super::drop_flag_effects_for_function_entry;
29 use super::drop_flag_effects_for_location;
30 use super::on_lookup_result_bits;
31
32 mod storage_liveness;
33
34 pub use self::storage_liveness::*;
35
36 mod borrowed_locals;
37
38 pub use self::borrowed_locals::*;
39
40 pub(super) mod borrows;
41
42 /// `MaybeInitializedPlaces` tracks all places that might be
43 /// initialized upon reaching a particular point in the control flow
44 /// for a function.
45 ///
46 /// For example, in code like the following, we have corresponding
47 /// dataflow information shown in the right-hand comments.
48 ///
49 /// ```rust
50 /// struct S;
51 /// fn foo(pred: bool) {                       // maybe-init:
52 ///                                            // {}
53 ///     let a = S; let b = S; let c; let d;    // {a, b}
54 ///
55 ///     if pred {
56 ///         drop(a);                           // {   b}
57 ///         b = S;                             // {   b}
58 ///
59 ///     } else {
60 ///         drop(b);                           // {a}
61 ///         d = S;                             // {a,       d}
62 ///
63 ///     }                                      // {a, b,    d}
64 ///
65 ///     c = S;                                 // {a, b, c, d}
66 /// }
67 /// ```
68 ///
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.
73 ///
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>,
79     mir: &'a Mir<'tcx>,
80     mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
81 }
82
83 impl<'a, 'gcx: 'tcx, 'tcx> MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
84     pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
85                mir: &'a Mir<'tcx>,
86                mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
87                -> Self
88     {
89         MaybeInitializedPlaces { tcx: tcx, mir: mir, mdpe: mdpe }
90     }
91 }
92
93 impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
94     fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
95 }
96
97 /// `MaybeUninitializedPlaces` tracks all places that might be
98 /// uninitialized upon reaching a particular point in the control flow
99 /// for a function.
100 ///
101 /// For example, in code like the following, we have corresponding
102 /// dataflow information shown in the right-hand comments.
103 ///
104 /// ```rust
105 /// struct S;
106 /// fn foo(pred: bool) {                       // maybe-uninit:
107 ///                                            // {a, b, c, d}
108 ///     let a = S; let b = S; let c; let d;    // {      c, d}
109 ///
110 ///     if pred {
111 ///         drop(a);                           // {a,    c, d}
112 ///         b = S;                             // {a,    c, d}
113 ///
114 ///     } else {
115 ///         drop(b);                           // {   b, c, d}
116 ///         d = S;                             // {   b, c   }
117 ///
118 ///     }                                      // {a, b, c, d}
119 ///
120 ///     c = S;                                 // {a, b,    d}
121 /// }
122 /// ```
123 ///
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.
128 ///
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>,
134     mir: &'a Mir<'tcx>,
135     mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
136 }
137
138 impl<'a, 'gcx, 'tcx> MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
139     pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
140                mir: &'a Mir<'tcx>,
141                mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
142                -> Self
143     {
144         MaybeUninitializedPlaces { tcx: tcx, mir: mir, mdpe: mdpe }
145     }
146 }
147
148 impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
149     fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
150 }
151
152 /// `DefinitelyInitializedPlaces` tracks all places that are definitely
153 /// initialized upon reaching a particular point in the control flow
154 /// for a function.
155 ///
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
161 /// when debugging.
162 ///
163 /// For example, in code like the following, we have corresponding
164 /// dataflow information shown in the right-hand comments.
165 ///
166 /// ```rust
167 /// struct S;
168 /// fn foo(pred: bool) {                       // definite-init:
169 ///                                            // {          }
170 ///     let a = S; let b = S; let c; let d;    // {a, b      }
171 ///
172 ///     if pred {
173 ///         drop(a);                           // {   b,     }
174 ///         b = S;                             // {   b,     }
175 ///
176 ///     } else {
177 ///         drop(b);                           // {a,        }
178 ///         d = S;                             // {a,       d}
179 ///
180 ///     }                                      // {          }
181 ///
182 ///     c = S;                                 // {       c  }
183 /// }
184 /// ```
185 ///
186 /// To determine whether a place *may* be uninitialized at a
187 /// particular control-flow point, one can take the set-complement
188 /// of this data.
189 ///
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>,
195     mir: &'a Mir<'tcx>,
196     mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
197 }
198
199 impl<'a, 'gcx, 'tcx: 'a> DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
200     pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
201                mir: &'a Mir<'tcx>,
202                mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
203                -> Self
204     {
205         DefinitelyInitializedPlaces { tcx: tcx, mir: mir, mdpe: mdpe }
206     }
207 }
208
209 impl<'a, 'gcx, 'tcx: 'a> HasMoveData<'tcx> for DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
210     fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
211 }
212
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`.
216 ///
217 /// This dataflow is used to determine if an immutable local variable may
218 /// be assigned to.
219 ///
220 /// For example, in code like the following, we have corresponding
221 /// dataflow information shown in the right-hand comments.
222 ///
223 /// ```rust
224 /// struct S;
225 /// fn foo(pred: bool) {                       // ever-init:
226 ///                                            // {          }
227 ///     let a = S; let b = S; let c; let d;    // {a, b      }
228 ///
229 ///     if pred {
230 ///         drop(a);                           // {a, b,     }
231 ///         b = S;                             // {a, b,     }
232 ///
233 ///     } else {
234 ///         drop(b);                           // {a, b,      }
235 ///         d = S;                             // {a, b,    d }
236 ///
237 ///     }                                      // {a, b,    d }
238 ///
239 ///     c = S;                                 // {a, b, c, d }
240 /// }
241 /// ```
242 pub struct EverInitializedPlaces<'a, 'gcx: 'tcx, 'tcx: 'a> {
243     tcx: TyCtxt<'a, 'gcx, 'tcx>,
244     mir: &'a Mir<'tcx>,
245     mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
246 }
247
248 impl<'a, 'gcx: 'tcx, 'tcx: 'a> EverInitializedPlaces<'a, 'gcx, 'tcx> {
249     pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
250                mir: &'a Mir<'tcx>,
251                mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
252                -> Self
253     {
254         EverInitializedPlaces { tcx: tcx, mir: mir, mdpe: mdpe }
255     }
256 }
257
258 impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'a, 'gcx, 'tcx> {
259     fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
260 }
261
262
263 impl<'a, 'gcx, 'tcx> MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
264     fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
265                    state: DropFlagState)
266     {
267         match state {
268             DropFlagState::Absent => sets.kill(path),
269             DropFlagState::Present => sets.gen(path),
270         }
271     }
272 }
273
274 impl<'a, 'gcx, 'tcx> MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
275     fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
276                    state: DropFlagState)
277     {
278         match state {
279             DropFlagState::Absent => sets.gen(path),
280             DropFlagState::Present => sets.kill(path),
281         }
282     }
283 }
284
285 impl<'a, 'gcx, 'tcx> DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
286     fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
287                    state: DropFlagState)
288     {
289         match state {
290             DropFlagState::Absent => sets.kill(path),
291             DropFlagState::Present => sets.gen(path),
292         }
293     }
294 }
295
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()
301     }
302
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,
306             |path, s| {
307                 assert!(s == DropFlagState::Present);
308                 entry_set.insert(path);
309             });
310     }
311
312     fn statement_effect(&self,
313                         sets: &mut BlockSets<MovePathIndex>,
314                         location: Location)
315     {
316         drop_flag_effects_for_location(
317             self.tcx, self.mir, self.mdpe,
318             location,
319             |path, s| Self::update_bits(sets, path, s)
320         )
321     }
322
323     fn terminator_effect(&self,
324                          sets: &mut BlockSets<MovePathIndex>,
325                          location: Location)
326     {
327         drop_flag_effects_for_location(
328             self.tcx, self.mir, self.mdpe,
329             location,
330             |path, s| Self::update_bits(sets, path, s)
331         )
332     }
333
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); });
344     }
345 }
346
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()
352     }
353
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();
359
360         drop_flag_effects_for_function_entry(
361             self.tcx, self.mir, self.mdpe,
362             |path, s| {
363                 assert!(s == DropFlagState::Present);
364                 entry_set.remove(path);
365             });
366     }
367
368     fn statement_effect(&self,
369                         sets: &mut BlockSets<MovePathIndex>,
370                         location: Location)
371     {
372         drop_flag_effects_for_location(
373             self.tcx, self.mir, self.mdpe,
374             location,
375             |path, s| Self::update_bits(sets, path, s)
376         )
377     }
378
379     fn terminator_effect(&self,
380                          sets: &mut BlockSets<MovePathIndex>,
381                          location: Location)
382     {
383         drop_flag_effects_for_location(
384             self.tcx, self.mir, self.mdpe,
385             location,
386             |path, s| Self::update_bits(sets, path, s)
387         )
388     }
389
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); });
400     }
401 }
402
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()
408     }
409
410     // sets on_entry bits for Arg places
411     fn start_block_effect(&self, entry_set: &mut BitSet<MovePathIndex>) {
412         entry_set.clear();
413
414         drop_flag_effects_for_function_entry(
415             self.tcx, self.mir, self.mdpe,
416             |path, s| {
417                 assert!(s == DropFlagState::Present);
418                 entry_set.insert(path);
419             });
420     }
421
422     fn statement_effect(&self,
423                         sets: &mut BlockSets<MovePathIndex>,
424                         location: Location)
425     {
426         drop_flag_effects_for_location(
427             self.tcx, self.mir, self.mdpe,
428             location,
429             |path, s| Self::update_bits(sets, path, s)
430         )
431     }
432
433     fn terminator_effect(&self,
434                          sets: &mut BlockSets<MovePathIndex>,
435                          location: Location)
436     {
437         drop_flag_effects_for_location(
438             self.tcx, self.mir, self.mdpe,
439             location,
440             |path, s| Self::update_bits(sets, path, s)
441         )
442     }
443
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); });
454     }
455 }
456
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()
462     }
463
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));
467         }
468     }
469
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;
478
479         debug!("statement {:?} at loc {:?} initializes move_indexes {:?}",
480                stmt, location, &init_loc_map[location]);
481         sets.gen_all(&init_loc_map[location]);
482
483         match stmt.kind {
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.
488                 //
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
492                 //
493                 // ```
494                 //     fn main() {
495                 //         match 0 {
496                 //             a | a
497                 //             if { println!("a={}", a); false } => {}
498                 //             _ => {}
499                 //         }
500                 //     }
501                 // ```
502                 //
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]);
511                 }
512             }
513             _ => {}
514         }
515     }
516
517     fn terminator_effect(&self,
518                          sets: &mut BlockSets<InitIndex>,
519                          location: Location)
520     {
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]);
526         sets.gen_all(
527             init_loc_map[location].iter().filter(|init_index| {
528                 move_data.inits[**init_index].kind != InitKind::NonPanicPathOnly
529             })
530         );
531     }
532
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;
541
542         let call_loc = Location {
543             block: call_bb,
544             statement_index: self.mir[call_bb].statements.len(),
545         };
546         for init_index in &init_loc_map[call_loc] {
547             assert!(init_index.index() < bits_per_block);
548             in_out.insert(*init_index);
549         }
550     }
551 }
552
553 impl<'a, 'gcx, 'tcx> BitSetOperator for MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
554     #[inline]
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
557     }
558 }
559
560 impl<'a, 'gcx, 'tcx> BitSetOperator for MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
561     #[inline]
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
564     }
565 }
566
567 impl<'a, 'gcx, 'tcx> BitSetOperator for DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
568     #[inline]
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
571     }
572 }
573
574 impl<'a, 'gcx, 'tcx> BitSetOperator for EverInitializedPlaces<'a, 'gcx, 'tcx> {
575     #[inline]
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
578     }
579 }
580
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).
585 //
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.
590
591 impl<'a, 'gcx, 'tcx> InitialFlow for MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
592     #[inline]
593     fn bottom_value() -> bool {
594         false // bottom = uninitialized
595     }
596 }
597
598 impl<'a, 'gcx, 'tcx> InitialFlow for MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
599     #[inline]
600     fn bottom_value() -> bool {
601         false // bottom = initialized (start_block_effect counters this at outset)
602     }
603 }
604
605 impl<'a, 'gcx, 'tcx> InitialFlow for DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
606     #[inline]
607     fn bottom_value() -> bool {
608         true // bottom = initialized (start_block_effect counters this at outset)
609     }
610 }
611
612 impl<'a, 'gcx, 'tcx> InitialFlow for EverInitializedPlaces<'a, 'gcx, 'tcx> {
613     #[inline]
614     fn bottom_value() -> bool {
615         false // bottom = no initialized variables by default
616     }
617 }