1use std::assert_matches::assert_matches;
2
3use rustc_abi::VariantIdx;
4use rustc_index::Idx;
5use rustc_index::bit_set::{DenseBitSet, MixedBitSet};
6use rustc_middle::bug;
7use rustc_middle::mir::{
8 self, Body, CallReturnPlaces, Location, SwitchTargetValue, TerminatorEdges,
9};
10use rustc_middle::ty::util::Discr;
11use rustc_middle::ty::{self, TyCtxt};
12use tracing::{debug, instrument};
13
14use crate::drop_flag_effects::DropFlagState;
15use crate::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex};
16use crate::{
17 Analysis, GenKill, MaybeReachable, drop_flag_effects, drop_flag_effects_for_function_entry,
18 drop_flag_effects_for_location, on_all_children_bits, on_lookup_result_bits,
19};
20
21pub struct MaybePlacesSwitchIntData<'tcx> {
23 enum_place: mir::Place<'tcx>,
24 discriminants: Vec<(VariantIdx, Discr<'tcx>)>,
25 index: usize,
26}
27
28impl<'tcx> MaybePlacesSwitchIntData<'tcx> {
29 fn next_discr(&mut self, value: u128) -> VariantIdx {
33 loop {
35 let (variant, discr) = self.discriminants[self.index];
36 self.index += 1;
37 if discr.val == value {
38 return variant;
39 }
40 }
41 }
42}
43
44impl<'tcx> MaybePlacesSwitchIntData<'tcx> {
45 fn new(
46 tcx: TyCtxt<'tcx>,
47 body: &Body<'tcx>,
48 block: mir::BasicBlock,
49 discr: &mir::Operand<'tcx>,
50 ) -> Option<Self> {
51 let Some(discr) = discr.place() else { return None };
52
53 let block_data = &body[block];
65 for statement in block_data.statements.iter().rev() {
66 match statement.kind {
67 mir::StatementKind::Assign(box (lhs, mir::Rvalue::Discriminant(enum_place)))
68 if lhs == discr =>
69 {
70 match enum_place.ty(body, tcx).ty.kind() {
71 ty::Adt(enum_def, _) => {
72 return Some(MaybePlacesSwitchIntData {
73 enum_place,
74 discriminants: enum_def.discriminants(tcx).collect(),
75 index: 0,
76 });
77 }
78
79 ty::Coroutine(..) => break,
83
84 t => bug!("`discriminant` called on unexpected type {:?}", t),
85 }
86 }
87 mir::StatementKind::Coverage(_) => continue,
88 _ => break,
89 }
90 }
91 None
92 }
93}
94
95pub struct MaybeInitializedPlaces<'a, 'tcx> {
131 tcx: TyCtxt<'tcx>,
132 body: &'a Body<'tcx>,
133 move_data: &'a MoveData<'tcx>,
134 skip_unreachable_unwind: bool,
135}
136
137impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
138 pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>) -> Self {
139 MaybeInitializedPlaces { tcx, body, move_data, skip_unreachable_unwind: false }
140 }
141
142 pub fn skipping_unreachable_unwind(mut self) -> Self {
143 self.skip_unreachable_unwind = true;
144 self
145 }
146
147 pub fn is_unwind_dead(
148 &self,
149 place: mir::Place<'tcx>,
150 state: &<Self as Analysis<'tcx>>::Domain,
151 ) -> bool {
152 if let LookupResult::Exact(path) = self.move_data().rev_lookup.find(place.as_ref()) {
153 let mut maybe_live = false;
154 on_all_children_bits(self.move_data(), path, |child| {
155 maybe_live |= state.contains(child);
156 });
157 !maybe_live
158 } else {
159 false
160 }
161 }
162}
163
164impl<'a, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'tcx> {
165 fn move_data(&self) -> &MoveData<'tcx> {
166 self.move_data
167 }
168}
169
170pub struct MaybeUninitializedPlaces<'a, 'tcx> {
206 tcx: TyCtxt<'tcx>,
207 body: &'a Body<'tcx>,
208 move_data: &'a MoveData<'tcx>,
209
210 mark_inactive_variants_as_uninit: bool,
211 skip_unreachable_unwind: DenseBitSet<mir::BasicBlock>,
212}
213
214impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
215 pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>) -> Self {
216 MaybeUninitializedPlaces {
217 tcx,
218 body,
219 move_data,
220 mark_inactive_variants_as_uninit: false,
221 skip_unreachable_unwind: DenseBitSet::new_empty(body.basic_blocks.len()),
222 }
223 }
224
225 pub fn mark_inactive_variants_as_uninit(mut self) -> Self {
231 self.mark_inactive_variants_as_uninit = true;
232 self
233 }
234
235 pub fn skipping_unreachable_unwind(
236 mut self,
237 unreachable_unwind: DenseBitSet<mir::BasicBlock>,
238 ) -> Self {
239 self.skip_unreachable_unwind = unreachable_unwind;
240 self
241 }
242}
243
244impl<'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
245 fn move_data(&self) -> &MoveData<'tcx> {
246 self.move_data
247 }
248}
249
250pub struct EverInitializedPlaces<'a, 'tcx> {
280 body: &'a Body<'tcx>,
281 move_data: &'a MoveData<'tcx>,
282}
283
284impl<'a, 'tcx> EverInitializedPlaces<'a, 'tcx> {
285 pub fn new(body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>) -> Self {
286 EverInitializedPlaces { body, move_data }
287 }
288}
289
290impl<'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'_, 'tcx> {
291 fn move_data(&self) -> &MoveData<'tcx> {
292 self.move_data
293 }
294}
295
296impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
297 fn update_bits(
298 state: &mut <Self as Analysis<'tcx>>::Domain,
299 path: MovePathIndex,
300 dfstate: DropFlagState,
301 ) {
302 match dfstate {
303 DropFlagState::Absent => state.kill(path),
304 DropFlagState::Present => state.gen_(path),
305 }
306 }
307}
308
309impl<'tcx> MaybeUninitializedPlaces<'_, 'tcx> {
310 fn update_bits(
311 state: &mut <Self as Analysis<'tcx>>::Domain,
312 path: MovePathIndex,
313 dfstate: DropFlagState,
314 ) {
315 match dfstate {
316 DropFlagState::Absent => state.gen_(path),
317 DropFlagState::Present => state.kill(path),
318 }
319 }
320}
321
322impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
323 type Domain = MaybeReachable<MixedBitSet<MovePathIndex>>;
326
327 type SwitchIntData = MaybePlacesSwitchIntData<'tcx>;
328
329 const NAME: &'static str = "maybe_init";
330
331 fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
332 MaybeReachable::Unreachable
334 }
335
336 fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) {
337 *state =
338 MaybeReachable::Reachable(MixedBitSet::new_empty(self.move_data().move_paths.len()));
339 drop_flag_effects_for_function_entry(self.body, self.move_data, |path, s| {
340 assert!(s == DropFlagState::Present);
341 state.gen_(path);
342 });
343 }
344
345 fn apply_primary_statement_effect(
346 &mut self,
347 state: &mut Self::Domain,
348 statement: &mir::Statement<'tcx>,
349 location: Location,
350 ) {
351 drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| {
352 Self::update_bits(state, path, s)
353 });
354
355 if self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration
357 && let Some((_, rvalue)) = statement.kind.as_assign()
358 && let mir::Rvalue::Ref(_, mir::BorrowKind::Mut { .. }, place)
359 | mir::Rvalue::RawPtr(_, place) = rvalue
361 && let LookupResult::Exact(mpi) = self.move_data().rev_lookup.find(place.as_ref())
362 {
363 on_all_children_bits(self.move_data(), mpi, |child| {
364 state.gen_(child);
365 })
366 }
367 }
368
369 fn apply_primary_terminator_effect<'mir>(
370 &mut self,
371 state: &mut Self::Domain,
372 terminator: &'mir mir::Terminator<'tcx>,
373 location: Location,
374 ) -> TerminatorEdges<'mir, 'tcx> {
375 let mut edges = terminator.edges();
378 if self.skip_unreachable_unwind
379 && let mir::TerminatorKind::Drop { target, unwind, place, replace: _ } = terminator.kind
380 && matches!(unwind, mir::UnwindAction::Cleanup(_))
381 && self.is_unwind_dead(place, state)
382 {
383 edges = TerminatorEdges::Single(target);
384 }
385 drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| {
386 Self::update_bits(state, path, s)
387 });
388 edges
389 }
390
391 fn apply_call_return_effect(
392 &mut self,
393 state: &mut Self::Domain,
394 _block: mir::BasicBlock,
395 return_places: CallReturnPlaces<'_, 'tcx>,
396 ) {
397 return_places.for_each(|place| {
398 on_lookup_result_bits(
401 self.move_data(),
402 self.move_data().rev_lookup.find(place.as_ref()),
403 |mpi| {
404 state.gen_(mpi);
405 },
406 );
407 });
408 }
409
410 fn get_switch_int_data(
411 &mut self,
412 block: mir::BasicBlock,
413 discr: &mir::Operand<'tcx>,
414 ) -> Option<Self::SwitchIntData> {
415 if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration {
416 return None;
417 }
418
419 MaybePlacesSwitchIntData::new(self.tcx, self.body, block, discr)
420 }
421
422 fn apply_switch_int_edge_effect(
423 &mut self,
424 data: &mut Self::SwitchIntData,
425 state: &mut Self::Domain,
426 value: SwitchTargetValue,
427 ) {
428 if let SwitchTargetValue::Normal(value) = value {
429 drop_flag_effects::on_all_inactive_variants(
432 self.move_data,
433 data.enum_place,
434 data.next_discr(value),
435 |mpi| state.kill(mpi),
436 );
437 }
438 }
439}
440
441pub type MaybeUninitializedPlacesDomain = MixedBitSet<MovePathIndex>;
444
445impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
446 type Domain = MaybeUninitializedPlacesDomain;
447
448 type SwitchIntData = MaybePlacesSwitchIntData<'tcx>;
449
450 const NAME: &'static str = "maybe_uninit";
451
452 fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
453 MixedBitSet::new_empty(self.move_data().move_paths.len())
455 }
456
457 fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) {
459 state.insert_all();
461
462 drop_flag_effects_for_function_entry(self.body, self.move_data, |path, s| {
463 assert!(s == DropFlagState::Present);
464 state.remove(path);
465 });
466 }
467
468 fn apply_primary_statement_effect(
469 &mut self,
470 state: &mut Self::Domain,
471 _statement: &mir::Statement<'tcx>,
472 location: Location,
473 ) {
474 drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| {
475 Self::update_bits(state, path, s)
476 });
477
478 }
481
482 fn apply_primary_terminator_effect<'mir>(
483 &mut self,
484 state: &mut Self::Domain,
485 terminator: &'mir mir::Terminator<'tcx>,
486 location: Location,
487 ) -> TerminatorEdges<'mir, 'tcx> {
488 drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| {
489 Self::update_bits(state, path, s)
490 });
491 if self.skip_unreachable_unwind.contains(location.block) {
492 let mir::TerminatorKind::Drop { target, unwind, .. } = terminator.kind else { bug!() };
493 assert_matches!(unwind, mir::UnwindAction::Cleanup(_));
494 TerminatorEdges::Single(target)
495 } else {
496 terminator.edges()
497 }
498 }
499
500 fn apply_call_return_effect(
501 &mut self,
502 state: &mut Self::Domain,
503 _block: mir::BasicBlock,
504 return_places: CallReturnPlaces<'_, 'tcx>,
505 ) {
506 return_places.for_each(|place| {
507 on_lookup_result_bits(
510 self.move_data(),
511 self.move_data().rev_lookup.find(place.as_ref()),
512 |mpi| {
513 state.kill(mpi);
514 },
515 );
516 });
517 }
518
519 fn get_switch_int_data(
520 &mut self,
521 block: mir::BasicBlock,
522 discr: &mir::Operand<'tcx>,
523 ) -> Option<Self::SwitchIntData> {
524 if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration {
525 return None;
526 }
527
528 if !self.mark_inactive_variants_as_uninit {
529 return None;
530 }
531
532 MaybePlacesSwitchIntData::new(self.tcx, self.body, block, discr)
533 }
534
535 fn apply_switch_int_edge_effect(
536 &mut self,
537 data: &mut Self::SwitchIntData,
538 state: &mut Self::Domain,
539 value: SwitchTargetValue,
540 ) {
541 if let SwitchTargetValue::Normal(value) = value {
542 drop_flag_effects::on_all_inactive_variants(
545 self.move_data,
546 data.enum_place,
547 data.next_discr(value),
548 |mpi| state.gen_(mpi),
549 );
550 }
551 }
552}
553
554pub type EverInitializedPlacesDomain = MixedBitSet<InitIndex>;
557
558impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
559 type Domain = EverInitializedPlacesDomain;
560
561 const NAME: &'static str = "ever_init";
562
563 fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
564 MixedBitSet::new_empty(self.move_data().inits.len())
566 }
567
568 fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) {
569 for arg_init in 0..body.arg_count {
570 state.insert(InitIndex::new(arg_init));
571 }
572 }
573
574 #[instrument(skip(self, state), level = "debug")]
575 fn apply_primary_statement_effect(
576 &mut self,
577 state: &mut Self::Domain,
578 stmt: &mir::Statement<'tcx>,
579 location: Location,
580 ) {
581 let move_data = self.move_data();
582 let init_path_map = &move_data.init_path_map;
583 let init_loc_map = &move_data.init_loc_map;
584 let rev_lookup = &move_data.rev_lookup;
585
586 debug!("initializes move_indexes {:?}", init_loc_map[location]);
587 state.gen_all(init_loc_map[location].iter().copied());
588
589 if let mir::StatementKind::StorageDead(local) = stmt.kind {
590 if let Some(move_path_index) = rev_lookup.find_local(local) {
593 debug!(
594 "clears the ever initialized status of {:?}",
595 init_path_map[move_path_index]
596 );
597 state.kill_all(init_path_map[move_path_index].iter().copied());
598 }
599 }
600 }
601
602 #[instrument(skip(self, state, terminator), level = "debug")]
603 fn apply_primary_terminator_effect<'mir>(
604 &mut self,
605 state: &mut Self::Domain,
606 terminator: &'mir mir::Terminator<'tcx>,
607 location: Location,
608 ) -> TerminatorEdges<'mir, 'tcx> {
609 let (body, move_data) = (self.body, self.move_data());
610 let term = body[location.block].terminator();
611 let init_loc_map = &move_data.init_loc_map;
612 debug!(?term);
613 debug!("initializes move_indexes {:?}", init_loc_map[location]);
614 state.gen_all(
615 init_loc_map[location]
616 .iter()
617 .filter(|init_index| {
618 move_data.inits[**init_index].kind != InitKind::NonPanicPathOnly
619 })
620 .copied(),
621 );
622 terminator.edges()
623 }
624
625 fn apply_call_return_effect(
626 &mut self,
627 state: &mut Self::Domain,
628 block: mir::BasicBlock,
629 _return_places: CallReturnPlaces<'_, 'tcx>,
630 ) {
631 let move_data = self.move_data();
632 let init_loc_map = &move_data.init_loc_map;
633
634 let call_loc = self.body.terminator_loc(block);
635 for init_index in &init_loc_map[call_loc] {
636 state.gen_(*init_index);
637 }
638 }
639}