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