]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/dataflow/impls/mod.rs
Auto merge of #59627 - LooMaclin:issue_57128_improve_miri_error_reporting_in_check_in...
[rust.git] / src / librustc_mir / dataflow / impls / mod.rs
1 //! Dataflow analyses are built upon some interpretation of the
2 //! bitvectors attached to each basic block, represented via a
3 //! zero-sized structure.
4
5 use rustc::ty::TyCtxt;
6 use rustc::mir::{self, Mir, Location};
7 use rustc_data_structures::bit_set::{BitSet, BitSetOperator};
8 use rustc_data_structures::indexed_vec::Idx;
9
10 use super::MoveDataParamEnv;
11
12 use crate::util::elaborate_drops::DropFlagState;
13
14 use super::move_paths::{HasMoveData, MoveData, MovePathIndex, InitIndex, InitKind};
15 use super::{BitDenotation, BlockSets, InitialFlow};
16
17 use super::drop_flag_effects_for_function_entry;
18 use super::drop_flag_effects_for_location;
19 use super::on_lookup_result_bits;
20
21 mod storage_liveness;
22
23 pub use self::storage_liveness::*;
24
25 mod borrowed_locals;
26
27 pub use self::borrowed_locals::*;
28
29 pub(super) mod borrows;
30
31 /// `MaybeInitializedPlaces` tracks all places that might be
32 /// initialized upon reaching a particular point in the control flow
33 /// for a function.
34 ///
35 /// For example, in code like the following, we have corresponding
36 /// dataflow information shown in the right-hand comments.
37 ///
38 /// ```rust
39 /// struct S;
40 /// fn foo(pred: bool) {                       // maybe-init:
41 ///                                            // {}
42 ///     let a = S; let b = S; let c; let d;    // {a, b}
43 ///
44 ///     if pred {
45 ///         drop(a);                           // {   b}
46 ///         b = S;                             // {   b}
47 ///
48 ///     } else {
49 ///         drop(b);                           // {a}
50 ///         d = S;                             // {a,       d}
51 ///
52 ///     }                                      // {a, b,    d}
53 ///
54 ///     c = S;                                 // {a, b, c, d}
55 /// }
56 /// ```
57 ///
58 /// To determine whether a place *must* be initialized at a
59 /// particular control-flow point, one can take the set-difference
60 /// between this data and the data from `MaybeUninitializedPlaces` at the
61 /// corresponding control-flow point.
62 ///
63 /// Similarly, at a given `drop` statement, the set-intersection
64 /// between this data and `MaybeUninitializedPlaces` yields the set of
65 /// places that would require a dynamic drop-flag at that statement.
66 pub struct MaybeInitializedPlaces<'a, 'gcx: 'tcx, 'tcx: 'a> {
67     tcx: TyCtxt<'a, 'gcx, 'tcx>,
68     mir: &'a Mir<'tcx>,
69     mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
70 }
71
72 impl<'a, 'gcx: 'tcx, 'tcx> MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
73     pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
74                mir: &'a Mir<'tcx>,
75                mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
76                -> Self
77     {
78         MaybeInitializedPlaces { tcx: tcx, mir: mir, mdpe: mdpe }
79     }
80 }
81
82 impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
83     fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
84 }
85
86 /// `MaybeUninitializedPlaces` tracks all places that might be
87 /// uninitialized upon reaching a particular point in the control flow
88 /// for a function.
89 ///
90 /// For example, in code like the following, we have corresponding
91 /// dataflow information shown in the right-hand comments.
92 ///
93 /// ```rust
94 /// struct S;
95 /// fn foo(pred: bool) {                       // maybe-uninit:
96 ///                                            // {a, b, c, d}
97 ///     let a = S; let b = S; let c; let d;    // {      c, d}
98 ///
99 ///     if pred {
100 ///         drop(a);                           // {a,    c, d}
101 ///         b = S;                             // {a,    c, d}
102 ///
103 ///     } else {
104 ///         drop(b);                           // {   b, c, d}
105 ///         d = S;                             // {   b, c   }
106 ///
107 ///     }                                      // {a, b, c, d}
108 ///
109 ///     c = S;                                 // {a, b,    d}
110 /// }
111 /// ```
112 ///
113 /// To determine whether a place *must* be uninitialized at a
114 /// particular control-flow point, one can take the set-difference
115 /// between this data and the data from `MaybeInitializedPlaces` at the
116 /// corresponding control-flow point.
117 ///
118 /// Similarly, at a given `drop` statement, the set-intersection
119 /// between this data and `MaybeInitializedPlaces` yields the set of
120 /// places that would require a dynamic drop-flag at that statement.
121 pub struct MaybeUninitializedPlaces<'a, 'gcx: 'tcx, 'tcx: 'a> {
122     tcx: TyCtxt<'a, 'gcx, 'tcx>,
123     mir: &'a Mir<'tcx>,
124     mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
125 }
126
127 impl<'a, 'gcx, 'tcx> MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
128     pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
129                mir: &'a Mir<'tcx>,
130                mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
131                -> Self
132     {
133         MaybeUninitializedPlaces { tcx: tcx, mir: mir, mdpe: mdpe }
134     }
135 }
136
137 impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
138     fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
139 }
140
141 /// `DefinitelyInitializedPlaces` tracks all places that are definitely
142 /// initialized upon reaching a particular point in the control flow
143 /// for a function.
144 ///
145 /// For example, in code like the following, we have corresponding
146 /// dataflow information shown in the right-hand comments.
147 ///
148 /// ```rust
149 /// struct S;
150 /// fn foo(pred: bool) {                       // definite-init:
151 ///                                            // {          }
152 ///     let a = S; let b = S; let c; let d;    // {a, b      }
153 ///
154 ///     if pred {
155 ///         drop(a);                           // {   b,     }
156 ///         b = S;                             // {   b,     }
157 ///
158 ///     } else {
159 ///         drop(b);                           // {a,        }
160 ///         d = S;                             // {a,       d}
161 ///
162 ///     }                                      // {          }
163 ///
164 ///     c = S;                                 // {       c  }
165 /// }
166 /// ```
167 ///
168 /// To determine whether a place *may* be uninitialized at a
169 /// particular control-flow point, one can take the set-complement
170 /// of this data.
171 ///
172 /// Similarly, at a given `drop` statement, the set-difference between
173 /// this data and `MaybeInitializedPlaces` yields the set of places
174 /// that would require a dynamic drop-flag at that statement.
175 pub struct DefinitelyInitializedPlaces<'a, 'gcx: 'tcx, 'tcx: 'a> {
176     tcx: TyCtxt<'a, 'gcx, 'tcx>,
177     mir: &'a Mir<'tcx>,
178     mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
179 }
180
181 impl<'a, 'gcx, 'tcx: 'a> DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
182     pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
183                mir: &'a Mir<'tcx>,
184                mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
185                -> Self
186     {
187         DefinitelyInitializedPlaces { tcx: tcx, mir: mir, mdpe: mdpe }
188     }
189 }
190
191 impl<'a, 'gcx, 'tcx: 'a> HasMoveData<'tcx> for DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
192     fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
193 }
194
195 /// `EverInitializedPlaces` tracks all places that might have ever been
196 /// initialized upon reaching a particular point in the control flow
197 /// for a function, without an intervening `Storage Dead`.
198 ///
199 /// This dataflow is used to determine if an immutable local variable may
200 /// be assigned to.
201 ///
202 /// For example, in code like the following, we have corresponding
203 /// dataflow information shown in the right-hand comments.
204 ///
205 /// ```rust
206 /// struct S;
207 /// fn foo(pred: bool) {                       // ever-init:
208 ///                                            // {          }
209 ///     let a = S; let b = S; let c; let d;    // {a, b      }
210 ///
211 ///     if pred {
212 ///         drop(a);                           // {a, b,     }
213 ///         b = S;                             // {a, b,     }
214 ///
215 ///     } else {
216 ///         drop(b);                           // {a, b,      }
217 ///         d = S;                             // {a, b,    d }
218 ///
219 ///     }                                      // {a, b,    d }
220 ///
221 ///     c = S;                                 // {a, b, c, d }
222 /// }
223 /// ```
224 pub struct EverInitializedPlaces<'a, 'gcx: 'tcx, 'tcx: 'a> {
225     tcx: TyCtxt<'a, 'gcx, 'tcx>,
226     mir: &'a Mir<'tcx>,
227     mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
228 }
229
230 impl<'a, 'gcx: 'tcx, 'tcx: 'a> EverInitializedPlaces<'a, 'gcx, 'tcx> {
231     pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
232                mir: &'a Mir<'tcx>,
233                mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
234                -> Self
235     {
236         EverInitializedPlaces { tcx: tcx, mir: mir, mdpe: mdpe }
237     }
238 }
239
240 impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'a, 'gcx, 'tcx> {
241     fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
242 }
243
244
245 impl<'a, 'gcx, 'tcx> MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
246     fn update_bits(sets: &mut BlockSets<'_, MovePathIndex>, path: MovePathIndex,
247                    state: DropFlagState)
248     {
249         match state {
250             DropFlagState::Absent => sets.kill(path),
251             DropFlagState::Present => sets.gen(path),
252         }
253     }
254 }
255
256 impl<'a, 'gcx, 'tcx> MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
257     fn update_bits(sets: &mut BlockSets<'_, MovePathIndex>, path: MovePathIndex,
258                    state: DropFlagState)
259     {
260         match state {
261             DropFlagState::Absent => sets.gen(path),
262             DropFlagState::Present => sets.kill(path),
263         }
264     }
265 }
266
267 impl<'a, 'gcx, 'tcx> DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
268     fn update_bits(sets: &mut BlockSets<'_, MovePathIndex>, path: MovePathIndex,
269                    state: DropFlagState)
270     {
271         match state {
272             DropFlagState::Absent => sets.kill(path),
273             DropFlagState::Present => sets.gen(path),
274         }
275     }
276 }
277
278 impl<'a, 'gcx, 'tcx> BitDenotation<'tcx> for MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
279     type Idx = MovePathIndex;
280     fn name() -> &'static str { "maybe_init" }
281     fn bits_per_block(&self) -> usize {
282         self.move_data().move_paths.len()
283     }
284
285     fn start_block_effect(&self, entry_set: &mut BitSet<MovePathIndex>) {
286         drop_flag_effects_for_function_entry(
287             self.tcx, self.mir, self.mdpe,
288             |path, s| {
289                 assert!(s == DropFlagState::Present);
290                 entry_set.insert(path);
291             });
292     }
293
294     fn statement_effect(&self,
295                         sets: &mut BlockSets<'_, MovePathIndex>,
296                         location: Location)
297     {
298         drop_flag_effects_for_location(
299             self.tcx, self.mir, self.mdpe,
300             location,
301             |path, s| Self::update_bits(sets, path, s)
302         )
303     }
304
305     fn terminator_effect(&self,
306                          sets: &mut BlockSets<'_, MovePathIndex>,
307                          location: Location)
308     {
309         drop_flag_effects_for_location(
310             self.tcx, self.mir, self.mdpe,
311             location,
312             |path, s| Self::update_bits(sets, path, s)
313         )
314     }
315
316     fn propagate_call_return(
317         &self,
318         in_out: &mut BitSet<MovePathIndex>,
319         _call_bb: mir::BasicBlock,
320         _dest_bb: mir::BasicBlock,
321         dest_place: &mir::Place<'tcx>,
322     ) {
323         // when a call returns successfully, that means we need to set
324         // the bits for that dest_place to 1 (initialized).
325         on_lookup_result_bits(self.tcx, self.mir, self.move_data(),
326                               self.move_data().rev_lookup.find(dest_place),
327                               |mpi| { in_out.insert(mpi); });
328     }
329 }
330
331 impl<'a, 'gcx, 'tcx> BitDenotation<'tcx> for MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
332     type Idx = MovePathIndex;
333     fn name() -> &'static str { "maybe_uninit" }
334     fn bits_per_block(&self) -> usize {
335         self.move_data().move_paths.len()
336     }
337
338     // sets on_entry bits for Arg places
339     fn start_block_effect(&self, entry_set: &mut BitSet<MovePathIndex>) {
340         // set all bits to 1 (uninit) before gathering counterevidence
341         assert!(self.bits_per_block() == entry_set.domain_size());
342         entry_set.insert_all();
343
344         drop_flag_effects_for_function_entry(
345             self.tcx, self.mir, self.mdpe,
346             |path, s| {
347                 assert!(s == DropFlagState::Present);
348                 entry_set.remove(path);
349             });
350     }
351
352     fn statement_effect(&self,
353                         sets: &mut BlockSets<'_, MovePathIndex>,
354                         location: Location)
355     {
356         drop_flag_effects_for_location(
357             self.tcx, self.mir, self.mdpe,
358             location,
359             |path, s| Self::update_bits(sets, path, s)
360         )
361     }
362
363     fn terminator_effect(&self,
364                          sets: &mut BlockSets<'_, MovePathIndex>,
365                          location: Location)
366     {
367         drop_flag_effects_for_location(
368             self.tcx, self.mir, self.mdpe,
369             location,
370             |path, s| Self::update_bits(sets, path, s)
371         )
372     }
373
374     fn propagate_call_return(
375         &self,
376         in_out: &mut BitSet<MovePathIndex>,
377         _call_bb: mir::BasicBlock,
378         _dest_bb: mir::BasicBlock,
379         dest_place: &mir::Place<'tcx>,
380     ) {
381         // when a call returns successfully, that means we need to set
382         // the bits for that dest_place to 0 (initialized).
383         on_lookup_result_bits(self.tcx, self.mir, self.move_data(),
384                               self.move_data().rev_lookup.find(dest_place),
385                               |mpi| { in_out.remove(mpi); });
386     }
387 }
388
389 impl<'a, 'gcx, 'tcx> BitDenotation<'tcx> for DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
390     type Idx = MovePathIndex;
391     fn name() -> &'static str { "definite_init" }
392     fn bits_per_block(&self) -> usize {
393         self.move_data().move_paths.len()
394     }
395
396     // sets on_entry bits for Arg places
397     fn start_block_effect(&self, entry_set: &mut BitSet<MovePathIndex>) {
398         entry_set.clear();
399
400         drop_flag_effects_for_function_entry(
401             self.tcx, self.mir, self.mdpe,
402             |path, s| {
403                 assert!(s == DropFlagState::Present);
404                 entry_set.insert(path);
405             });
406     }
407
408     fn statement_effect(&self,
409                         sets: &mut BlockSets<'_, MovePathIndex>,
410                         location: Location)
411     {
412         drop_flag_effects_for_location(
413             self.tcx, self.mir, self.mdpe,
414             location,
415             |path, s| Self::update_bits(sets, path, s)
416         )
417     }
418
419     fn terminator_effect(&self,
420                          sets: &mut BlockSets<'_, MovePathIndex>,
421                          location: Location)
422     {
423         drop_flag_effects_for_location(
424             self.tcx, self.mir, self.mdpe,
425             location,
426             |path, s| Self::update_bits(sets, path, s)
427         )
428     }
429
430     fn propagate_call_return(
431         &self,
432         in_out: &mut BitSet<MovePathIndex>,
433         _call_bb: mir::BasicBlock,
434         _dest_bb: mir::BasicBlock,
435         dest_place: &mir::Place<'tcx>,
436     ) {
437         // when a call returns successfully, that means we need to set
438         // the bits for that dest_place to 1 (initialized).
439         on_lookup_result_bits(self.tcx, self.mir, self.move_data(),
440                               self.move_data().rev_lookup.find(dest_place),
441                               |mpi| { in_out.insert(mpi); });
442     }
443 }
444
445 impl<'a, 'gcx, 'tcx> BitDenotation<'tcx> for EverInitializedPlaces<'a, 'gcx, 'tcx> {
446     type Idx = InitIndex;
447     fn name() -> &'static str { "ever_init" }
448     fn bits_per_block(&self) -> usize {
449         self.move_data().inits.len()
450     }
451
452     fn start_block_effect(&self, entry_set: &mut BitSet<InitIndex>) {
453         for arg_init in 0..self.mir.arg_count {
454             entry_set.insert(InitIndex::new(arg_init));
455         }
456     }
457
458     fn statement_effect(&self,
459                         sets: &mut BlockSets<'_, InitIndex>,
460                         location: Location) {
461         let (_, mir, move_data) = (self.tcx, self.mir, self.move_data());
462         let stmt = &mir[location.block].statements[location.statement_index];
463         let init_path_map = &move_data.init_path_map;
464         let init_loc_map = &move_data.init_loc_map;
465         let rev_lookup = &move_data.rev_lookup;
466
467         debug!("statement {:?} at loc {:?} initializes move_indexes {:?}",
468                stmt, location, &init_loc_map[location]);
469         sets.gen_all(&init_loc_map[location]);
470
471         match stmt.kind {
472             mir::StatementKind::StorageDead(local) => {
473                 // End inits for StorageDead, so that an immutable variable can
474                 // be reinitialized on the next iteration of the loop.
475                 let move_path_index = rev_lookup.find_local(local);
476                 debug!("stmt {:?} at loc {:?} clears the ever initialized status of {:?}",
477                         stmt, location, &init_path_map[move_path_index]);
478                 sets.kill_all(&init_path_map[move_path_index]);
479             }
480             _ => {}
481         }
482     }
483
484     fn terminator_effect(&self,
485                          sets: &mut BlockSets<'_, InitIndex>,
486                          location: Location)
487     {
488         let (mir, move_data) = (self.mir, self.move_data());
489         let term = mir[location.block].terminator();
490         let init_loc_map = &move_data.init_loc_map;
491         debug!("terminator {:?} at loc {:?} initializes move_indexes {:?}",
492                term, location, &init_loc_map[location]);
493         sets.gen_all(
494             init_loc_map[location].iter().filter(|init_index| {
495                 move_data.inits[**init_index].kind != InitKind::NonPanicPathOnly
496             })
497         );
498     }
499
500     fn propagate_call_return(
501         &self,
502         in_out: &mut BitSet<InitIndex>,
503         call_bb: mir::BasicBlock,
504         _dest_bb: mir::BasicBlock,
505         _dest_place: &mir::Place<'tcx>,
506     ) {
507         let move_data = self.move_data();
508         let bits_per_block = self.bits_per_block();
509         let init_loc_map = &move_data.init_loc_map;
510
511         let call_loc = Location {
512             block: call_bb,
513             statement_index: self.mir[call_bb].statements.len(),
514         };
515         for init_index in &init_loc_map[call_loc] {
516             assert!(init_index.index() < bits_per_block);
517             in_out.insert(*init_index);
518         }
519     }
520 }
521
522 impl<'a, 'gcx, 'tcx> BitSetOperator for MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
523     #[inline]
524     fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
525         inout_set.union(in_set) // "maybe" means we union effects of both preds
526     }
527 }
528
529 impl<'a, 'gcx, 'tcx> BitSetOperator for MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
530     #[inline]
531     fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
532         inout_set.union(in_set) // "maybe" means we union effects of both preds
533     }
534 }
535
536 impl<'a, 'gcx, 'tcx> BitSetOperator for DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
537     #[inline]
538     fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
539         inout_set.intersect(in_set) // "definitely" means we intersect effects of both preds
540     }
541 }
542
543 impl<'a, 'gcx, 'tcx> BitSetOperator for EverInitializedPlaces<'a, 'gcx, 'tcx> {
544     #[inline]
545     fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
546         inout_set.union(in_set) // inits from both preds are in scope
547     }
548 }
549
550 // The way that dataflow fixed point iteration works, you want to
551 // start at bottom and work your way to a fixed point. Control-flow
552 // merges will apply the `join` operator to each block entry's current
553 // state (which starts at that bottom value).
554 //
555 // This means, for propagation across the graph, that you either want
556 // to start at all-zeroes and then use Union as your merge when
557 // propagating, or you start at all-ones and then use Intersect as
558 // your merge when propagating.
559
560 impl<'a, 'gcx, 'tcx> InitialFlow for MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
561     #[inline]
562     fn bottom_value() -> bool {
563         false // bottom = uninitialized
564     }
565 }
566
567 impl<'a, 'gcx, 'tcx> InitialFlow for MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
568     #[inline]
569     fn bottom_value() -> bool {
570         false // bottom = initialized (start_block_effect counters this at outset)
571     }
572 }
573
574 impl<'a, 'gcx, 'tcx> InitialFlow for DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
575     #[inline]
576     fn bottom_value() -> bool {
577         true // bottom = initialized (start_block_effect counters this at outset)
578     }
579 }
580
581 impl<'a, 'gcx, 'tcx> InitialFlow for EverInitializedPlaces<'a, 'gcx, 'tcx> {
582     #[inline]
583     fn bottom_value() -> bool {
584         false // bottom = no initialized variables by default
585     }
586 }