rustc_mir_transform/
remove_zsts.rs1use rustc_middle::mir::visit::*;
4use rustc_middle::mir::*;
5use rustc_middle::ty::{self, Ty, TyCtxt};
6
7use crate::PassPolicy;
8
9pub(super) struct RemoveZsts;
10
11impl<'tcx> crate::MirPass<'tcx> for RemoveZsts {
12 fn policy(&self, sess: &rustc_session::Session) -> PassPolicy {
13 PassPolicy::optional_non_optimization(sess.mir_opt_level() > 0)
14 }
15
16 fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
17 if tcx.type_of(body.source.def_id()).instantiate_identity().skip_norm_wip().is_coroutine() {
19 return;
20 }
21
22 let typing_env = body.typing_env(tcx);
23 let local_decls = &body.local_decls;
24 let mut replacer = Replacer { tcx, typing_env, local_decls };
25 for var_debug_info in &mut body.var_debug_info {
26 replacer.visit_var_debug_info(var_debug_info);
27 }
28 for (bb, data) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() {
29 replacer.visit_basic_block_data(bb, data);
30 }
31 }
32}
33
34struct Replacer<'a, 'tcx> {
35 tcx: TyCtxt<'tcx>,
36 typing_env: ty::TypingEnv<'tcx>,
37 local_decls: &'a LocalDecls<'tcx>,
38}
39
40fn trivially_zst<'tcx>(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<bool> {
46 match ty.kind() {
47 ty::FnDef(..) | ty::Never => Some(true),
49 ty::Tuple(fields) if fields.is_empty() => Some(true),
50 ty::Array(_ty, len) if let Some(0) = len.try_to_target_usize(tcx) => Some(true),
51 ty::Bool
53 | ty::Char
54 | ty::Int(..)
55 | ty::Uint(..)
56 | ty::Float(..)
57 | ty::RawPtr(..)
58 | ty::Ref(..)
59 | ty::FnPtr(..) => Some(false),
60 ty::Coroutine(def_id, _) => {
61 if tcx.is_async_drop_in_place_coroutine(*def_id) { Some(false) } else { None }
64 }
65 _ => None,
67 }
68}
69
70impl<'tcx> Replacer<'_, 'tcx> {
71 fn known_to_be_zst(&self, ty: Ty<'tcx>) -> bool {
72 if let Some(is_zst) = trivially_zst(ty, self.tcx) {
73 is_zst
74 } else {
75 self.tcx
76 .layout_of(self.typing_env.as_query_input(ty))
77 .is_ok_and(|layout| layout.is_zst())
78 }
79 }
80
81 fn make_zst(&self, ty: Ty<'tcx>) -> ConstOperand<'tcx> {
82 debug_assert!(self.known_to_be_zst(ty));
83 ConstOperand {
84 span: rustc_span::DUMMY_SP,
85 user_ty: None,
86 const_: Const::Val(ConstValue::ZeroSized, ty),
87 }
88 }
89}
90
91impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
92 fn tcx(&self) -> TyCtxt<'tcx> {
93 self.tcx
94 }
95
96 fn visit_var_debug_info(&mut self, var_debug_info: &mut VarDebugInfo<'tcx>) {
97 match var_debug_info.value {
98 VarDebugInfoContents::Const(_) => {}
99 VarDebugInfoContents::Place(place) => {
100 let place_ty = place.ty(self.local_decls, self.tcx).ty;
101 if self.known_to_be_zst(place_ty) {
102 var_debug_info.value = VarDebugInfoContents::Const(self.make_zst(place_ty))
103 }
104 }
105 }
106 }
107
108 fn visit_operand(&mut self, operand: &mut Operand<'tcx>, _: Location) {
109 if let Operand::Constant(_) = operand {
110 return;
111 }
112 let op_ty = operand.ty(self.local_decls, self.tcx);
113 if self.known_to_be_zst(op_ty) {
114 *operand = Operand::Constant(Box::new(self.make_zst(op_ty)))
115 }
116 }
117
118 fn visit_statement(&mut self, statement: &mut Statement<'tcx>, loc: Location) {
119 let place_for_ty = match statement.kind {
120 StatementKind::Assign((place, ref rvalue)) => {
121 rvalue.is_safe_to_remove().then_some(place)
122 }
123 StatementKind::SetDiscriminant { ref place, variant_index: _ }
124 | StatementKind::PlaceMention(ref place) => Some(**place),
125 StatementKind::AscribeUserType((place, _), _) | StatementKind::FakeRead((_, place)) => {
126 Some(place)
127 }
128 StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => {
129 Some(local.into())
130 }
131 StatementKind::Coverage(_)
132 | StatementKind::Intrinsic(_)
133 | StatementKind::Nop
134 | StatementKind::BackwardIncompatibleDropHint { .. }
135 | StatementKind::ConstEvalCounter => None,
136 };
137 if let Some(place_for_ty) = place_for_ty
138 && let ty = place_for_ty.ty(self.local_decls, self.tcx).ty
139 && self.known_to_be_zst(ty)
140 {
141 statement.make_nop(true);
142 } else {
143 self.super_statement(statement, loc);
144 }
145 }
146}