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