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