rustc_mir_transform/
elaborate_box_derefs.rs1use rustc_abi::FieldIdx;
6use rustc_middle::mir::visit::MutVisitor;
7use rustc_middle::mir::*;
8use rustc_middle::span_bug;
9use rustc_middle::ty::{self, PatternKind, Ty, TyCtxt};
10
11use crate::PassPolicy;
12use crate::patch::MirPatch;
13
14fn build_ptr_tys<'tcx>(
16 tcx: TyCtxt<'tcx>,
17 pointee: Ty<'tcx>,
18 unique_def: ty::AdtDef<'tcx>,
19 nonnull_def: ty::AdtDef<'tcx>,
20) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
21 let args = tcx.mk_args(&[pointee.into()]);
22 let unique_ty = Ty::new_adt(tcx, unique_def, args);
23 let nonnull_ty = Ty::new_adt(tcx, nonnull_def, args);
24 let ptr_ty = Ty::new_imm_ptr(tcx, pointee);
25
26 (unique_ty, nonnull_ty, ptr_ty)
27}
28
29struct ElaborateBoxDerefVisitor<'a, 'tcx> {
30 tcx: TyCtxt<'tcx>,
31 local_decls: &'a mut LocalDecls<'tcx>,
32 patch: MirPatch<'tcx>,
33}
34
35impl<'a, 'tcx> MutVisitor<'tcx> for ElaborateBoxDerefVisitor<'a, 'tcx> {
36 fn tcx(&self) -> TyCtxt<'tcx> {
37 self.tcx
38 }
39
40 fn visit_place(
41 &mut self,
42 place: &mut Place<'tcx>,
43 context: visit::PlaceContext,
44 location: Location,
45 ) {
46 let tcx = self.tcx;
47
48 let base_ty = self.local_decls[place.local].ty;
49
50 if let Some(PlaceElem::Deref) = place.projection.first()
52 && let Some(boxed_ty) = base_ty.boxed_ty()
53 {
54 let source_info = self.local_decls[place.local].source_info;
55
56 let ptr_ty = Ty::new_imm_ptr(tcx, boxed_ty);
57
58 let ptr_local = self.patch.new_temp(ptr_ty, source_info.span);
59
60 let field_place =
63 Place::from(place.local).project_to_field(FieldIdx::ZERO, &*self.local_decls, tcx);
64 self.patch.add_assign(
65 location,
66 Place::from(ptr_local),
67 Rvalue::Cast(CastKind::BoxDerefTransmute, Operand::Copy(field_place), ptr_ty),
68 );
69
70 place.local = ptr_local;
71 }
72
73 self.super_place(place, context, location);
74 }
75}
76
77pub(super) struct ElaborateBoxDerefs;
78
79impl<'tcx> crate::MirPass<'tcx> for ElaborateBoxDerefs {
80 fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
81 let Some(def_id) = tcx.lang_items().owned_box() else { return };
83
84 let unique_did = tcx.adt_def(def_id).non_enum_variant().fields[FieldIdx::ZERO].did;
85
86 let Some(unique_def) =
87 tcx.type_of(unique_did).instantiate_identity().skip_norm_wip().ty_adt_def()
88 else {
89 span_bug!(tcx.def_span(unique_did), "expected Box to contain Unique")
90 };
91
92 let nonnull_did = unique_def.non_enum_variant().fields[FieldIdx::ZERO].did;
93
94 let Some(nonnull_def) =
95 tcx.type_of(nonnull_did).instantiate_identity().skip_norm_wip().ty_adt_def()
96 else {
97 span_bug!(tcx.def_span(nonnull_did), "expected Unique to contain Nonnull")
98 };
99
100 let patch = MirPatch::new(body);
101
102 let local_decls = &mut body.local_decls;
103
104 let mut visitor = ElaborateBoxDerefVisitor { tcx, local_decls, patch };
105
106 for (block, data) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() {
107 visitor.visit_basic_block_data(block, data);
108 }
109
110 visitor.patch.apply(body);
111
112 for debug_info in body.var_debug_info.iter_mut() {
113 if let VarDebugInfoContents::Place(place) = &mut debug_info.value {
114 let mut new_projections: Option<Vec<_>> = None;
115
116 for (base, elem) in place.iter_projections() {
117 let base_ty = base.ty(&body.local_decls, tcx).ty;
118
119 if let PlaceElem::Deref = elem
120 && let Some(boxed_ty) = base_ty.boxed_ty()
121 {
122 let new_projections =
124 new_projections.get_or_insert_with(|| base.projection.to_vec());
125
126 let (unique_ty, nonnull_ty, ptr_ty) =
127 build_ptr_tys(tcx, boxed_ty, unique_def, nonnull_def);
128
129 new_projections.extend_from_slice(&[
130 PlaceElem::Field(FieldIdx::ZERO, unique_ty),
131 PlaceElem::Field(FieldIdx::ZERO, nonnull_ty),
132 ]);
133 let pat_ty = Ty::new_pat(tcx, ptr_ty, tcx.mk_pat(PatternKind::NotNull));
136 new_projections.push(PlaceElem::Field(FieldIdx::ZERO, pat_ty));
137 new_projections.push(PlaceElem::Field(FieldIdx::ZERO, ptr_ty));
138 new_projections.push(PlaceElem::Deref);
139 } else if let Some(new_projections) = new_projections.as_mut() {
140 new_projections.push(elem);
142 }
143 }
144
145 if let Some(new_projections) = new_projections {
147 place.projection = tcx.mk_place_elems(&new_projections);
148 }
149 }
150 }
151 }
152
153 fn policy(&self, _sess: &rustc_session::Session) -> PassPolicy {
154 PassPolicy::Required
156 }
157}