rustc_mir_transform/
remove_uninit_drops.rs1use rustc_abi::FieldIdx;
2use rustc_index::bit_set::MixedBitSet;
3use rustc_middle::mir::{Body, TerminatorKind};
4use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, VariantDef};
5use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
6use rustc_mir_dataflow::move_paths::{LookupResult, MoveData, MovePathIndex};
7use rustc_mir_dataflow::{Analysis, MaybeReachable, move_path_children_matching};
8
9pub(super) struct RemoveUninitDrops;
18
19impl<'tcx> crate::MirPass<'tcx> for RemoveUninitDrops {
20 fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
21 let typing_env = body.typing_env(tcx);
22 let move_data = MoveData::gather_moves(body, tcx, |ty| ty.needs_drop(tcx, typing_env));
23
24 let mut maybe_inits = MaybeInitializedPlaces::new(tcx, body, &move_data)
25 .exclude_inactive_in_otherwise()
26 .iterate_to_fixpoint(tcx, body, Some("remove_uninit_drops"))
27 .into_results_cursor(body);
28
29 let mut to_remove = vec![];
30 for (bb, block) in body.basic_blocks.iter_enumerated() {
31 let terminator = block.terminator();
32 let TerminatorKind::Drop { place, .. } = &terminator.kind else { continue };
33
34 maybe_inits.seek_before_primary_effect(body.terminator_loc(bb));
35 let MaybeReachable::Reachable(maybe_inits) = maybe_inits.get() else { continue };
36
37 let LookupResult::Exact(mpi) = move_data.rev_lookup.find(place.as_ref()) else {
39 continue;
40 };
41
42 let should_keep = is_needs_drop_and_init(
43 tcx,
44 typing_env,
45 maybe_inits,
46 &move_data,
47 place.ty(body, tcx).ty,
48 mpi,
49 );
50 if !should_keep {
51 to_remove.push(bb)
52 }
53 }
54
55 for bb in to_remove {
56 let block = &mut body.basic_blocks_mut()[bb];
57
58 let TerminatorKind::Drop { target, .. } = &block.terminator().kind else {
59 unreachable!()
60 };
61
62 block.terminator_mut().kind = TerminatorKind::Goto { target: *target };
64 }
65 }
66
67 fn is_required(&self) -> bool {
68 true
69 }
70}
71
72fn is_needs_drop_and_init<'tcx>(
73 tcx: TyCtxt<'tcx>,
74 typing_env: ty::TypingEnv<'tcx>,
75 maybe_inits: &MixedBitSet<MovePathIndex>,
76 move_data: &MoveData<'tcx>,
77 ty: Ty<'tcx>,
78 mpi: MovePathIndex,
79) -> bool {
80 if !maybe_inits.contains(mpi) || !ty.needs_drop(tcx, typing_env) {
82 return false;
83 }
84
85 let field_needs_drop_and_init = |(f, f_ty, mpi)| {
86 let child = move_path_children_matching(move_data, mpi, |x| x.is_field_to(f));
87 let Some(mpi) = child else {
88 return Ty::needs_drop(f_ty, tcx, typing_env);
89 };
90
91 is_needs_drop_and_init(tcx, typing_env, maybe_inits, move_data, f_ty, mpi)
92 };
93
94 match ty.kind() {
97 ty::Adt(adt, args) => {
98 let dont_elaborate = adt.is_union() || adt.is_manually_drop() || adt.has_dtor(tcx);
99 if dont_elaborate {
100 return true;
101 }
102
103 adt.variants().iter_enumerated().any(|(vid, variant)| {
111 let mpi = if adt.is_enum() {
115 let downcast =
116 move_path_children_matching(move_data, mpi, |x| x.is_downcast_to(vid));
117 let Some(dc_mpi) = downcast else {
118 return variant_needs_drop(tcx, typing_env, args, variant);
119 };
120
121 dc_mpi
122 } else {
123 mpi
124 };
125
126 variant
127 .fields
128 .iter()
129 .enumerate()
130 .map(|(f, field)| (FieldIdx::from_usize(f), field.ty(tcx, args), mpi))
131 .any(field_needs_drop_and_init)
132 })
133 }
134
135 ty::Tuple(fields) => fields
136 .iter()
137 .enumerate()
138 .map(|(f, f_ty)| (FieldIdx::from_usize(f), f_ty, mpi))
139 .any(field_needs_drop_and_init),
140
141 _ => true,
142 }
143}
144
145fn variant_needs_drop<'tcx>(
146 tcx: TyCtxt<'tcx>,
147 typing_env: ty::TypingEnv<'tcx>,
148 args: GenericArgsRef<'tcx>,
149 variant: &VariantDef,
150) -> bool {
151 variant.fields.iter().any(|field| {
152 let f_ty = field.ty(tcx, args);
153 f_ty.needs_drop(tcx, typing_env)
154 })
155}