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
24pub struct MaybePlacesSwitchIntData<'tcx> {
26 enum_place: mir::Place<'tcx>,
27
28 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 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 let mut discriminants = enum_def.discriminants(tcx);
71 let variants = targets
72 .all_values()
73 .iter()
74 .map(|value| {
75 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 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
103pub 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 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
193pub 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 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
274pub 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 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 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 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 | 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 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 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 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
485pub 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 MixedBitSet::new_empty(self.move_data().move_paths.len())
499 }
500
501 fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) {
503 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 }
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 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 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 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 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 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 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 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 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 InitLocation::Argument(_) => return true,
709 InitLocation::Statement(init_loc) => init_loc,
710 };
711
712 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 queue.push(init_loc.successor_within_block());
720 } else if init.kind == InitKind::NonPanicPathOnly {
721 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 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 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}