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