]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
Auto merge of #99725 - lcnr:dedup-region_bound_pairs, r=compiler-errors
[rust.git] / compiler / rustc_mir_transform / src / elaborate_box_derefs.rs
1 //! This pass transforms derefs of Box into a deref of the pointer inside Box.
2 //!
3 //! Box is not actually a pointer so it is incorrect to dereference it directly.
4
5 use crate::MirPass;
6 use rustc_hir::def_id::DefId;
7 use rustc_index::vec::Idx;
8 use rustc_middle::mir::patch::MirPatch;
9 use rustc_middle::mir::visit::MutVisitor;
10 use rustc_middle::mir::*;
11 use rustc_middle::ty::subst::Subst;
12 use rustc_middle::ty::{Ty, TyCtxt};
13
14 /// Constructs the types used when accessing a Box's pointer
15 pub fn 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 substs = tcx.intern_substs(&[pointee.into()]);
22     let unique_ty = tcx.bound_type_of(unique_did).subst(tcx, substs);
23     let nonnull_ty = tcx.bound_type_of(nonnull_did).subst(tcx, substs);
24     let ptr_ty = tcx.mk_imm_ptr(pointee);
25
26     (unique_ty, nonnull_ty, ptr_ty)
27 }
28
29 // Constructs the projection needed to access a Box's pointer
30 pub fn build_projection<'tcx>(
31     unique_ty: Ty<'tcx>,
32     nonnull_ty: Ty<'tcx>,
33     ptr_ty: Ty<'tcx>,
34 ) -> [PlaceElem<'tcx>; 3] {
35     [
36         PlaceElem::Field(Field::new(0), unique_ty),
37         PlaceElem::Field(Field::new(0), nonnull_ty),
38         PlaceElem::Field(Field::new(0), ptr_ty),
39     ]
40 }
41
42 struct ElaborateBoxDerefVisitor<'tcx, 'a> {
43     tcx: TyCtxt<'tcx>,
44     unique_did: DefId,
45     nonnull_did: DefId,
46     local_decls: &'a mut LocalDecls<'tcx>,
47     patch: MirPatch<'tcx>,
48 }
49
50 impl<'tcx, 'a> MutVisitor<'tcx> for ElaborateBoxDerefVisitor<'tcx, 'a> {
51     fn tcx(&self) -> TyCtxt<'tcx> {
52         self.tcx
53     }
54
55     fn visit_place(
56         &mut self,
57         place: &mut Place<'tcx>,
58         context: visit::PlaceContext,
59         location: Location,
60     ) {
61         let tcx = self.tcx;
62
63         let base_ty = self.local_decls[place.local].ty;
64
65         // Derefer ensures that derefs are always the first projection
66         if place.projection.first() == Some(&PlaceElem::Deref) && base_ty.is_box() {
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, base_ty.boxed_ty(), self.unique_did, self.nonnull_did);
71
72             let ptr_local = self.patch.new_temp(ptr_ty, source_info.span);
73             self.local_decls.push(LocalDecl::new(ptr_ty, source_info.span));
74
75             self.patch.add_statement(location, StatementKind::StorageLive(ptr_local));
76
77             self.patch.add_assign(
78                 location,
79                 Place::from(ptr_local),
80                 Rvalue::Use(Operand::Copy(
81                     Place::from(place.local)
82                         .project_deeper(&build_projection(unique_ty, nonnull_ty, ptr_ty), tcx),
83                 )),
84             );
85
86             place.local = ptr_local;
87
88             self.patch.add_statement(
89                 Location { block: location.block, statement_index: location.statement_index + 1 },
90                 StatementKind::StorageDead(ptr_local),
91             );
92         }
93
94         self.super_place(place, context, location);
95     }
96 }
97
98 pub struct ElaborateBoxDerefs;
99
100 impl<'tcx> MirPass<'tcx> for ElaborateBoxDerefs {
101     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
102         if let Some(def_id) = tcx.lang_items().owned_box() {
103             let unique_did = tcx.adt_def(def_id).non_enum_variant().fields[0].did;
104
105             let Some(nonnull_def) = tcx.type_of(unique_did).ty_adt_def() else {
106                 span_bug!(tcx.def_span(unique_did), "expected Box to contain Unique")
107             };
108
109             let nonnull_did = nonnull_def.non_enum_variant().fields[0].did;
110
111             let patch = MirPatch::new(body);
112
113             let local_decls = &mut body.local_decls;
114
115             let mut visitor =
116                 ElaborateBoxDerefVisitor { tcx, unique_did, nonnull_did, local_decls, patch };
117
118             for (block, BasicBlockData { statements, terminator, .. }) in
119                 body.basic_blocks.as_mut().iter_enumerated_mut()
120             {
121                 let mut index = 0;
122                 for statement in statements {
123                     let location = Location { block, statement_index: index };
124                     visitor.visit_statement(statement, location);
125                     index += 1;
126                 }
127
128                 if let Some(terminator) = terminator
129                 && !matches!(terminator.kind, TerminatorKind::Yield{..})
130                 {
131                     let location = Location { block, statement_index: index };
132                     visitor.visit_terminator(terminator, location);
133                 }
134
135                 let location = Location { block, statement_index: index };
136                 match terminator {
137                     // yielding into a box is handled when lowering generators
138                     Some(Terminator { kind: TerminatorKind::Yield { value, .. }, .. }) => {
139                         visitor.visit_operand(value, location);
140                     }
141                     Some(terminator) => {
142                         visitor.visit_terminator(terminator, location);
143                     }
144                     None => {}
145                 }
146             }
147
148             visitor.patch.apply(body);
149
150             for debug_info in body.var_debug_info.iter_mut() {
151                 if let VarDebugInfoContents::Place(place) = &mut debug_info.value {
152                     let mut new_projections: Option<Vec<_>> = None;
153                     let mut last_deref = 0;
154
155                     for (i, (base, elem)) in place.iter_projections().enumerate() {
156                         let base_ty = base.ty(&body.local_decls, tcx).ty;
157
158                         if elem == PlaceElem::Deref && base_ty.is_box() {
159                             let new_projections = new_projections.get_or_insert_default();
160
161                             let (unique_ty, nonnull_ty, ptr_ty) =
162                                 build_ptr_tys(tcx, base_ty.boxed_ty(), unique_did, nonnull_did);
163
164                             new_projections.extend_from_slice(&base.projection[last_deref..]);
165                             new_projections.extend_from_slice(&build_projection(
166                                 unique_ty, nonnull_ty, ptr_ty,
167                             ));
168                             new_projections.push(PlaceElem::Deref);
169
170                             last_deref = i;
171                         }
172                     }
173
174                     if let Some(mut new_projections) = new_projections {
175                         new_projections.extend_from_slice(&place.projection[last_deref..]);
176                         place.projection = tcx.intern_place_elems(&new_projections);
177                     }
178                 }
179             }
180         } else {
181             // box is not present, this pass doesn't need to do anything
182         }
183     }
184 }