1use rustc_abi::{FieldIdx, VariantIdx};
9use rustc_index::{IndexVec, indexvec};
10use rustc_middle::mir::visit::MutVisitor;
11use rustc_middle::mir::*;
12use rustc_middle::span_bug;
13use rustc_middle::ty::{self, Ty, TyCtxt};
14
15use crate::patch::MirPatch;
16
17fn build_ptr_tys<'tcx>(
19 tcx: TyCtxt<'tcx>,
20 pointee: Ty<'tcx>,
21 unique_def: ty::AdtDef<'tcx>,
22 nonnull_def: ty::AdtDef<'tcx>,
23) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
24 let args = tcx.mk_args(&[pointee.into()]);
25 let unique_ty = Ty::new_adt(tcx, unique_def, args);
26 let nonnull_ty = Ty::new_adt(tcx, nonnull_def, args);
27 let ptr_ty = Ty::new_imm_ptr(tcx, pointee);
28
29 (unique_ty, nonnull_ty, ptr_ty)
30}
31
32pub(super) fn build_projection<'tcx>(
34 unique_ty: Ty<'tcx>,
35 nonnull_ty: Ty<'tcx>,
36) -> [PlaceElem<'tcx>; 2] {
37 [PlaceElem::Field(FieldIdx::ZERO, unique_ty), PlaceElem::Field(FieldIdx::ZERO, nonnull_ty)]
38}
39
40struct ElaborateBoxDerefVisitor<'a, 'tcx> {
41 tcx: TyCtxt<'tcx>,
42 unique_def: ty::AdtDef<'tcx>,
43 nonnull_def: ty::AdtDef<'tcx>,
44 local_decls: &'a mut LocalDecls<'tcx>,
45 patch: MirPatch<'tcx>,
46}
47
48impl<'a, 'tcx> MutVisitor<'tcx> for ElaborateBoxDerefVisitor<'a, 'tcx> {
49 fn tcx(&self) -> TyCtxt<'tcx> {
50 self.tcx
51 }
52
53 fn visit_place(
54 &mut self,
55 place: &mut Place<'tcx>,
56 context: visit::PlaceContext,
57 location: Location,
58 ) {
59 let tcx = self.tcx;
60
61 let base_ty = self.local_decls[place.local].ty;
62
63 if let Some(PlaceElem::Deref) = place.projection.first()
65 && let Some(boxed_ty) = base_ty.boxed_ty()
66 {
67 let source_info = self.local_decls[place.local].source_info;
68
69 let (unique_ty, nonnull_ty, ptr_ty) =
70 build_ptr_tys(tcx, boxed_ty, self.unique_def, self.nonnull_def);
71
72 let ptr_local = self.patch.new_temp(ptr_ty, source_info.span);
73
74 self.patch.add_assign(
75 location,
76 Place::from(ptr_local),
77 Rvalue::Cast(
78 CastKind::Transmute,
79 Operand::Copy(
80 Place::from(place.local)
81 .project_deeper(&build_projection(unique_ty, nonnull_ty), tcx),
82 ),
83 ptr_ty,
84 ),
85 );
86
87 place.local = ptr_local;
88 }
89
90 self.super_place(place, context, location);
91 }
92
93 fn visit_statement(&mut self, stmt: &mut Statement<'tcx>, location: Location) {
94 self.super_statement(stmt, location);
95
96 let tcx = self.tcx;
97 let source_info = stmt.source_info;
98
99 if let StatementKind::Assign(box (_, ref mut rvalue)) = stmt.kind
100 && let Rvalue::ShallowInitBox(ref mut mutptr_to_u8, pointee) = *rvalue
101 && let ty::Adt(box_adt, box_args) = Ty::new_box(tcx, pointee).kind()
102 {
103 let args = tcx.mk_args(&[pointee.into()]);
104 let (unique_ty, nonnull_ty, ptr_ty) =
105 build_ptr_tys(tcx, pointee, self.unique_def, self.nonnull_def);
106 let adt_kind = |def: ty::AdtDef<'tcx>, args| {
107 Box::new(AggregateKind::Adt(def.did(), VariantIdx::ZERO, args, None, None))
108 };
109 let zst = |ty| {
110 Operand::Constant(Box::new(ConstOperand {
111 span: source_info.span,
112 user_ty: None,
113 const_: Const::zero_sized(ty),
114 }))
115 };
116
117 let constptr = self.patch.new_temp(ptr_ty, source_info.span);
118 self.patch.add_assign(
119 location,
120 constptr.into(),
121 Rvalue::Cast(CastKind::Transmute, mutptr_to_u8.clone(), ptr_ty),
122 );
123
124 let nonnull = self.patch.new_temp(nonnull_ty, source_info.span);
125 self.patch.add_assign(
126 location,
127 nonnull.into(),
128 Rvalue::Aggregate(
129 adt_kind(self.nonnull_def, args),
130 indexvec![Operand::Move(constptr.into())],
131 ),
132 );
133
134 let unique = self.patch.new_temp(unique_ty, source_info.span);
135 let phantomdata_ty =
136 self.unique_def.non_enum_variant().fields[FieldIdx::ONE].ty(tcx, args);
137 self.patch.add_assign(
138 location,
139 unique.into(),
140 Rvalue::Aggregate(
141 adt_kind(self.unique_def, args),
142 indexvec![Operand::Move(nonnull.into()), zst(phantomdata_ty)],
143 ),
144 );
145
146 let global_alloc_ty =
147 box_adt.non_enum_variant().fields[FieldIdx::ONE].ty(tcx, box_args);
148 *rvalue = Rvalue::Aggregate(
149 adt_kind(*box_adt, box_args),
150 indexvec![Operand::Move(unique.into()), zst(global_alloc_ty)],
151 );
152 }
153 }
154}
155
156pub(super) struct ElaborateBoxDerefs;
157
158impl<'tcx> crate::MirPass<'tcx> for ElaborateBoxDerefs {
159 fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
160 let Some(def_id) = tcx.lang_items().owned_box() else { return };
162
163 let unique_did = tcx.adt_def(def_id).non_enum_variant().fields[FieldIdx::ZERO].did;
164
165 let Some(unique_def) = tcx.type_of(unique_did).instantiate_identity().ty_adt_def() else {
166 span_bug!(tcx.def_span(unique_did), "expected Box to contain Unique")
167 };
168
169 let nonnull_did = unique_def.non_enum_variant().fields[FieldIdx::ZERO].did;
170
171 let Some(nonnull_def) = tcx.type_of(nonnull_did).instantiate_identity().ty_adt_def() else {
172 span_bug!(tcx.def_span(nonnull_did), "expected Unique to contain Nonnull")
173 };
174
175 let patch = MirPatch::new(body);
176
177 let local_decls = &mut body.local_decls;
178
179 let mut visitor =
180 ElaborateBoxDerefVisitor { tcx, unique_def, nonnull_def, local_decls, patch };
181
182 for (block, data) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() {
183 visitor.visit_basic_block_data(block, data);
184 }
185
186 visitor.patch.apply(body);
187
188 for debug_info in body.var_debug_info.iter_mut() {
189 if let VarDebugInfoContents::Place(place) = &mut debug_info.value {
190 let mut new_projections: Option<Vec<_>> = None;
191
192 for (base, elem) in place.iter_projections() {
193 let base_ty = base.ty(&body.local_decls, tcx).ty;
194
195 if let PlaceElem::Deref = elem
196 && let Some(boxed_ty) = base_ty.boxed_ty()
197 {
198 let new_projections =
200 new_projections.get_or_insert_with(|| base.projection.to_vec());
201
202 let (unique_ty, nonnull_ty, ptr_ty) =
203 build_ptr_tys(tcx, boxed_ty, unique_def, nonnull_def);
204
205 new_projections.extend_from_slice(&build_projection(unique_ty, nonnull_ty));
206 new_projections.push(PlaceElem::Field(FieldIdx::ZERO, ptr_ty));
209 new_projections.push(PlaceElem::Deref);
210 } else if let Some(new_projections) = new_projections.as_mut() {
211 new_projections.push(elem);
213 }
214 }
215
216 if let Some(new_projections) = new_projections {
218 place.projection = tcx.mk_place_elems(&new_projections);
219 }
220 }
221 }
222 }
223
224 fn is_required(&self) -> bool {
225 true
226 }
227}