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