Skip to main content

rustc_mir_dataflow/
drop_flag_effects.rs

1use rustc_abi::VariantIdx;
2use rustc_middle::mir::{self, Body, Location, Terminator, TerminatorKind};
3use smallvec::SmallVec;
4use tracing::debug;
5
6use super::move_paths::{InitKind, LookupResult, MoveData, MovePathIndex};
7
8/// The value of an inserted drop flag.
9#[derive(#[automatically_derived]
impl ::core::fmt::Debug for DropFlagState {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                DropFlagState::Present => "Present",
                DropFlagState::Absent => "Absent",
            })
    }
}Debug, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for DropFlagState { }
#[automatically_derived]
impl ::core::cmp::PartialEq for DropFlagState {
    #[inline]
    fn eq(&self, other: &DropFlagState) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for DropFlagState {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {}
}Eq, #[automatically_derived]
impl ::core::marker::Copy for DropFlagState { }Copy, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for DropFlagState { }
#[automatically_derived]
impl ::core::clone::Clone for DropFlagState {
    #[inline]
    fn clone(&self) -> DropFlagState { *self }
}Clone)]
10pub enum DropFlagState {
11    /// The tracked value is initialized and needs to be dropped when leaving its scope.
12    Present,
13
14    /// The tracked value is uninitialized or was moved out of and does not need to be dropped when
15    /// leaving its scope.
16    Absent,
17}
18
19impl DropFlagState {
20    pub fn value(self) -> bool {
21        match self {
22            DropFlagState::Present => true,
23            DropFlagState::Absent => false,
24        }
25    }
26}
27
28pub fn move_path_children_matching<'tcx, F>(
29    move_data: &MoveData<'tcx>,
30    path: MovePathIndex,
31    mut cond: F,
32) -> Option<MovePathIndex>
33where
34    F: FnMut(mir::PlaceElem<'tcx>) -> bool,
35{
36    let mut next_child = move_data.move_paths[path].first_child;
37    while let Some(child_index) = next_child {
38        let move_path_children = &move_data.move_paths[child_index];
39        if let Some(&elem) = move_path_children.place.projection.last() {
40            if cond(elem) {
41                return Some(child_index);
42            }
43        }
44        next_child = move_path_children.next_sibling;
45    }
46
47    None
48}
49
50pub fn on_lookup_result_bits<'tcx, F>(
51    move_data: &MoveData<'tcx>,
52    lookup_result: LookupResult,
53    each_child: F,
54) where
55    F: FnMut(MovePathIndex),
56{
57    match lookup_result {
58        LookupResult::Parent(..) => {
59            // access to untracked value - do not touch children
60        }
61        LookupResult::Exact(e) => on_all_children_bits(move_data, e, each_child),
62    }
63}
64
65pub fn on_all_children_bits<'tcx, F>(
66    move_data: &MoveData<'tcx>,
67    move_path_index: MovePathIndex,
68    mut each_child: F,
69) where
70    F: FnMut(MovePathIndex),
71{
72    fn on_all_children_bits<'tcx, F>(
73        move_data: &MoveData<'tcx>,
74        move_path_index: MovePathIndex,
75        each_child: &mut F,
76    ) where
77        F: FnMut(MovePathIndex),
78    {
79        each_child(move_path_index);
80
81        let mut next_child_index = move_data.move_paths[move_path_index].first_child;
82        while let Some(child_index) = next_child_index {
83            on_all_children_bits(move_data, child_index, each_child);
84            next_child_index = move_data.move_paths[child_index].next_sibling;
85        }
86    }
87    on_all_children_bits(move_data, move_path_index, &mut each_child);
88}
89
90/// Calls `callback` for each child move path of the function's arguments. Note the move paths'
91/// `DropFlagState` argument to the callback will always be `DropFlagState::Present`.
92pub fn drop_flag_effects_for_function_entry<'tcx, F>(
93    body: &Body<'tcx>,
94    move_data: &MoveData<'tcx>,
95    mut callback: F,
96) where
97    F: FnMut(MovePathIndex, DropFlagState),
98{
99    for arg in body.args_iter() {
100        let place = mir::Place::from(arg);
101        let lookup_result = move_data.rev_lookup.find(place.as_ref());
102        on_lookup_result_bits(move_data, lookup_result, |mpi| {
103            callback(mpi, DropFlagState::Present)
104        });
105    }
106}
107
108pub fn drop_flag_effects_for_location<'tcx, F>(
109    body: &Body<'tcx>,
110    move_data: &MoveData<'tcx>,
111    loc: Location,
112    mut callback: F,
113) where
114    F: FnMut(MovePathIndex, DropFlagState),
115{
116    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/5a2be9f5f075d31e3ca5526b5b029881ce441253/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs:116",
                        "rustc_mir_dataflow::drop_flag_effects",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/5a2be9f5f075d31e3ca5526b5b029881ce441253/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs"),
                        ::tracing_core::__macro_support::Option::Some(116u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_mir_dataflow::drop_flag_effects"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("drop_flag_effects_for_location({0:?})",
                                                    loc) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("drop_flag_effects_for_location({:?})", loc);
117
118    // first, move out of the RHS
119    for mi in &move_data.move_out_loc_map[loc] {
120        let path = mi.move_path_index(move_data);
121        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/5a2be9f5f075d31e3ca5526b5b029881ce441253/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs:121",
                        "rustc_mir_dataflow::drop_flag_effects",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/5a2be9f5f075d31e3ca5526b5b029881ce441253/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs"),
                        ::tracing_core::__macro_support::Option::Some(121u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_mir_dataflow::drop_flag_effects"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("moving out of path {0:?}",
                                                    move_data.move_paths[path]) as
                                            &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("moving out of path {:?}", move_data.move_paths[path]);
122
123        on_all_children_bits(move_data, path, |mpi| callback(mpi, DropFlagState::Absent))
124    }
125
126    // Drop does not count as a move but we should still consider the variable uninitialized.
127    if let Some(Terminator { kind: TerminatorKind::Drop { place, .. }, .. }) =
128        body.stmt_at(loc).right()
129        && let LookupResult::Exact(mpi) = move_data.rev_lookup.find(place.as_ref())
130    {
131        on_all_children_bits(move_data, mpi, |mpi| callback(mpi, DropFlagState::Absent))
132    }
133
134    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/5a2be9f5f075d31e3ca5526b5b029881ce441253/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs:134",
                        "rustc_mir_dataflow::drop_flag_effects",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/5a2be9f5f075d31e3ca5526b5b029881ce441253/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs"),
                        ::tracing_core::__macro_support::Option::Some(134u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_mir_dataflow::drop_flag_effects"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("drop_flag_effects: assignment for location({0:?})",
                                                    loc) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("drop_flag_effects: assignment for location({:?})", loc);
135
136    for_location_inits(move_data, loc, |mpi| callback(mpi, DropFlagState::Present));
137}
138
139fn for_location_inits<'tcx, F>(move_data: &MoveData<'tcx>, loc: Location, mut callback: F)
140where
141    F: FnMut(MovePathIndex),
142{
143    for ii in &move_data.init_loc_map[loc] {
144        let init = move_data.inits[*ii];
145        match init.kind {
146            InitKind::Deep => {
147                let path = init.path;
148
149                on_all_children_bits(move_data, path, &mut callback)
150            }
151            InitKind::Shallow => {
152                let mpi = init.path;
153                callback(mpi);
154            }
155            InitKind::NonPanicPathOnly => (),
156        }
157    }
158}
159
160/// Indicates which variants are inactive at a `SwitchInt` edge by listing their `VariantIdx`s or
161/// specifying the single active variant's `VariantIdx`.
162pub(crate) enum InactiveVariants {
163    Inactives(SmallVec<[VariantIdx; 4]>),
164    Active(VariantIdx),
165}
166
167impl InactiveVariants {
168    fn contains(&self, variant_idx: VariantIdx) -> bool {
169        match self {
170            InactiveVariants::Inactives(inactives) => inactives.contains(&variant_idx),
171            InactiveVariants::Active(active) => variant_idx != *active,
172        }
173    }
174}
175
176/// Calls `handle_inactive_variant` for each child move path of `enum_place` corresponding to an
177/// inactive variant at a particular `SwitchInt` edge.
178pub(crate) fn on_all_inactive_variants<'tcx>(
179    move_data: &MoveData<'tcx>,
180    enum_place: mir::Place<'tcx>,
181    inactive_variants: &InactiveVariants,
182    mut handle_inactive_variant: impl FnMut(MovePathIndex),
183) {
184    let LookupResult::Exact(enum_mpi) = move_data.rev_lookup.find(enum_place.as_ref()) else {
185        return;
186    };
187
188    let enum_path = &move_data.move_paths[enum_mpi];
189    for (variant_mpi, variant_path) in enum_path.children(&move_data.move_paths) {
190        // Because of the way we build the `MoveData` tree, each child should have exactly one more
191        // projection than `enum_place`. This additional projection must be a downcast since the
192        // base is an enum.
193        let (downcast, base_proj) = variant_path.place.projection.split_last().unwrap();
194        {
    match (&enum_place.projection.len(), &base_proj.len()) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};assert_eq!(enum_place.projection.len(), base_proj.len());
195
196        let mir::ProjectionElem::Downcast(_, variant_idx) = *downcast else {
197            ::core::panicking::panic("internal error: entered unreachable code");unreachable!();
198        };
199
200        if inactive_variants.contains(variant_idx) {
201            on_all_children_bits(move_data, variant_mpi, |mpi| handle_inactive_variant(mpi));
202        }
203    }
204}