]>
Commit | Line | Data |
---|---|---|
923072b8 FG |
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 (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); | |
114 | ||
115 | let mut visitor = | |
116 | ElaborateBoxDerefVisitor { tcx, unique_did, nonnull_did, local_decls, patch }; | |
117 | ||
118 | for (block, BasicBlockData { statements, terminator, .. }) in | |
119 | basic_blocks.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 | } |