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