Skip to main content

rustc_mir_dataflow/impls/
initialized.rs

1use std::assert_matches;
2
3use rustc_abi::VariantIdx;
4use rustc_data_structures::fx::FxIndexSet;
5use rustc_index::bit_set::{DenseBitSet, MixedBitSet};
6use rustc_middle::bug;
7use rustc_middle::mir::{
8    self, BasicBlock, Body, CallReturnPlaces, Local, Location, StatementKind, TerminatorEdges,
9};
10use rustc_middle::ty::{self, TyCtxt};
11use smallvec::SmallVec;
12use tracing::instrument;
13
14use crate::drop_flag_effects::{DropFlagState, InactiveVariants};
15use crate::move_paths::{
16    HasMoveData, Init, InitKind, InitLocation, LookupResult, MoveData, MovePathIndex,
17};
18use crate::{
19    Analysis, GenKill, MaybeReachable, SwitchTargetIndex, drop_flag_effects,
20    drop_flag_effects_for_function_entry, drop_flag_effects_for_location, on_all_children_bits,
21    on_lookup_result_bits,
22};
23
24// Used by both `MaybeInitializedPlaces` and `MaybeUninitializedPlaces`.
25pub struct MaybePlacesSwitchIntData<'tcx> {
26    enum_place: mir::Place<'tcx>,
27
28    // Variant indices targeted by the SwitchInt. For example, if you have:
29    // ```
30    // enum E { A = 1, B = 3, C = 5, D = 7 }
31    // ```
32    // and a `SwitchInt(A -> bb1, C -> bb2, _ -> bb3)`, this vec will contain `[0, 2]` because
33    // those are the variant indices for `A` and `C`.
34    variants: SmallVec<[VariantIdx; 4]>,
35}
36
37impl<'tcx> MaybePlacesSwitchIntData<'tcx> {
38    fn new(
39        tcx: TyCtxt<'tcx>,
40        body: &Body<'tcx>,
41        block: mir::BasicBlock,
42        targets: &mir::SwitchTargets,
43        discr: &mir::Operand<'tcx>,
44    ) -> Option<Self> {
45        let Some(discr) = discr.place() else { return None };
46
47        // Inspect a `SwitchInt`-terminated basic block to see if the condition of that `SwitchInt`
48        // is an enum discriminant.
49        //
50        // We expect such blocks to have a call to `discriminant` as their last statement like so:
51        // ```text
52        // ...
53        // _42 = discriminant(_1)
54        // SwitchInt(_42, ..)
55        // ```
56        // If the basic block matches this pattern, this function gathers the place corresponding
57        // to the enum (`_1` in the example above) as well as the discriminants.
58        let block_data = &body[block];
59        for statement in block_data.statements.iter().rev() {
60            match statement.kind {
61                mir::StatementKind::Assign((lhs, mir::Rvalue::Discriminant(enum_place)))
62                    if lhs == discr =>
63                {
64                    match enum_place.ty(body, tcx).ty.kind() {
65                        ty::Adt(enum_def, _) => {
66                            // For each value in the SwitchInt, find the VariantIdx for the variant
67                            // with that value. This works because `discriminant_vals` and
68                            // `targets.all_values()` are guaranteed to list variants in the same
69                            // AdtDef order. (If that ever changes the `expect` will panic.)
70                            let mut discriminants = enum_def.discriminants(tcx);
71                            let variants = targets
72                                .all_values()
73                                .iter()
74                                .map(|value| {
75                                    // On each call to this closure `find` only consumes part of
76                                    // the `discriminants` iterator.
77                                    discriminants
78                                        .find(|(_, discr)| discr.val == value.get())
79                                        .expect("SwitchInt vals should match a variant")
80                                        .0
81                                })
82                                .collect();
83
84                            return Some(MaybePlacesSwitchIntData { enum_place, variants });
85                        }
86
87                        // `Rvalue::Discriminant` is also used to get the active yield point for a
88                        // coroutine, but we do not need edge-specific effects in that case. This
89                        // may change in the future.
90                        ty::Coroutine(..) => break,
91
92                        t => ::rustc_middle::util::bug::bug_fmt(format_args!("`discriminant` called on unexpected type {0:?}",
        t))bug!("`discriminant` called on unexpected type {:?}", t),
93                    }
94                }
95                mir::StatementKind::Coverage(_) => continue,
96                _ => break,
97            }
98        }
99        None
100    }
101}
102
103/// `MaybeInitializedPlaces` tracks all places that might be
104/// initialized upon reaching a particular point in the control flow
105/// for a function.
106///
107/// For example, in code like the following, we have corresponding
108/// dataflow information shown in the right-hand comments.
109///
110/// ```rust
111/// struct S;
112/// #[rustfmt::skip]
113/// fn foo(p: bool) {                           // maybe-init:
114///                                             // {p}
115///     let a = S; let mut b = S; let c; let d; // {p, a, b}
116///
117///     if p {
118///         drop(a);                            // {p,    b}
119///         b = S;                              // {p,    b}
120///
121///     } else {
122///         drop(b);                            // {p, a}
123///         d = S;                              // {p, a,       d}
124///
125///     }                                       // {p, a, b,    d}
126///
127///     c = S;                                  // {p, a, b, c, d}
128/// }
129/// ```
130///
131/// To determine whether a place is *definitely* initialized at a
132/// particular control-flow point, one can take the set-complement
133/// of the data from `MaybeUninitializedPlaces` at the corresponding
134/// control-flow point.
135///
136/// Similarly, at a given `drop` statement, the set-intersection
137/// between this data and `MaybeUninitializedPlaces` yields the set of
138/// places that would require a dynamic drop-flag at that statement.
139pub struct MaybeInitializedPlaces<'a, 'tcx> {
140    tcx: TyCtxt<'tcx>,
141    body: &'a Body<'tcx>,
142    move_data: &'a MoveData<'tcx>,
143    exclude_inactive_in_otherwise: bool,
144    skip_unreachable_unwind: bool,
145}
146
147impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
148    pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>) -> Self {
149        MaybeInitializedPlaces {
150            tcx,
151            body,
152            move_data,
153            exclude_inactive_in_otherwise: false,
154            skip_unreachable_unwind: false,
155        }
156    }
157
158    /// Ensures definitely inactive variants are excluded from the set of initialized places for
159    /// blocks reached through an `otherwise` edge.
160    pub fn exclude_inactive_in_otherwise(mut self) -> Self {
161        self.exclude_inactive_in_otherwise = true;
162        self
163    }
164
165    pub fn skipping_unreachable_unwind(mut self) -> Self {
166        self.skip_unreachable_unwind = true;
167        self
168    }
169
170    pub fn is_unwind_dead(
171        &self,
172        place: mir::Place<'tcx>,
173        state: &<Self as Analysis<'tcx>>::Domain,
174    ) -> bool {
175        if let LookupResult::Exact(path) = self.move_data().rev_lookup.find(place.as_ref()) {
176            let mut maybe_live = false;
177            on_all_children_bits(self.move_data(), path, |child| {
178                maybe_live |= state.contains(child);
179            });
180            !maybe_live
181        } else {
182            false
183        }
184    }
185}
186
187impl<'a, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'tcx> {
188    fn move_data(&self) -> &MoveData<'tcx> {
189        self.move_data
190    }
191}
192
193/// `MaybeUninitializedPlaces` tracks all places that might be
194/// uninitialized upon reaching a particular point in the control flow
195/// for a function.
196///
197/// For example, in code like the following, we have corresponding
198/// dataflow information shown in the right-hand comments.
199///
200/// ```rust
201/// struct S;
202/// #[rustfmt::skip]
203/// fn foo(p: bool) {                           // maybe-uninit:
204///                                             // {a, b, c, d}
205///     let a = S; let mut b = S; let c; let d; // {      c, d}
206///
207///     if p {
208///         drop(a);                            // {a,    c, d}
209///         b = S;                              // {a,    c, d}
210///
211///     } else {
212///         drop(b);                            // {   b, c, d}
213///         d = S;                              // {   b, c   }
214///
215///     }                                       // {a, b, c, d}
216///
217///     c = S;                                  // {a, b,    d}
218/// }
219/// ```
220///
221/// To determine whether a place is *definitely* uninitialized at a
222/// particular control-flow point, one can take the set-complement
223/// of the data from `MaybeInitializedPlaces` at the corresponding
224/// control-flow point.
225///
226/// Similarly, at a given `drop` statement, the set-intersection
227/// between this data and `MaybeInitializedPlaces` yields the set of
228/// places that would require a dynamic drop-flag at that statement.
229pub struct MaybeUninitializedPlaces<'a, 'tcx> {
230    tcx: TyCtxt<'tcx>,
231    body: &'a Body<'tcx>,
232    move_data: &'a MoveData<'tcx>,
233
234    mark_inactive_variants_as_uninit: bool,
235    skip_unreachable_unwind: DenseBitSet<mir::BasicBlock>,
236}
237
238impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
239    pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>) -> Self {
240        MaybeUninitializedPlaces {
241            tcx,
242            body,
243            move_data,
244            mark_inactive_variants_as_uninit: false,
245            skip_unreachable_unwind: DenseBitSet::new_empty(body.basic_blocks.len()),
246        }
247    }
248
249    /// Causes inactive enum variants to be marked as "maybe uninitialized" after a switch on an
250    /// enum discriminant.
251    ///
252    /// This is correct in a vacuum but is not the default because it causes problems in the borrow
253    /// checker, where this information gets propagated along `FakeEdge`s.
254    pub fn mark_inactive_variants_as_uninit(mut self) -> Self {
255        self.mark_inactive_variants_as_uninit = true;
256        self
257    }
258
259    pub fn skipping_unreachable_unwind(
260        mut self,
261        unreachable_unwind: DenseBitSet<mir::BasicBlock>,
262    ) -> Self {
263        self.skip_unreachable_unwind = unreachable_unwind;
264        self
265    }
266}
267
268impl<'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
269    fn move_data(&self) -> &MoveData<'tcx> {
270        self.move_data
271    }
272}
273
274/// `EverInitializedPlaces` tracks all initializations of locals that may have
275/// occurred upon reaching a particular point in the control flow for a
276/// function, without an intervening `StorageDead`.
277///
278/// This dataflow is used to determine if an immutable local variable may
279/// be assigned to.
280///
281/// For example, in code like the following, we have corresponding
282/// dataflow information shown in the right-hand comments.
283///
284/// ```rust
285/// struct S;
286/// #[rustfmt::skip]
287/// fn foo(p: bool) {                           // ever-init:
288///                                             // {p,           }
289///     let a = S; let mut b = S; let c; let d; // {p, a, b,     }
290///
291///     if p {
292///         drop(a);                            // {p, a, b,     }
293///         b = S;                              // {p, a, b,     }
294///
295///     } else {
296///         drop(b);                            // {p, a, b,     }
297///         d = S;                              // {p, a, b,    d}
298///
299///     }                                       // {p, a, b,    d}
300///
301///     c = S;                                  // {p, a, b, c, d}
302/// }
303/// ```
304pub struct EverInitializedPlaces<'a, 'tcx> {
305    body: &'a Body<'tcx>,
306    move_data: &'a MoveData<'tcx>,
307}
308
309impl<'a, 'tcx> EverInitializedPlaces<'a, 'tcx> {
310    pub fn new(body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>) -> Self {
311        EverInitializedPlaces { body, move_data }
312    }
313}
314
315impl<'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'_, 'tcx> {
316    fn move_data(&self) -> &MoveData<'tcx> {
317        self.move_data
318    }
319}
320
321impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
322    fn update_bits(
323        state: &mut <Self as Analysis<'tcx>>::Domain,
324        path: MovePathIndex,
325        dfstate: DropFlagState,
326    ) {
327        match dfstate {
328            DropFlagState::Absent => state.kill(path),
329            DropFlagState::Present => state.gen_(path),
330        }
331    }
332}
333
334impl<'tcx> MaybeUninitializedPlaces<'_, 'tcx> {
335    fn update_bits(
336        state: &mut <Self as Analysis<'tcx>>::Domain,
337        path: MovePathIndex,
338        dfstate: DropFlagState,
339    ) {
340        match dfstate {
341            DropFlagState::Absent => state.gen_(path),
342            DropFlagState::Present => state.kill(path),
343        }
344    }
345}
346
347impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
348    /// There can be many more `MovePathIndex` than there are locals in a MIR body.
349    /// We use a mixed bitset to avoid paying too high a memory footprint.
350    type Domain = MaybeReachable<MixedBitSet<MovePathIndex>>;
351
352    type SwitchIntData = MaybePlacesSwitchIntData<'tcx>;
353
354    const NAME: &'static str = "maybe_init";
355
356    fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
357        // bottom = uninitialized
358        MaybeReachable::Unreachable
359    }
360
361    fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) {
362        *state =
363            MaybeReachable::Reachable(MixedBitSet::new_empty(self.move_data().move_paths.len()));
364        drop_flag_effects_for_function_entry(self.body, self.move_data, |path, s| {
365            if !(s == DropFlagState::Present) {
    ::core::panicking::panic("assertion failed: s == DropFlagState::Present")
};assert!(s == DropFlagState::Present);
366            state.gen_(path);
367        });
368    }
369
370    fn apply_primary_statement_effect(
371        &self,
372        state: &mut Self::Domain,
373        statement: &mir::Statement<'tcx>,
374        location: Location,
375    ) {
376        drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| {
377            Self::update_bits(state, path, s)
378        });
379
380        // Mark all places as "maybe init" if they are mutably borrowed. See #90752.
381        if self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration
382            && let Some((_, rvalue)) = statement.kind.as_assign()
383            && let mir::Rvalue::Ref(_, mir::BorrowKind::Mut { .. }, place)
384                // FIXME: Does `&raw const foo` allow mutation? See #90413.
385                | mir::Rvalue::RawPtr(_, place) = rvalue
386            && let LookupResult::Exact(mpi) = self.move_data().rev_lookup.find(place.as_ref())
387        {
388            on_all_children_bits(self.move_data(), mpi, |child| {
389                state.gen_(child);
390            })
391        }
392    }
393
394    fn get_terminator_edges<'mir>(
395        &self,
396        state: &Self::Domain,
397        terminator: &'mir mir::Terminator<'tcx>,
398        _location: Location,
399    ) -> TerminatorEdges<'mir, 'tcx> {
400        // Note: this relies on `get_terminator_edges` being called before
401        // `apply_primary_terminator_effect` because the result of `is_unwind_dead` is affected by
402        // the `drop_flag_effects_for_location` in `apply_primary_terminator_effect`.
403        let mut edges = terminator.edges();
404        if self.skip_unreachable_unwind
405            && let mir::TerminatorKind::Drop { target, unwind, place, replace: _, drop: _ } =
406                terminator.kind
407            && #[allow(non_exhaustive_omitted_patterns)] match unwind {
    mir::UnwindAction::Cleanup(_) => true,
    _ => false,
}matches!(unwind, mir::UnwindAction::Cleanup(_))
408            && self.is_unwind_dead(place, state)
409        {
410            edges = TerminatorEdges::Single(target);
411        }
412        edges
413    }
414
415    fn apply_primary_terminator_effect(
416        &self,
417        state: &mut Self::Domain,
418        _terminator: &mir::Terminator<'tcx>,
419        location: Location,
420    ) {
421        drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| {
422            Self::update_bits(state, path, s)
423        });
424    }
425
426    fn apply_call_return_effect(
427        &self,
428        state: &mut Self::Domain,
429        _block: mir::BasicBlock,
430        return_places: CallReturnPlaces<'_, 'tcx>,
431    ) {
432        return_places.for_each(|place| {
433            // when a call returns successfully, that means we need to set
434            // the bits for that dest_place to 1 (initialized).
435            on_lookup_result_bits(
436                self.move_data(),
437                self.move_data().rev_lookup.find(place.as_ref()),
438                |mpi| {
439                    state.gen_(mpi);
440                },
441            );
442        });
443    }
444
445    fn get_switch_int_data(
446        &self,
447        block: mir::BasicBlock,
448        targets: &mir::SwitchTargets,
449        discr: &mir::Operand<'tcx>,
450    ) -> Option<Self::SwitchIntData> {
451        if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration {
452            return None;
453        }
454
455        MaybePlacesSwitchIntData::new(self.tcx, self.body, block, targets, discr)
456    }
457
458    fn apply_switch_int_edge_effect(
459        &self,
460        state: &mut Self::Domain,
461        data: &Self::SwitchIntData,
462        target_idx: SwitchTargetIndex,
463    ) {
464        let inactive_variants = match target_idx {
465            SwitchTargetIndex::Normal(target_idx) => {
466                InactiveVariants::Active(data.variants[target_idx])
467            }
468            SwitchTargetIndex::Otherwise if self.exclude_inactive_in_otherwise => {
469                InactiveVariants::Inactives(data.variants.clone())
470            }
471            _ => return,
472        };
473
474        // Kill all move paths that correspond to variants we know to be inactive along this
475        // particular outgoing edge of a `SwitchInt`.
476        drop_flag_effects::on_all_inactive_variants(
477            self.move_data,
478            data.enum_place,
479            &inactive_variants,
480            |mpi| state.kill(mpi),
481        );
482    }
483}
484
485/// There can be many more `MovePathIndex` than there are locals in a MIR body.
486/// We use a mixed bitset to avoid paying too high a memory footprint.
487pub type MaybeUninitializedPlacesDomain = MixedBitSet<MovePathIndex>;
488
489impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
490    type Domain = MaybeUninitializedPlacesDomain;
491
492    type SwitchIntData = MaybePlacesSwitchIntData<'tcx>;
493
494    const NAME: &'static str = "maybe_uninit";
495
496    fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
497        // bottom = initialized (`initialize_start_block` overwrites this on first entry)
498        MixedBitSet::new_empty(self.move_data().move_paths.len())
499    }
500
501    // sets state bits for Arg places
502    fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) {
503        // set all bits to 1 (uninit) before gathering counter-evidence
504        state.insert_all();
505
506        drop_flag_effects_for_function_entry(self.body, self.move_data, |path, s| {
507            if !(s == DropFlagState::Present) {
    ::core::panicking::panic("assertion failed: s == DropFlagState::Present")
};assert!(s == DropFlagState::Present);
508            state.remove(path);
509        });
510    }
511
512    fn apply_primary_statement_effect(
513        &self,
514        state: &mut Self::Domain,
515        _statement: &mir::Statement<'tcx>,
516        location: Location,
517    ) {
518        drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| {
519            Self::update_bits(state, path, s)
520        });
521
522        // Unlike in `MaybeInitializedPlaces` above, we don't need to change the state when a
523        // mutable borrow occurs. Places cannot become uninitialized through a mutable reference.
524    }
525
526    fn get_terminator_edges<'mir>(
527        &self,
528        _state: &Self::Domain,
529        terminator: &'mir mir::Terminator<'tcx>,
530        location: Location,
531    ) -> TerminatorEdges<'mir, 'tcx> {
532        if self.skip_unreachable_unwind.contains(location.block) {
533            let mir::TerminatorKind::Drop { target, unwind, .. } = terminator.kind else { ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!() };
534            {
    match unwind {
        mir::UnwindAction::Cleanup(_) => {}
        ref left_val => {
            ::core::panicking::assert_matches_failed(left_val,
                "mir::UnwindAction::Cleanup(_)",
                ::core::option::Option::None);
        }
    }
};assert_matches!(unwind, mir::UnwindAction::Cleanup(_));
535            TerminatorEdges::Single(target)
536        } else {
537            terminator.edges()
538        }
539    }
540
541    fn apply_primary_terminator_effect(
542        &self,
543        state: &mut Self::Domain,
544        _terminator: &mir::Terminator<'tcx>,
545        location: Location,
546    ) {
547        drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| {
548            Self::update_bits(state, path, s)
549        });
550    }
551
552    fn apply_call_return_effect(
553        &self,
554        state: &mut Self::Domain,
555        _block: mir::BasicBlock,
556        return_places: CallReturnPlaces<'_, 'tcx>,
557    ) {
558        return_places.for_each(|place| {
559            // when a call returns successfully, that means we need to set
560            // the bits for that dest_place to 0 (initialized).
561            on_lookup_result_bits(
562                self.move_data(),
563                self.move_data().rev_lookup.find(place.as_ref()),
564                |mpi| {
565                    state.kill(mpi);
566                },
567            );
568        });
569    }
570
571    fn get_switch_int_data(
572        &self,
573        block: mir::BasicBlock,
574        targets: &mir::SwitchTargets,
575        discr: &mir::Operand<'tcx>,
576    ) -> Option<Self::SwitchIntData> {
577        if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration {
578            return None;
579        }
580
581        if !self.mark_inactive_variants_as_uninit {
582            return None;
583        }
584
585        MaybePlacesSwitchIntData::new(self.tcx, self.body, block, targets, discr)
586    }
587
588    fn apply_switch_int_edge_effect(
589        &self,
590        state: &mut Self::Domain,
591        data: &Self::SwitchIntData,
592        target_idx: SwitchTargetIndex,
593    ) {
594        let inactive_variants = match target_idx {
595            SwitchTargetIndex::Normal(target_idx) => {
596                InactiveVariants::Active(data.variants[target_idx])
597            }
598            SwitchTargetIndex::Otherwise => InactiveVariants::Inactives(data.variants.clone()),
599        };
600
601        // Mark all move paths that correspond to variants other than this one as maybe
602        // uninitialized (in reality, they are *definitely* uninitialized).
603        drop_flag_effects::on_all_inactive_variants(
604            self.move_data,
605            data.enum_place,
606            &inactive_variants,
607            |mpi| state.gen_(mpi),
608        );
609    }
610}
611
612pub type EverInitializedPlacesDomain = DenseBitSet<Local>;
613
614impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
615    type Domain = EverInitializedPlacesDomain;
616
617    const NAME: &'static str = "ever_init";
618
619    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
620        // bottom = no initialized locals by default
621        DenseBitSet::new_empty(body.local_decls.len())
622    }
623
624    fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) {
625        for arg in body.args_iter() {
626            state.insert(arg);
627        }
628    }
629
630    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("apply_primary_statement_effect",
                                    "rustc_mir_dataflow::impls::initialized",
                                    ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_dataflow/src/impls/initialized.rs"),
                                    ::tracing_core::__macro_support::Option::Some(630u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_mir_dataflow::impls::initialized"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("stmt")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("stmt");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("location")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("location");
                                                        NAME.as_str()
                                                    }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&stmt)
                                                            as &dyn ::tracing::field::Value)),
                                                (::tracing::__macro_support::Option::Some(&::tracing::field::debug(&location)
                                                            as &dyn ::tracing::field::Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: () = loop {};
            return __tracing_attr_fake_return;
        }
        {
            let move_data = self.move_data();
            let init_loc_map = &move_data.init_loc_map;
            state.gen_all(init_loc_map[location].iter().copied().filter_map(|ii|
                        {
                            let init_mpi = move_data.inits[ii].path;
                            move_data.move_paths[init_mpi].place.as_local()
                        }));
            if let mir::StatementKind::StorageDead(local) = stmt.kind {
                state.kill(local);
            }
        }
    }
}#[instrument(skip(self, state), level = "debug")]
631    fn apply_primary_statement_effect(
632        &self,
633        state: &mut Self::Domain,
634        stmt: &mir::Statement<'tcx>,
635        location: Location,
636    ) {
637        let move_data = self.move_data();
638        let init_loc_map = &move_data.init_loc_map;
639
640        // Record inits of locals. Projections can be ignored.
641        state.gen_all(init_loc_map[location].iter().copied().filter_map(|ii| {
642            let init_mpi = move_data.inits[ii].path;
643            move_data.move_paths[init_mpi].place.as_local()
644        }));
645
646        // Kill on StorageDead, so that an immutable variable can
647        // be reinitialized on the next iteration of the loop.
648        if let mir::StatementKind::StorageDead(local) = stmt.kind {
649            state.kill(local);
650        }
651    }
652
653    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("apply_primary_terminator_effect",
                                    "rustc_mir_dataflow::impls::initialized",
                                    ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_dataflow/src/impls/initialized.rs"),
                                    ::tracing_core::__macro_support::Option::Some(653u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_mir_dataflow::impls::initialized"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("location")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("location");
                                                        NAME.as_str()
                                                    }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&location)
                                                            as &dyn ::tracing::field::Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: () = loop {};
            return __tracing_attr_fake_return;
        }
        {
            let move_data = self.move_data();
            let init_loc_map = &move_data.init_loc_map;
            state.gen_all(init_loc_map[location].iter().copied().filter_map(|ii|
                        {
                            let init = &move_data.inits[ii];
                            if init.kind != InitKind::NonPanicPathOnly {
                                move_data.move_paths[init.path].place.as_local()
                            } else { None }
                        }));
        }
    }
}#[instrument(skip(self, state, _terminator), level = "debug")]
654    fn apply_primary_terminator_effect(
655        &self,
656        state: &mut Self::Domain,
657        _terminator: &mir::Terminator<'tcx>,
658        location: Location,
659    ) {
660        let move_data = self.move_data();
661        let init_loc_map = &move_data.init_loc_map;
662
663        // Record inits of locals. Projections can be ignored.
664        state.gen_all(init_loc_map[location].iter().copied().filter_map(|ii| {
665            let init = &move_data.inits[ii];
666            if init.kind != InitKind::NonPanicPathOnly {
667                move_data.move_paths[init.path].place.as_local()
668            } else {
669                None
670            }
671        }));
672    }
673
674    fn apply_call_return_effect(
675        &self,
676        state: &mut Self::Domain,
677        block: mir::BasicBlock,
678        _return_places: CallReturnPlaces<'_, 'tcx>,
679    ) {
680        let move_data = self.move_data();
681        let init_loc_map = &move_data.init_loc_map;
682
683        // Record inits of locals. Projections can be ignored.
684        let call_loc = self.body.terminator_loc(block);
685        state.gen_all(init_loc_map[call_loc].iter().copied().filter_map(|ii| {
686            let init = &move_data.inits[ii];
687            if init.kind == InitKind::NonPanicPathOnly {
688                move_data.move_paths[init.path].place.as_local()
689            } else {
690                None
691            }
692        }));
693    }
694}
695
696impl EverInitializedPlaces<'_, '_> {
697    /// Whether the init of `local` at `init` can reach `target` via a path that doesn't pass
698    /// through a `StorageDead(local)`. Mirrors the gen/kill structure of `EverInitializedPlaces`.
699    pub fn init_reaches_location(
700        body: &Body<'_>,
701        local: Local,
702        init: Init,
703        target: Location,
704    ) -> bool {
705        let init_loc = match init.location {
706            // Arguments are initialized on entry, and `StorageDead` is never emitted for them, so
707            // they reach every location.
708            InitLocation::Argument(_) => return true,
709            InitLocation::Statement(init_loc) => init_loc,
710        };
711
712        // Worklist of locations to walk forward from, seeded with the location(s) following `init`.
713        let mut queue = ::alloc::vec::Vec::new()vec![];
714
715        let basic_blocks = &body.basic_blocks;
716        let init_block_data = &basic_blocks[init_loc.block];
717        if init_loc.statement_index < init_block_data.statements.len() {
718            // This case mirrors `apply_primary_statement_effect`.
719            queue.push(init_loc.successor_within_block());
720        } else if init.kind == InitKind::NonPanicPathOnly {
721            // This case mirrors `apply_call_return_effect`.
722            let TerminatorEdges::AssignOnReturn { return_, .. } =
723                init_block_data.terminator().edges()
724            else {
725                ::rustc_middle::util::bug::bug_fmt(format_args!("`NonPanicPathOnly` should only be seen on terminators with return edges"));bug!("`NonPanicPathOnly` should only be seen on terminators with return edges");
726            };
727            queue.extend(return_.into_iter().map(BasicBlock::start_location));
728        } else {
729            // This case mirrors `apply_primary_terminator_effect`.
730            queue.extend(init_block_data.terminator().successors().map(BasicBlock::start_location));
731        }
732
733        let mut visited = FxIndexSet::default();
734        'outer: while let Some(loc) = queue.pop() {
735            if !visited.insert(loc) {
736                continue;
737            }
738            // Walk from `loc` to the end of its block, looking for `target` or a kill.
739            let block_data = &basic_blocks[loc.block];
740            for statement_index in loc.statement_index..=block_data.statements.len() {
741                if target == (Location { block: loc.block, statement_index }) {
742                    return true;
743                }
744                if let Some(stmt) = block_data.statements.get(statement_index)
745                    && let StatementKind::StorageDead(dead) = stmt.kind
746                    && dead == local
747                {
748                    continue 'outer;
749                }
750            }
751
752            queue.extend(block_data.terminator().successors().map(BasicBlock::start_location));
753        }
754        false
755    }
756}