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