]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
Rollup merge of #104967 - willcrichton:fix-scrape-examples, r=notriddle
[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::{Ty, TyCtxt};
12
13 /// Constructs the types used when accessing a Box's pointer
14 pub fn build_ptr_tys<'tcx>(
15     tcx: TyCtxt<'tcx>,
16     pointee: Ty<'tcx>,
17     unique_did: DefId,
18     nonnull_did: DefId,
19 ) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
20     let substs = tcx.intern_substs(&[pointee.into()]);
21     let unique_ty = tcx.bound_type_of(unique_did).subst(tcx, substs);
22     let nonnull_ty = tcx.bound_type_of(nonnull_did).subst(tcx, substs);
23     let ptr_ty = tcx.mk_imm_ptr(pointee);
24
25     (unique_ty, nonnull_ty, ptr_ty)
26 }
27
28 /// Constructs the projection needed to access a Box's pointer
29 pub fn build_projection<'tcx>(
30     unique_ty: Ty<'tcx>,
31     nonnull_ty: Ty<'tcx>,
32     ptr_ty: Ty<'tcx>,
33 ) -> [PlaceElem<'tcx>; 3] {
34     [
35         PlaceElem::Field(Field::new(0), unique_ty),
36         PlaceElem::Field(Field::new(0), nonnull_ty),
37         PlaceElem::Field(Field::new(0), ptr_ty),
38     ]
39 }
40
41 struct ElaborateBoxDerefVisitor<'tcx, 'a> {
42     tcx: TyCtxt<'tcx>,
43     unique_did: DefId,
44     nonnull_did: DefId,
45     local_decls: &'a mut LocalDecls<'tcx>,
46     patch: MirPatch<'tcx>,
47 }
48
49 impl<'tcx, 'a> MutVisitor<'tcx> for ElaborateBoxDerefVisitor<'tcx, 'a> {
50     fn tcx(&self) -> TyCtxt<'tcx> {
51         self.tcx
52     }
53
54     fn visit_place(
55         &mut self,
56         place: &mut Place<'tcx>,
57         context: visit::PlaceContext,
58         location: Location,
59     ) {
60         let tcx = self.tcx;
61
62         let base_ty = self.local_decls[place.local].ty;
63
64         // Derefer ensures that derefs are always the first projection
65         if place.projection.first() == Some(&PlaceElem::Deref) && base_ty.is_box() {
66             let source_info = self.local_decls[place.local].source_info;
67
68             let (unique_ty, nonnull_ty, ptr_ty) =
69                 build_ptr_tys(tcx, base_ty.boxed_ty(), self.unique_did, self.nonnull_did);
70
71             let ptr_local = self.patch.new_internal(ptr_ty, source_info.span);
72
73             self.patch.add_assign(
74                 location,
75                 Place::from(ptr_local),
76                 Rvalue::Use(Operand::Copy(
77                     Place::from(place.local)
78                         .project_deeper(&build_projection(unique_ty, nonnull_ty, ptr_ty), tcx),
79                 )),
80             );
81
82             place.local = ptr_local;
83         }
84
85         self.super_place(place, context, location);
86     }
87 }
88
89 pub struct ElaborateBoxDerefs;
90
91 impl<'tcx> MirPass<'tcx> for ElaborateBoxDerefs {
92     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
93         if let Some(def_id) = tcx.lang_items().owned_box() {
94             let unique_did = tcx.adt_def(def_id).non_enum_variant().fields[0].did;
95
96             let Some(nonnull_def) = tcx.type_of(unique_did).ty_adt_def() else {
97                 span_bug!(tcx.def_span(unique_did), "expected Box to contain Unique")
98             };
99
100             let nonnull_did = nonnull_def.non_enum_variant().fields[0].did;
101
102             let patch = MirPatch::new(body);
103
104             let local_decls = &mut body.local_decls;
105
106             let mut visitor =
107                 ElaborateBoxDerefVisitor { tcx, unique_did, nonnull_did, local_decls, patch };
108
109             for (block, data) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() {
110                 visitor.visit_basic_block_data(block, data);
111             }
112
113             visitor.patch.apply(body);
114
115             for debug_info in body.var_debug_info.iter_mut() {
116                 if let VarDebugInfoContents::Place(place) = &mut debug_info.value {
117                     let mut new_projections: Option<Vec<_>> = None;
118                     let mut last_deref = 0;
119
120                     for (i, (base, elem)) in place.iter_projections().enumerate() {
121                         let base_ty = base.ty(&body.local_decls, tcx).ty;
122
123                         if elem == PlaceElem::Deref && base_ty.is_box() {
124                             let new_projections = new_projections.get_or_insert_default();
125
126                             let (unique_ty, nonnull_ty, ptr_ty) =
127                                 build_ptr_tys(tcx, base_ty.boxed_ty(), unique_did, nonnull_did);
128
129                             new_projections.extend_from_slice(&base.projection[last_deref..]);
130                             new_projections.extend_from_slice(&build_projection(
131                                 unique_ty, nonnull_ty, ptr_ty,
132                             ));
133                             new_projections.push(PlaceElem::Deref);
134
135                             last_deref = i;
136                         }
137                     }
138
139                     if let Some(mut new_projections) = new_projections {
140                         new_projections.extend_from_slice(&place.projection[last_deref..]);
141                         place.projection = tcx.intern_place_elems(&new_projections);
142                     }
143                 }
144             }
145         } else {
146             // box is not present, this pass doesn't need to do anything
147         }
148     }
149 }