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
159impl<'tcx> MaybeInitializedPlaces<'_, 'tcx> {
160    /// Ensures definitely inactive variants are excluded from the set of initialized places for
161    /// blocks reached through an `otherwise` edge.
162    pub fn exclude_inactive_in_otherwise(mut self) -> Self {
163        self.exclude_inactive_in_otherwise = true;
164        self
165    }
166
167    pub fn skipping_unreachable_unwind(mut self) -> Self {
168        self.skip_unreachable_unwind = true;
169        self
170    }
171
172    pub fn is_unwind_dead(
173        &self,
174        place: mir::Place<'tcx>,
175        state: &<Self as Analysis<'tcx>>::Domain,
176    ) -> bool {
177        if let LookupResult::Exact(path) = self.move_data().rev_lookup.find(place.as_ref()) {
178            let mut maybe_live = false;
179            on_all_children_bits(self.move_data(), path, |child| {
180                maybe_live |= state.contains(child);
181            });
182            !maybe_live
183        } else {
184            false
185        }
186    }
187
188    fn update_bits(
189        state: &mut <Self as Analysis<'tcx>>::Domain,
190        path: MovePathIndex,
191        dfstate: DropFlagState,
192    ) {
193        match dfstate {
194            DropFlagState::Absent => state.kill(path),
195            DropFlagState::Present => state.gen_(path),
196        }
197    }
198}
199
200impl<'a, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'tcx> {
201    fn move_data(&self) -> &MoveData<'tcx> {
202        self.move_data
203    }
204}
205
206impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
207    /// There can be many more `MovePathIndex` than there are locals in a MIR body.
208    /// We use a mixed bitset to avoid paying too high a memory footprint.
209    type Domain = MaybeReachable<MixedBitSet<MovePathIndex>>;
210
211    type SwitchIntData = MaybePlacesSwitchIntData<'tcx>;
212
213    const NAME: &'static str = "maybe_init";
214
215    fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
216        // bottom = uninitialized
217        MaybeReachable::Unreachable
218    }
219
220    fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) {
221        *state =
222            MaybeReachable::Reachable(MixedBitSet::new_empty(self.move_data().move_paths.len()));
223        drop_flag_effects_for_function_entry(self.body, self.move_data, |path, s| {
224            if true {
    if !(s == DropFlagState::Present) {
        ::core::panicking::panic("assertion failed: s == DropFlagState::Present")
    };
};debug_assert!(s == DropFlagState::Present);
225            state.gen_(path);
226        });
227    }
228
229    fn apply_primary_statement_effect(
230        &self,
231        state: &mut Self::Domain,
232        statement: &mir::Statement<'tcx>,
233        location: Location,
234    ) {
235        drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| {
236            Self::update_bits(state, path, s)
237        });
238
239        // Mark all places as "maybe init" if they are mutably borrowed. See #90752.
240        if self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration
241            && let Some((_, rvalue)) = statement.kind.as_assign()
242            && let mir::Rvalue::Ref(_, mir::BorrowKind::Mut { .. }, place)
243                // FIXME: Does `&raw const foo` allow mutation? See #90413.
244                | mir::Rvalue::RawPtr(_, place) = rvalue
245            && let LookupResult::Exact(mpi) = self.move_data().rev_lookup.find(place.as_ref())
246        {
247            on_all_children_bits(self.move_data(), mpi, |child| {
248                state.gen_(child);
249            })
250        }
251    }
252
253    fn get_terminator_edges<'mir>(
254        &self,
255        state: &Self::Domain,
256        terminator: &'mir mir::Terminator<'tcx>,
257        _location: Location,
258    ) -> TerminatorEdges<'mir, 'tcx> {
259        // Note: this relies on `get_terminator_edges` being called before
260        // `apply_primary_terminator_effect` because the result of `is_unwind_dead` is affected by
261        // the `drop_flag_effects_for_location` in `apply_primary_terminator_effect`.
262        let mut edges = terminator.edges();
263        if self.skip_unreachable_unwind
264            && let mir::TerminatorKind::Drop { target, unwind, place, replace: _, drop: _ } =
265                terminator.kind
266            && #[allow(non_exhaustive_omitted_patterns)] match unwind {
    mir::UnwindAction::Cleanup(_) => true,
    _ => false,
}matches!(unwind, mir::UnwindAction::Cleanup(_))
267            && self.is_unwind_dead(place, state)
268        {
269            edges = TerminatorEdges::Single(target);
270        }
271        edges
272    }
273
274    fn apply_primary_terminator_effect(
275        &self,
276        state: &mut Self::Domain,
277        _terminator: &mir::Terminator<'tcx>,
278        location: Location,
279    ) {
280        drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| {
281            Self::update_bits(state, path, s)
282        });
283    }
284
285    fn apply_call_return_effect(
286        &self,
287        state: &mut Self::Domain,
288        _block: mir::BasicBlock,
289        return_places: CallReturnPlaces<'_, 'tcx>,
290    ) {
291        return_places.for_each(|place| {
292            // when a call returns successfully, that means we need to set
293            // the bits for that dest_place to 1 (initialized).
294            on_lookup_result_bits(
295                self.move_data(),
296                self.move_data().rev_lookup.find(place.as_ref()),
297                |mpi| {
298                    state.gen_(mpi);
299                },
300            );
301        });
302    }
303
304    fn get_switch_int_data(
305        &self,
306        block: mir::BasicBlock,
307        targets: &mir::SwitchTargets,
308        discr: &mir::Operand<'tcx>,
309    ) -> Option<Self::SwitchIntData> {
310        if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration {
311            return None;
312        }
313
314        MaybePlacesSwitchIntData::new(self.tcx, self.body, block, targets, discr)
315    }
316
317    fn apply_switch_int_edge_effect(
318        &self,
319        state: &mut Self::Domain,
320        data: &Self::SwitchIntData,
321        target_idx: SwitchTargetIndex,
322    ) {
323        let inactive_variants = match target_idx {
324            SwitchTargetIndex::Normal(target_idx) => {
325                InactiveVariants::Active(data.variants[target_idx])
326            }
327            SwitchTargetIndex::Otherwise if self.exclude_inactive_in_otherwise => {
328                InactiveVariants::Inactives(data.variants.clone())
329            }
330            _ => return,
331        };
332
333        // Kill all move paths that correspond to variants we know to be inactive along this
334        // particular outgoing edge of a `SwitchInt`.
335        drop_flag_effects::on_all_inactive_variants(
336            self.move_data,
337            data.enum_place,
338            &inactive_variants,
339            |mpi| state.kill(mpi),
340        );
341    }
342}
343
344/// `MaybeUninitializedPlaces` tracks all places that might be
345/// uninitialized upon reaching a particular point in the control flow
346/// for a function.
347///
348/// For example, in code like the following, we have corresponding
349/// dataflow information shown in the right-hand comments.
350///
351/// ```rust
352/// struct S;
353/// #[rustfmt::skip]
354/// fn foo(p: bool) {                           // maybe-uninit:
355///                                             // {a, b, c, d}
356///     let a = S; let mut b = S; let c; let d; // {      c, d}
357///
358///     if p {
359///         drop(a);                            // {a,    c, d}
360///         b = S;                              // {a,    c, d}
361///
362///     } else {
363///         drop(b);                            // {   b, c, d}
364///         d = S;                              // {   b, c   }
365///
366///     }                                       // {a, b, c, d}
367///
368///     c = S;                                  // {a, b,    d}
369/// }
370/// ```
371///
372/// To determine whether a place is *definitely* uninitialized at a
373/// particular control-flow point, one can take the set-complement
374/// of the data from `MaybeInitializedPlaces` at the corresponding
375/// control-flow point.
376///
377/// Similarly, at a given `drop` statement, the set-intersection
378/// between this data and `MaybeInitializedPlaces` yields the set of
379/// places that would require a dynamic drop-flag at that statement.
380pub struct MaybeUninitializedPlaces<'a, 'tcx> {
381    tcx: TyCtxt<'tcx>,
382    body: &'a Body<'tcx>,
383    move_data: &'a MoveData<'tcx>,
384
385    mark_inactive_variants_as_uninit: bool,
386    skip_unreachable_unwind: DenseBitSet<mir::BasicBlock>,
387}
388
389impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
390    pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>) -> Self {
391        MaybeUninitializedPlaces {
392            tcx,
393            body,
394            move_data,
395            mark_inactive_variants_as_uninit: false,
396            skip_unreachable_unwind: DenseBitSet::new_empty(body.basic_blocks.len()),
397        }
398    }
399}
400
401impl<'tcx> MaybeUninitializedPlaces<'_, 'tcx> {
402    /// Causes inactive enum variants to be marked as "maybe uninitialized" after a switch on an
403    /// enum discriminant.
404    ///
405    /// This is correct in a vacuum but is not the default because it causes problems in the borrow
406    /// checker, where this information gets propagated along `FakeEdge`s.
407    pub fn mark_inactive_variants_as_uninit(mut self) -> Self {
408        self.mark_inactive_variants_as_uninit = true;
409        self
410    }
411
412    pub fn skipping_unreachable_unwind(
413        mut self,
414        unreachable_unwind: DenseBitSet<mir::BasicBlock>,
415    ) -> Self {
416        self.skip_unreachable_unwind = unreachable_unwind;
417        self
418    }
419
420    fn update_bits(
421        state: &mut <Self as Analysis<'tcx>>::Domain,
422        path: MovePathIndex,
423        dfstate: DropFlagState,
424    ) {
425        match dfstate {
426            DropFlagState::Absent => state.gen_(path),
427            DropFlagState::Present => state.kill(path),
428        }
429    }
430}
431
432impl<'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
433    fn move_data(&self) -> &MoveData<'tcx> {
434        self.move_data
435    }
436}
437
438/// There can be many more `MovePathIndex` than there are locals in a MIR body.
439/// We use a mixed bitset to avoid paying too high a memory footprint.
440pub type MaybeUninitializedPlacesDomain = MixedBitSet<MovePathIndex>;
441
442impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
443    type Domain = MaybeUninitializedPlacesDomain;
444
445    type SwitchIntData = MaybePlacesSwitchIntData<'tcx>;
446
447    const NAME: &'static str = "maybe_uninit";
448
449    fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
450        // bottom = initialized (`initialize_start_block` overwrites this on first entry)
451        MixedBitSet::new_empty(self.move_data().move_paths.len())
452    }
453
454    // sets state bits for Arg places
455    fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) {
456        // set all bits to 1 (uninit) before gathering counter-evidence
457        state.insert_all();
458
459        drop_flag_effects_for_function_entry(self.body, self.move_data, |path, s| {
460            if true {
    if !(s == DropFlagState::Present) {
        ::core::panicking::panic("assertion failed: s == DropFlagState::Present")
    };
};debug_assert!(s == DropFlagState::Present);
461            state.remove(path);
462        });
463    }
464
465    fn apply_primary_statement_effect(
466        &self,
467        state: &mut Self::Domain,
468        _statement: &mir::Statement<'tcx>,
469        location: Location,
470    ) {
471        drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| {
472            Self::update_bits(state, path, s)
473        });
474
475        // Unlike in `MaybeInitializedPlaces` above, we don't need to change the state when a
476        // mutable borrow occurs. Places cannot become uninitialized through a mutable reference.
477    }
478
479    fn get_terminator_edges<'mir>(
480        &self,
481        _state: &Self::Domain,
482        terminator: &'mir mir::Terminator<'tcx>,
483        location: Location,
484    ) -> TerminatorEdges<'mir, 'tcx> {
485        if self.skip_unreachable_unwind.contains(location.block) {
486            let mir::TerminatorKind::Drop { target, unwind, .. } = terminator.kind else { ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!() };
487            {
    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(_));
488            TerminatorEdges::Single(target)
489        } else {
490            terminator.edges()
491        }
492    }
493
494    fn apply_primary_terminator_effect(
495        &self,
496        state: &mut Self::Domain,
497        _terminator: &mir::Terminator<'tcx>,
498        location: Location,
499    ) {
500        drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| {
501            Self::update_bits(state, path, s)
502        });
503    }
504
505    fn apply_call_return_effect(
506        &self,
507        state: &mut Self::Domain,
508        _block: mir::BasicBlock,
509        return_places: CallReturnPlaces<'_, 'tcx>,
510    ) {
511        return_places.for_each(|place| {
512            // when a call returns successfully, that means we need to set
513            // the bits for that dest_place to 0 (initialized).
514            on_lookup_result_bits(
515                self.move_data(),
516                self.move_data().rev_lookup.find(place.as_ref()),
517                |mpi| {
518                    state.kill(mpi);
519                },
520            );
521        });
522    }
523
524    fn get_switch_int_data(
525        &self,
526        block: mir::BasicBlock,
527        targets: &mir::SwitchTargets,
528        discr: &mir::Operand<'tcx>,
529    ) -> Option<Self::SwitchIntData> {
530        if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration {
531            return None;
532        }
533
534        if !self.mark_inactive_variants_as_uninit {
535            return None;
536        }
537
538        MaybePlacesSwitchIntData::new(self.tcx, self.body, block, targets, discr)
539    }
540
541    fn apply_switch_int_edge_effect(
542        &self,
543        state: &mut Self::Domain,
544        data: &Self::SwitchIntData,
545        target_idx: SwitchTargetIndex,
546    ) {
547        let inactive_variants = match target_idx {
548            SwitchTargetIndex::Normal(target_idx) => {
549                InactiveVariants::Active(data.variants[target_idx])
550            }
551            SwitchTargetIndex::Otherwise => InactiveVariants::Inactives(data.variants.clone()),
552        };
553
554        // Mark all move paths that correspond to variants other than this one as maybe
555        // uninitialized (in reality, they are *definitely* uninitialized).
556        drop_flag_effects::on_all_inactive_variants(
557            self.move_data,
558            data.enum_place,
559            &inactive_variants,
560            |mpi| state.gen_(mpi),
561        );
562    }
563}
564
565/// `EverInitializedPlaces` tracks all initializations of locals that may have
566/// occurred upon reaching a particular point in the control flow for a
567/// function, without an intervening `StorageDead`.
568///
569/// This dataflow is used to determine if an immutable local variable may
570/// be assigned to.
571///
572/// For example, in code like the following, we have corresponding
573/// dataflow information shown in the right-hand comments.
574///
575/// ```rust
576/// struct S;
577/// #[rustfmt::skip]
578/// fn foo(p: bool) {                           // ever-init:
579///                                             // {p,           }
580///     let a = S; let mut b = S; let c; let d; // {p, a, b,     }
581///
582///     if p {
583///         drop(a);                            // {p, a, b,     }
584///         b = S;                              // {p, a, b,     }
585///
586///     } else {
587///         drop(b);                            // {p, a, b,     }
588///         d = S;                              // {p, a, b,    d}
589///
590///     }                                       // {p, a, b,    d}
591///
592///     c = S;                                  // {p, a, b, c, d}
593/// }
594/// ```
595pub struct EverInitializedPlaces<'a, 'tcx> {
596    body: &'a Body<'tcx>,
597    move_data: &'a MoveData<'tcx>,
598}
599
600impl<'a, 'tcx> EverInitializedPlaces<'a, 'tcx> {
601    pub fn new(body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>) -> Self {
602        EverInitializedPlaces { body, move_data }
603    }
604}
605
606impl EverInitializedPlaces<'_, '_> {
607    /// Whether the init of `local` at `init` can reach `target` via a path that doesn't pass
608    /// through a `StorageDead(local)`. Mirrors the gen/kill structure of `EverInitializedPlaces`.
609    pub fn init_reaches_location(
610        body: &Body<'_>,
611        local: Local,
612        init: Init,
613        target: Location,
614    ) -> bool {
615        let init_loc = match init.location {
616            // Arguments are initialized on entry, and `StorageDead` is never emitted for them, so
617            // they reach every location.
618            InitLocation::Argument(_) => return true,
619            InitLocation::Statement(init_loc) => init_loc,
620        };
621
622        // Worklist of locations to walk forward from, seeded with the location(s) following `init`.
623        let mut queue = ::alloc::vec::Vec::new()vec![];
624
625        let basic_blocks = &body.basic_blocks;
626        let init_block_data = &basic_blocks[init_loc.block];
627        if init_loc.statement_index < init_block_data.statements.len() {
628            // This case mirrors `apply_primary_statement_effect`.
629            queue.push(init_loc.successor_within_block());
630        } else if init.kind == InitKind::NonPanicPathOnly {
631            // This case mirrors `apply_call_return_effect`.
632            let TerminatorEdges::AssignOnReturn { return_, .. } =
633                init_block_data.terminator().edges()
634            else {
635                ::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");
636            };
637            queue.extend(return_.into_iter().map(BasicBlock::start_location));
638        } else {
639            // This case mirrors `apply_primary_terminator_effect`.
640            queue.extend(init_block_data.terminator().successors().map(BasicBlock::start_location));
641        }
642
643        let mut visited = FxIndexSet::default();
644        'outer: while let Some(loc) = queue.pop() {
645            if !visited.insert(loc) {
646                continue;
647            }
648            // Walk from `loc` to the end of its block, looking for `target` or a kill.
649            let block_data = &basic_blocks[loc.block];
650            for statement_index in loc.statement_index..=block_data.statements.len() {
651                if target == (Location { block: loc.block, statement_index }) {
652                    return true;
653                }
654                if let Some(stmt) = block_data.statements.get(statement_index)
655                    && let StatementKind::StorageDead(dead) = stmt.kind
656                    && dead == local
657                {
658                    continue 'outer;
659                }
660            }
661
662            queue.extend(block_data.terminator().successors().map(BasicBlock::start_location));
663        }
664        false
665    }
666}
667
668impl<'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'_, 'tcx> {
669    fn move_data(&self) -> &MoveData<'tcx> {
670        self.move_data
671    }
672}
673
674pub type EverInitializedPlacesDomain = DenseBitSet<Local>;
675
676impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
677    type Domain = EverInitializedPlacesDomain;
678
679    const NAME: &'static str = "ever_init";
680
681    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
682        // bottom = no initialized locals by default
683        DenseBitSet::new_empty(body.local_decls.len())
684    }
685
686    fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) {
687        for arg in body.args_iter() {
688            state.insert(arg);
689        }
690    }
691
692    {}
#[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("/rustc-dev/5a2be9f5f075d31e3ca5526b5b029881ce441253/compiler/rustc_mir_dataflow/src/impls/initialized.rs"),
                                    ::tracing_core::__macro_support::Option::Some(692u32),
                                    ::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")]
