rustc_mir_dataflow/
drop_flag_effects.rs

1use rustc_abi::VariantIdx;
2use rustc_middle::mir::{self, Body, Location, Terminator, TerminatorKind};
3use tracing::debug;
4
5use super::move_paths::{InitKind, LookupResult, MoveData, MovePathIndex};
6
7/// The value of an inserted drop flag.
8#[derive(Debug, PartialEq, Eq, Copy, Clone)]
9pub enum DropFlagState {
10    /// The tracked value is initialized and needs to be dropped when leaving its scope.
11    Present,
12
13    /// The tracked value is uninitialized or was moved out of and does not need to be dropped when
14    /// leaving its scope.
15    Absent,
16}
17
18impl DropFlagState {
19    pub fn value(self) -> bool {
20        match self {
21            DropFlagState::Present => true,
22            DropFlagState::Absent => false,
23        }
24    }
25}
26
27pub fn move_path_children_matching<'tcx, F>(
28    move_data: &MoveData<'tcx>,
29    path: MovePathIndex,
30    mut cond: F,
31) -> Option<MovePathIndex>
32where
33    F: FnMut(mir::PlaceElem<'tcx>) -> bool,
34{
35    let mut next_child = move_data.move_paths[path].first_child;
36    while let Some(child_index) = next_child {
37        let move_path_children = &move_data.move_paths[child_index];
38        if let Some(&elem) = move_path_children.place.projection.last() {
39            if cond(elem) {
40                return Some(child_index);
41            }
42        }
43        next_child = move_path_children.next_sibling;
44    }
45
46    None
47}
48
49pub fn on_lookup_result_bits<'tcx, F>(
50    move_data: &MoveData<'tcx>,
51    lookup_result: LookupResult,
52    each_child: F,
53) where
54    F: FnMut(MovePathIndex),
55{
56    match lookup_result {
57        LookupResult::Parent(..) => {
58            // access to untracked value - do not touch children
59        }
60        LookupResult::Exact(e) => on_all_children_bits(move_data, e, each_child),
61    }
62}
63
64pub fn on_all_children_bits<'tcx, F>(
65    move_data: &MoveData<'tcx>,
66    move_path_index: MovePathIndex,
67    mut each_child: F,
68) where
69    F: FnMut(MovePathIndex),
70{
71    fn on_all_children_bits<'tcx, F>(
72        move_data: &MoveData<'tcx>,
73        move_path_index: MovePathIndex,
74        each_child: &mut F,
75    ) where
76        F: FnMut(MovePathIndex),
77    {
78        each_child(move_path_index);
79
80        let mut next_child_index = move_data.move_paths[move_path_index].first_child;
81        while let Some(child_index) = next_child_index {
82            on_all_children_bits(move_data, child_index, each_child);
83            next_child_index = move_data.move_paths[child_index].next_sibling;
84        }
85    }
86    on_all_children_bits(move_data, move_path_index, &mut each_child);
87}
88
89pub fn drop_flag_effects_for_function_entry<'tcx, F>(
90    body: &Body<'tcx>,
91    move_data: &MoveData<'tcx>,
92    mut callback: F,
93) where
94    F: FnMut(MovePathIndex, DropFlagState),
95{
96    for arg in body.args_iter() {
97        let place = mir::Place::from(arg);
98        let lookup_result = move_data.rev_lookup.find(place.as_ref());
99        on_lookup_result_bits(move_data, lookup_result, |mpi| {
100            callback(mpi, DropFlagState::Present)
101        });
102    }
103}
104
105pub fn drop_flag_effects_for_location<'tcx, F>(
106    body: &Body<'tcx>,
107    move_data: &MoveData<'tcx>,
108    loc: Location,
109    mut callback: F,
110) where
111    F: FnMut(MovePathIndex, DropFlagState),
112{
113    debug!("drop_flag_effects_for_location({:?})", loc);
114
115    // first, move out of the RHS
116    for mi in &move_data.loc_map[loc] {
117        let path = mi.move_path_index(move_data);
118        debug!("moving out of path {:?}", move_data.move_paths[path]);
119
120        on_all_children_bits(move_data, path, |mpi| callback(mpi, DropFlagState::Absent))
121    }
122
123    // Drop does not count as a move but we should still consider the variable uninitialized.
124    if let Some(Terminator { kind: TerminatorKind::Drop { place, .. }, .. }) =
125        body.stmt_at(loc).right()
126    {
127        if let LookupResult::Exact(mpi) = move_data.rev_lookup.find(place.as_ref()) {
128            on_all_children_bits(move_data, mpi, |mpi| callback(mpi, DropFlagState::Absent))
129        }
130    }
131
132    debug!("drop_flag_effects: assignment for location({:?})", loc);
133
134    for_location_inits(move_data, loc, |mpi| callback(mpi, DropFlagState::Present));
135}
136
137fn for_location_inits<'tcx, F>(move_data: &MoveData<'tcx>, loc: Location, mut callback: F)
138where
139    F: FnMut(MovePathIndex),
140{
141    for ii in &move_data.init_loc_map[loc] {
142        let init = move_data.inits[*ii];
143        match init.kind {
144            InitKind::Deep => {
145                let path = init.path;
146
147                on_all_children_bits(move_data, path, &mut callback)
148            }
149            InitKind::Shallow => {
150                let mpi = init.path;
151                callback(mpi);
152            }
153            InitKind::NonPanicPathOnly => (),
154        }
155    }
156}
157
158/// Calls `handle_inactive_variant` for each descendant move path of `enum_place` that contains a
159/// `Downcast` to a variant besides the `active_variant`.
160///
161/// NOTE: If there are no move paths corresponding to an inactive variant,
162/// `handle_inactive_variant` will not be called for that variant.
163pub(crate) fn on_all_inactive_variants<'tcx>(
164    move_data: &MoveData<'tcx>,
165    enum_place: mir::Place<'tcx>,
166    active_variant: VariantIdx,
167    mut handle_inactive_variant: impl FnMut(MovePathIndex),
168) {
169    let LookupResult::Exact(enum_mpi) = move_data.rev_lookup.find(enum_place.as_ref()) else {
170        return;
171    };
172
173    let enum_path = &move_data.move_paths[enum_mpi];
174    for (variant_mpi, variant_path) in enum_path.children(&move_data.move_paths) {
175        // Because of the way we build the `MoveData` tree, each child should have exactly one more
176        // projection than `enum_place`. This additional projection must be a downcast since the
177        // base is an enum.
178        let (downcast, base_proj) = variant_path.place.projection.split_last().unwrap();
179        assert_eq!(enum_place.projection.len(), base_proj.len());
180
181        let mir::ProjectionElem::Downcast(_, variant_idx) = *downcast else {
182            unreachable!();
183        };
184
185        if variant_idx != active_variant {
186            on_all_children_bits(move_data, variant_mpi, |mpi| handle_inactive_variant(mpi));
187        }
188    }
189}