]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/dataflow/impls/mod.rs
Export dataflow impls by name
[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_index::bit_set::BitSet;
6 use rustc_index::vec::Idx;
7 use rustc_middle::mir::{self, Body, Location};
8 use rustc_middle::ty::{self, TyCtxt};
9 use rustc_target::abi::VariantIdx;
10
11 use super::MoveDataParamEnv;
12
13 use crate::util::elaborate_drops::DropFlagState;
14
15 use super::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex};
16 use super::{AnalysisDomain, BottomValue, GenKill, GenKillAnalysis};
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 use crate::dataflow::drop_flag_effects;
22
23 mod borrowed_locals;
24 pub(super) mod borrows;
25 mod liveness;
26 mod storage_liveness;
27
28 pub use self::borrowed_locals::{MaybeBorrowedLocals, MaybeMutBorrowedLocals};
29 pub use self::borrows::Borrows;
30 pub use self::liveness::MaybeLiveLocals;
31 pub use self::storage_liveness::{MaybeRequiresStorage, MaybeStorageLive};
32
33 /// `MaybeInitializedPlaces` tracks all places that might be
34 /// initialized upon reaching a particular point in the control flow
35 /// for a function.
36 ///
37 /// For example, in code like the following, we have corresponding
38 /// dataflow information shown in the right-hand comments.
39 ///
40 /// ```rust
41 /// struct S;
42 /// fn foo(pred: bool) {                       // maybe-init:
43 ///                                            // {}
44 ///     let a = S; let b = S; let c; let d;    // {a, b}
45 ///
46 ///     if pred {
47 ///         drop(a);                           // {   b}
48 ///         b = S;                             // {   b}
49 ///
50 ///     } else {
51 ///         drop(b);                           // {a}
52 ///         d = S;                             // {a,       d}
53 ///
54 ///     }                                      // {a, b,    d}
55 ///
56 ///     c = S;                                 // {a, b, c, d}
57 /// }
58 /// ```
59 ///
60 /// To determine whether a place *must* be initialized at a
61 /// particular control-flow point, one can take the set-difference
62 /// between this data and the data from `MaybeUninitializedPlaces` at the
63 /// corresponding control-flow point.
64 ///
65 /// Similarly, at a given `drop` statement, the set-intersection
66 /// between this data and `MaybeUninitializedPlaces` yields the set of
67 /// places that would require a dynamic drop-flag at that statement.
68 pub struct MaybeInitializedPlaces<'a, 'tcx> {
69     tcx: TyCtxt<'tcx>,
70     body: &'a Body<'tcx>,
71     mdpe: &'a MoveDataParamEnv<'tcx>,
72 }
73
74 impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
75     pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
76         MaybeInitializedPlaces { tcx, body, mdpe }
77     }
78 }
79
80 impl<'a, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'tcx> {
81     fn move_data(&self) -> &MoveData<'tcx> {
82         &self.mdpe.move_data
83     }
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, 'tcx> {
122     tcx: TyCtxt<'tcx>,
123     body: &'a Body<'tcx>,
124     mdpe: &'a MoveDataParamEnv<'tcx>,
125 }
126
127 impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
128     pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
129         MaybeUninitializedPlaces { tcx, body, mdpe }
130     }
131 }
132
133 impl<'a, 'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'a, 'tcx> {
134     fn move_data(&self) -> &MoveData<'tcx> {
135         &self.mdpe.move_data
136     }
137 }
138
139 /// `DefinitelyInitializedPlaces` tracks all places that are definitely
140 /// initialized upon reaching a particular point in the control flow
141 /// for a function.
142 ///
143 /// For example, in code like the following, we have corresponding
144 /// dataflow information shown in the right-hand comments.
145 ///
146 /// ```rust
147 /// struct S;
148 /// fn foo(pred: bool) {                       // definite-init:
149 ///                                            // {          }
150 ///     let a = S; let b = S; let c; let d;    // {a, b      }
151 ///
152 ///     if pred {
153 ///         drop(a);                           // {   b,     }
154 ///         b = S;                             // {   b,     }
155 ///
156 ///     } else {
157 ///         drop(b);                           // {a,        }
158 ///         d = S;                             // {a,       d}
159 ///
160 ///     }                                      // {          }
161 ///
162 ///     c = S;                                 // {       c  }
163 /// }
164 /// ```
165 ///
166 /// To determine whether a place *may* be uninitialized at a
167 /// particular control-flow point, one can take the set-complement
168 /// of this data.
169 ///
170 /// Similarly, at a given `drop` statement, the set-difference between
171 /// this data and `MaybeInitializedPlaces` yields the set of places
172 /// that would require a dynamic drop-flag at that statement.
173 pub struct DefinitelyInitializedPlaces<'a, 'tcx> {
174     tcx: TyCtxt<'tcx>,
175     body: &'a Body<'tcx>,
176     mdpe: &'a MoveDataParamEnv<'tcx>,
177 }
178
179 impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> {
180     pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
181         DefinitelyInitializedPlaces { tcx, body, mdpe }
182     }
183 }
184
185 impl<'a, 'tcx> HasMoveData<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> {
186     fn move_data(&self) -> &MoveData<'tcx> {
187         &self.mdpe.move_data
188     }
189 }
190
191 /// `EverInitializedPlaces` tracks all places that might have ever been
192 /// initialized upon reaching a particular point in the control flow
193 /// for a function, without an intervening `Storage Dead`.
194 ///
195 /// This dataflow is used to determine if an immutable local variable may
196 /// be assigned to.
197 ///
198 /// For example, in code like the following, we have corresponding
199 /// dataflow information shown in the right-hand comments.
200 ///
201 /// ```rust
202 /// struct S;
203 /// fn foo(pred: bool) {                       // ever-init:
204 ///                                            // {          }
205 ///     let a = S; let b = S; let c; let d;    // {a, b      }
206 ///
207 ///     if pred {
208 ///         drop(a);                           // {a, b,     }
209 ///         b = S;                             // {a, b,     }
210 ///
211 ///     } else {
212 ///         drop(b);                           // {a, b,      }
213 ///         d = S;                             // {a, b,    d }
214 ///
215 ///     }                                      // {a, b,    d }
216 ///
217 ///     c = S;                                 // {a, b, c, d }
218 /// }
219 /// ```
220 pub struct EverInitializedPlaces<'a, 'tcx> {
221     #[allow(dead_code)]
222     tcx: TyCtxt<'tcx>,
223     body: &'a Body<'tcx>,
224     mdpe: &'a MoveDataParamEnv<'tcx>,
225 }
226
227 impl<'a, 'tcx> EverInitializedPlaces<'a, 'tcx> {
228     pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
229         EverInitializedPlaces { tcx, body, mdpe }
230     }
231 }
232
233 impl<'a, 'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'a, 'tcx> {
234     fn move_data(&self) -> &MoveData<'tcx> {
235         &self.mdpe.move_data
236     }
237 }
238
239 impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
240     fn update_bits(
241         trans: &mut impl GenKill<MovePathIndex>,
242         path: MovePathIndex,
243         state: DropFlagState,
244     ) {
245         match state {
246             DropFlagState::Absent => trans.kill(path),
247             DropFlagState::Present => trans.gen(path),
248         }
249     }
250 }
251
252 impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
253     fn update_bits(
254         trans: &mut impl GenKill<MovePathIndex>,
255         path: MovePathIndex,
256         state: DropFlagState,
257     ) {
258         match state {
259             DropFlagState::Absent => trans.gen(path),
260             DropFlagState::Present => trans.kill(path),
261         }
262     }
263 }
264
265 impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> {
266     fn update_bits(
267         trans: &mut impl GenKill<MovePathIndex>,
268         path: MovePathIndex,
269         state: DropFlagState,
270     ) {
271         match state {
272             DropFlagState::Absent => trans.kill(path),
273             DropFlagState::Present => trans.gen(path),
274         }
275     }
276 }
277
278 impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
279     type Idx = MovePathIndex;
280
281     const NAME: &'static str = "maybe_init";
282
283     fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize {
284         self.move_data().move_paths.len()
285     }
286
287     fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) {
288         drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| {
289             assert!(s == DropFlagState::Present);
290             state.insert(path);
291         });
292     }
293
294     fn pretty_print_idx(&self, w: &mut impl std::io::Write, mpi: Self::Idx) -> std::io::Result<()> {
295         write!(w, "{}", self.move_data().move_paths[mpi])
296     }
297 }
298
299 impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
300     fn statement_effect(
301         &self,
302         trans: &mut impl GenKill<Self::Idx>,
303         _statement: &mir::Statement<'tcx>,
304         location: Location,
305     ) {
306         drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
307             Self::update_bits(trans, path, s)
308         })
309     }
310
311     fn terminator_effect(
312         &self,
313         trans: &mut impl GenKill<Self::Idx>,
314         _terminator: &mir::Terminator<'tcx>,
315         location: Location,
316     ) {
317         drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
318             Self::update_bits(trans, path, s)
319         })
320     }
321
322     fn call_return_effect(
323         &self,
324         trans: &mut impl GenKill<Self::Idx>,
325         _block: mir::BasicBlock,
326         _func: &mir::Operand<'tcx>,
327         _args: &[mir::Operand<'tcx>],
328         dest_place: mir::Place<'tcx>,
329     ) {
330         // when a call returns successfully, that means we need to set
331         // the bits for that dest_place to 1 (initialized).
332         on_lookup_result_bits(
333             self.tcx,
334             self.body,
335             self.move_data(),
336             self.move_data().rev_lookup.find(dest_place.as_ref()),
337             |mpi| {
338                 trans.gen(mpi);
339             },
340         );
341     }
342
343     fn discriminant_switch_effect(
344         &self,
345         trans: &mut impl GenKill<Self::Idx>,
346         _block: mir::BasicBlock,
347         enum_place: mir::Place<'tcx>,
348         _adt: &ty::AdtDef,
349         variant: VariantIdx,
350     ) {
351         let enum_mpi = match self.move_data().rev_lookup.find(enum_place.as_ref()) {
352             LookupResult::Exact(mpi) => mpi,
353             LookupResult::Parent(_) => return,
354         };
355
356         // Kill all move paths that correspond to variants other than this one
357         let move_paths = &self.move_data().move_paths;
358         let enum_path = &move_paths[enum_mpi];
359         for (mpi, variant_path) in enum_path.children(move_paths) {
360             trans.kill(mpi);
361             match variant_path.place.projection.last().unwrap() {
362                 mir::ProjectionElem::Downcast(_, idx) if *idx == variant => continue,
363                 _ => drop_flag_effects::on_all_children_bits(
364                     self.tcx,
365                     self.body,
366                     self.move_data(),
367                     mpi,
368                     |mpi| trans.kill(mpi),
369                 ),
370             }
371         }
372     }
373 }
374
375 impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
376     type Idx = MovePathIndex;
377
378     const NAME: &'static str = "maybe_uninit";
379
380     fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize {
381         self.move_data().move_paths.len()
382     }
383
384     // sets on_entry bits for Arg places
385     fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) {
386         // set all bits to 1 (uninit) before gathering counterevidence
387         assert!(self.bits_per_block(body) == state.domain_size());
388         state.insert_all();
389
390         drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| {
391             assert!(s == DropFlagState::Present);
392             state.remove(path);
393         });
394     }
395
396     fn pretty_print_idx(&self, w: &mut impl std::io::Write, mpi: Self::Idx) -> std::io::Result<()> {
397         write!(w, "{}", self.move_data().move_paths[mpi])
398     }
399 }
400
401 impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
402     fn statement_effect(
403         &self,
404         trans: &mut impl GenKill<Self::Idx>,
405         _statement: &mir::Statement<'tcx>,
406         location: Location,
407     ) {
408         drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
409             Self::update_bits(trans, path, s)
410         })
411     }
412
413     fn terminator_effect(
414         &self,
415         trans: &mut impl GenKill<Self::Idx>,
416         _terminator: &mir::Terminator<'tcx>,
417         location: Location,
418     ) {
419         drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
420             Self::update_bits(trans, path, s)
421         })
422     }
423
424     fn call_return_effect(
425         &self,
426         trans: &mut impl GenKill<Self::Idx>,
427         _block: mir::BasicBlock,
428         _func: &mir::Operand<'tcx>,
429         _args: &[mir::Operand<'tcx>],
430         dest_place: mir::Place<'tcx>,
431     ) {
432         // when a call returns successfully, that means we need to set
433         // the bits for that dest_place to 0 (initialized).
434         on_lookup_result_bits(
435             self.tcx,
436             self.body,
437             self.move_data(),
438             self.move_data().rev_lookup.find(dest_place.as_ref()),
439             |mpi| {
440                 trans.kill(mpi);
441             },
442         );
443     }
444 }
445
446 impl<'a, 'tcx> AnalysisDomain<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> {
447     type Idx = MovePathIndex;
448
449     const NAME: &'static str = "definite_init";
450
451     fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize {
452         self.move_data().move_paths.len()
453     }
454
455     // sets on_entry bits for Arg places
456     fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) {
457         state.clear();
458
459         drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| {
460             assert!(s == DropFlagState::Present);
461             state.insert(path);
462         });
463     }
464
465     fn pretty_print_idx(&self, w: &mut impl std::io::Write, mpi: Self::Idx) -> std::io::Result<()> {
466         write!(w, "{}", self.move_data().move_paths[mpi])
467     }
468 }
469
470 impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> {
471     fn statement_effect(
472         &self,
473         trans: &mut impl GenKill<Self::Idx>,
474         _statement: &mir::Statement<'tcx>,
475         location: Location,
476     ) {
477         drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
478             Self::update_bits(trans, path, s)
479         })
480     }
481
482     fn terminator_effect(
483         &self,
484         trans: &mut impl GenKill<Self::Idx>,
485         _terminator: &mir::Terminator<'tcx>,
486         location: Location,
487     ) {
488         drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
489             Self::update_bits(trans, path, s)
490         })
491     }
492
493     fn call_return_effect(
494         &self,
495         trans: &mut impl GenKill<Self::Idx>,
496         _block: mir::BasicBlock,
497         _func: &mir::Operand<'tcx>,
498         _args: &[mir::Operand<'tcx>],
499         dest_place: mir::Place<'tcx>,
500     ) {
501         // when a call returns successfully, that means we need to set
502         // the bits for that dest_place to 1 (initialized).
503         on_lookup_result_bits(
504             self.tcx,
505             self.body,
506             self.move_data(),
507             self.move_data().rev_lookup.find(dest_place.as_ref()),
508             |mpi| {
509                 trans.gen(mpi);
510             },
511         );
512     }
513 }
514
515 impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, 'tcx> {
516     type Idx = InitIndex;
517
518     const NAME: &'static str = "ever_init";
519
520     fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize {
521         self.move_data().inits.len()
522     }
523
524     fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) {
525         for arg_init in 0..body.arg_count {
526             state.insert(InitIndex::new(arg_init));
527         }
528     }
529 }
530
531 impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
532     fn statement_effect(
533         &self,
534         trans: &mut impl GenKill<Self::Idx>,
535         stmt: &mir::Statement<'tcx>,
536         location: Location,
537     ) {
538         let move_data = self.move_data();
539         let init_path_map = &move_data.init_path_map;
540         let init_loc_map = &move_data.init_loc_map;
541         let rev_lookup = &move_data.rev_lookup;
542
543         debug!(
544             "statement {:?} at loc {:?} initializes move_indexes {:?}",
545             stmt, location, &init_loc_map[location]
546         );
547         trans.gen_all(init_loc_map[location].iter().copied());
548
549         if let mir::StatementKind::StorageDead(local) = stmt.kind {
550             // End inits for StorageDead, so that an immutable variable can
551             // be reinitialized on the next iteration of the loop.
552             let move_path_index = rev_lookup.find_local(local);
553             debug!(
554                 "stmt {:?} at loc {:?} clears the ever initialized status of {:?}",
555                 stmt, location, &init_path_map[move_path_index]
556             );
557             trans.kill_all(init_path_map[move_path_index].iter().copied());
558         }
559     }
560
561     fn terminator_effect(
562         &self,
563         trans: &mut impl GenKill<Self::Idx>,
564         _terminator: &mir::Terminator<'tcx>,
565         location: Location,
566     ) {
567         let (body, move_data) = (self.body, self.move_data());
568         let term = body[location.block].terminator();
569         let init_loc_map = &move_data.init_loc_map;
570         debug!(
571             "terminator {:?} at loc {:?} initializes move_indexes {:?}",
572             term, location, &init_loc_map[location]
573         );
574         trans.gen_all(
575             init_loc_map[location]
576                 .iter()
577                 .filter(|init_index| {
578                     move_data.inits[**init_index].kind != InitKind::NonPanicPathOnly
579                 })
580                 .copied(),
581         );
582     }
583
584     fn call_return_effect(
585         &self,
586         trans: &mut impl GenKill<Self::Idx>,
587         block: mir::BasicBlock,
588         _func: &mir::Operand<'tcx>,
589         _args: &[mir::Operand<'tcx>],
590         _dest_place: mir::Place<'tcx>,
591     ) {
592         let move_data = self.move_data();
593         let init_loc_map = &move_data.init_loc_map;
594
595         let call_loc = self.body.terminator_loc(block);
596         for init_index in &init_loc_map[call_loc] {
597             trans.gen(*init_index);
598         }
599     }
600 }
601
602 impl<'a, 'tcx> BottomValue for MaybeInitializedPlaces<'a, 'tcx> {
603     /// bottom = uninitialized
604     const BOTTOM_VALUE: bool = false;
605 }
606
607 impl<'a, 'tcx> BottomValue for MaybeUninitializedPlaces<'a, 'tcx> {
608     /// bottom = initialized (start_block_effect counters this at outset)
609     const BOTTOM_VALUE: bool = false;
610 }
611
612 impl<'a, 'tcx> BottomValue for DefinitelyInitializedPlaces<'a, 'tcx> {
613     /// bottom = initialized (start_block_effect counters this at outset)
614     const BOTTOM_VALUE: bool = true;
615 }
616
617 impl<'a, 'tcx> BottomValue for EverInitializedPlaces<'a, 'tcx> {
618     /// bottom = no initialized variables by default
619     const BOTTOM_VALUE: bool = false;
620 }