693    fn apply_primary_statement_effect(
694        &self,
695        state: &mut Self::Domain,
696        stmt: &mir::Statement<'tcx>,
697        location: Location,
698    ) {
699        let move_data = self.move_data();
700        let init_loc_map = &move_data.init_loc_map;
701
702        // Record inits of locals. Projections can be ignored.
703        state.gen_all(init_loc_map[location].iter().copied().filter_map(|ii| {
704            let init_mpi = move_data.inits[ii].path;
705            move_data.move_paths[init_mpi].place.as_local()
706        }));
707
708        // Kill on StorageDead, so that an immutable variable can
709        // be reinitialized on the next iteration of the loop.
710        if let mir::StatementKind::StorageDead(local) = stmt.kind {
711            state.kill(local);
712        }
713    }
714
715    {}
#[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("/rustc-dev/5a2be9f5f075d31e3ca5526b5b029881ce441253/compiler/rustc_mir_dataflow/src/impls/initialized.rs"),
                                    ::tracing_core::__macro_support::Option::Some(715u32),
                                    ::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")]
716    fn apply_primary_terminator_effect(
717        &self,
718        state: &mut Self::Domain,
719        _terminator: &mir::Terminator<'tcx>,
720        location: Location,
721    ) {
722        let move_data = self.move_data();
723        let init_loc_map = &move_data.init_loc_map;
724
725        // Record inits of locals. Projections can be ignored.
726        state.gen_all(init_loc_map[location].iter().copied().filter_map(|ii| {
727            let init = &move_data.inits[ii];
728            if init.kind != InitKind::NonPanicPathOnly {
729                move_data.move_paths[init.path].place.as_local()
730            } else {
731                None
732            }
733        }));
734    }
735
736    fn apply_call_return_effect(
737        &self,
738        state: &mut Self::Domain,
739        block: mir::BasicBlock,
740        _return_places: CallReturnPlaces<'_, 'tcx>,
741    ) {
742        let move_data = self.move_data();
743        let init_loc_map = &move_data.init_loc_map;
744
745        // Record inits of locals. Projections can be ignored.
746        let call_loc = self.body.terminator_loc(block);
747        state.gen_all(init_loc_map[call_loc].iter().copied().filter_map(|ii| {
748            let init = &move_data.inits[ii];
749            if init.kind == InitKind::NonPanicPathOnly {
750                move_data.move_paths[init.path].place.as_local()
751            } else {
752                None
753            }
754        }));
755    }
756}