1 //! Dataflow analyses are built upon some interpretation of the
2 //! bitvectors attached to each basic block, represented via a
3 //! zero-sized structure.
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;
11 use super::MoveDataParamEnv;
13 use crate::util::elaborate_drops::DropFlagState;
15 use super::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex};
16 use super::{AnalysisDomain, BottomValue, GenKill, GenKillAnalysis};
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;
24 pub(super) mod borrows;
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};
33 /// `MaybeInitializedPlaces` tracks all places that might be
34 /// initialized upon reaching a particular point in the control flow
37 /// For example, in code like the following, we have corresponding
38 /// dataflow information shown in the right-hand comments.
42 /// fn foo(pred: bool) { // maybe-init:
44 /// let a = S; let b = S; let c; let d; // {a, b}
56 /// c = S; // {a, b, c, d}
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.
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> {
71 mdpe: &'a MoveDataParamEnv<'tcx>,
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 }
80 impl<'a, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'tcx> {
81 fn move_data(&self) -> &MoveData<'tcx> {
86 /// `MaybeUninitializedPlaces` tracks all places that might be
87 /// uninitialized upon reaching a particular point in the control flow
90 /// For example, in code like the following, we have corresponding
91 /// dataflow information shown in the right-hand comments.
95 /// fn foo(pred: bool) { // maybe-uninit:
97 /// let a = S; let b = S; let c; let d; // { c, d}
100 /// drop(a); // {a, c, d}
101 /// b = S; // {a, c, d}
104 /// drop(b); // { b, c, d}
105 /// d = S; // { b, c }
107 /// } // {a, b, c, d}
109 /// c = S; // {a, b, d}
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.
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> {
123 body: &'a Body<'tcx>,
124 mdpe: &'a MoveDataParamEnv<'tcx>,
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 }
133 impl<'a, 'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'a, 'tcx> {
134 fn move_data(&self) -> &MoveData<'tcx> {
139 /// `DefinitelyInitializedPlaces` tracks all places that are definitely
140 /// initialized upon reaching a particular point in the control flow
143 /// For example, in code like the following, we have corresponding
144 /// dataflow information shown in the right-hand comments.
148 /// fn foo(pred: bool) { // definite-init:
150 /// let a = S; let b = S; let c; let d; // {a, b }
153 /// drop(a); // { b, }
157 /// drop(b); // {a, }
166 /// To determine whether a place *may* be uninitialized at a
167 /// particular control-flow point, one can take the set-complement
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> {
175 body: &'a Body<'tcx>,
176 mdpe: &'a MoveDataParamEnv<'tcx>,
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 }
185 impl<'a, 'tcx> HasMoveData<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> {
186 fn move_data(&self) -> &MoveData<'tcx> {
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`.
195 /// This dataflow is used to determine if an immutable local variable may
198 /// For example, in code like the following, we have corresponding
199 /// dataflow information shown in the right-hand comments.
203 /// fn foo(pred: bool) { // ever-init:
205 /// let a = S; let b = S; let c; let d; // {a, b }
208 /// drop(a); // {a, b, }
209 /// b = S; // {a, b, }
212 /// drop(b); // {a, b, }
213 /// d = S; // {a, b, d }
217 /// c = S; // {a, b, c, d }
220 pub struct EverInitializedPlaces<'a, 'tcx> {
223 body: &'a Body<'tcx>,
224 mdpe: &'a MoveDataParamEnv<'tcx>,
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 }
233 impl<'a, 'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'a, 'tcx> {
234 fn move_data(&self) -> &MoveData<'tcx> {
239 impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
241 trans: &mut impl GenKill<MovePathIndex>,
243 state: DropFlagState,
246 DropFlagState::Absent => trans.kill(path),
247 DropFlagState::Present => trans.gen(path),
252 impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
254 trans: &mut impl GenKill<MovePathIndex>,
256 state: DropFlagState,
259 DropFlagState::Absent => trans.gen(path),
260 DropFlagState::Present => trans.kill(path),
265 impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> {
267 trans: &mut impl GenKill<MovePathIndex>,
269 state: DropFlagState,
272 DropFlagState::Absent => trans.kill(path),
273 DropFlagState::Present => trans.gen(path),
278 impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
279 type Idx = MovePathIndex;
281 const NAME: &'static str = "maybe_init";
283 fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize {
284 self.move_data().move_paths.len()
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);
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])
299 impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
302 trans: &mut impl GenKill<Self::Idx>,
303 _statement: &mir::Statement<'tcx>,
306 drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
307 Self::update_bits(trans, path, s)
311 fn terminator_effect(
313 trans: &mut impl GenKill<Self::Idx>,
314 _terminator: &mir::Terminator<'tcx>,
317 drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
318 Self::update_bits(trans, path, s)
322 fn call_return_effect(
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>,
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(
336 self.move_data().rev_lookup.find(dest_place.as_ref()),
343 fn discriminant_switch_effect(
345 trans: &mut impl GenKill<Self::Idx>,
346 _block: mir::BasicBlock,
347 enum_place: mir::Place<'tcx>,
351 let enum_mpi = match self.move_data().rev_lookup.find(enum_place.as_ref()) {
352 LookupResult::Exact(mpi) => mpi,
353 LookupResult::Parent(_) => return,
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) {
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(
368 |mpi| trans.kill(mpi),
375 impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
376 type Idx = MovePathIndex;
378 const NAME: &'static str = "maybe_uninit";
380 fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize {
381 self.move_data().move_paths.len()
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());
390 drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| {
391 assert!(s == DropFlagState::Present);
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])
401 impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
404 trans: &mut impl GenKill<Self::Idx>,
405 _statement: &mir::Statement<'tcx>,
408 drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
409 Self::update_bits(trans, path, s)
413 fn terminator_effect(
415 trans: &mut impl GenKill<Self::Idx>,
416 _terminator: &mir::Terminator<'tcx>,
419 drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
420 Self::update_bits(trans, path, s)
424 fn call_return_effect(
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>,
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(
438 self.move_data().rev_lookup.find(dest_place.as_ref()),
446 impl<'a, 'tcx> AnalysisDomain<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> {
447 type Idx = MovePathIndex;
449 const NAME: &'static str = "definite_init";
451 fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize {
452 self.move_data().move_paths.len()
455 // sets on_entry bits for Arg places
456 fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) {
459 drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| {
460 assert!(s == DropFlagState::Present);
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])
470 impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> {
473 trans: &mut impl GenKill<Self::Idx>,
474 _statement: &mir::Statement<'tcx>,
477 drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
478 Self::update_bits(trans, path, s)
482 fn terminator_effect(
484 trans: &mut impl GenKill<Self::Idx>,
485 _terminator: &mir::Terminator<'tcx>,
488 drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
489 Self::update_bits(trans, path, s)
493 fn call_return_effect(
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>,
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(
507 self.move_data().rev_lookup.find(dest_place.as_ref()),
515 impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, 'tcx> {
516 type Idx = InitIndex;
518 const NAME: &'static str = "ever_init";
520 fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize {
521 self.move_data().inits.len()
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));
531 impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
534 trans: &mut impl GenKill<Self::Idx>,
535 stmt: &mir::Statement<'tcx>,
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;
544 "statement {:?} at loc {:?} initializes move_indexes {:?}",
545 stmt, location, &init_loc_map[location]
547 trans.gen_all(init_loc_map[location].iter().copied());
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);
554 "stmt {:?} at loc {:?} clears the ever initialized status of {:?}",
555 stmt, location, &init_path_map[move_path_index]
557 trans.kill_all(init_path_map[move_path_index].iter().copied());
561 fn terminator_effect(
563 trans: &mut impl GenKill<Self::Idx>,
564 _terminator: &mir::Terminator<'tcx>,
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;
571 "terminator {:?} at loc {:?} initializes move_indexes {:?}",
572 term, location, &init_loc_map[location]
575 init_loc_map[location]
577 .filter(|init_index| {
578 move_data.inits[**init_index].kind != InitKind::NonPanicPathOnly
584 fn call_return_effect(
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>,
592 let move_data = self.move_data();
593 let init_loc_map = &move_data.init_loc_map;
595 let call_loc = self.body.terminator_loc(block);
596 for init_index in &init_loc_map[call_loc] {
597 trans.gen(*init_index);
602 impl<'a, 'tcx> BottomValue for MaybeInitializedPlaces<'a, 'tcx> {
603 /// bottom = uninitialized
604 const BOTTOM_VALUE: bool = false;
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;
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;
617 impl<'a, 'tcx> BottomValue for EverInitializedPlaces<'a, 'tcx> {
618 /// bottom = no initialized variables by default
619 const BOTTOM_VALUE: bool = false;