1 //! This pass transforms derefs of Box into a deref of the pointer inside Box.
3 //! Box is not actually a pointer so it is incorrect to dereference it directly.
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}
;
14 /// Constructs the types used when accessing a Box's pointer
15 pub fn build_ptr_tys
<'tcx
>(
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
);
26 (unique_ty
, nonnull_ty
, ptr_ty
)
29 // Constructs the projection needed to access a Box's pointer
30 pub fn build_projection
<'tcx
>(
34 ) -> [PlaceElem
<'tcx
>; 3] {
36 PlaceElem
::Field(Field
::new(0), unique_ty
),
37 PlaceElem
::Field(Field
::new(0), nonnull_ty
),
38 PlaceElem
::Field(Field
::new(0), ptr_ty
),
42 struct ElaborateBoxDerefVisitor
<'tcx
, 'a
> {
46 local_decls
: &'a
mut LocalDecls
<'tcx
>,
47 patch
: MirPatch
<'tcx
>,
50 impl<'tcx
, 'a
> MutVisitor
<'tcx
> for ElaborateBoxDerefVisitor
<'tcx
, 'a
> {
51 fn tcx(&self) -> TyCtxt
<'tcx
> {
57 place
: &mut Place
<'tcx
>,
58 context
: visit
::PlaceContext
,
63 let base_ty
= self.local_decls
[place
.local
].ty
;
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
;
69 let (unique_ty
, nonnull_ty
, ptr_ty
) =
70 build_ptr_tys(tcx
, base_ty
.boxed_ty(), self.unique_did
, self.nonnull_did
);
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
));
75 self.patch
.add_statement(location
, StatementKind
::StorageLive(ptr_local
));
77 self.patch
.add_assign(
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
),
86 place
.local
= ptr_local
;
88 self.patch
.add_statement(
89 Location { block: location.block, statement_index: location.statement_index + 1 }
,
90 StatementKind
::StorageDead(ptr_local
),
94 self.super_place(place
, context
, location
);
98 pub struct ElaborateBoxDerefs
;
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
;
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")
109 let nonnull_did
= nonnull_def
.non_enum_variant().fields
[0].did
;
111 let patch
= MirPatch
::new(body
);
113 let (basic_blocks
, local_decls
) = body
.basic_blocks_and_local_decls_mut();
116 ElaborateBoxDerefVisitor { tcx, unique_did, nonnull_did, local_decls, patch }
;
118 for (block
, BasicBlockData { statements, terminator, .. }
) in
119 basic_blocks
.iter_enumerated_mut()
122 for statement
in statements
{
123 let location
= Location { block, statement_index: index }
;
124 visitor
.visit_statement(statement
, location
);
128 if let Some(terminator
) = terminator
129 && !matches
!(terminator
.kind
, TerminatorKind
::Yield{..}
)
131 let location
= Location { block, statement_index: index }
;
132 visitor
.visit_terminator(terminator
, location
);
135 let location
= Location { block, statement_index: index }
;
137 // yielding into a box is handled when lowering generators
138 Some(Terminator { kind: TerminatorKind::Yield { value, .. }
, .. }) => {
139 visitor
.visit_operand(value
, location
);
141 Some(terminator
) => {
142 visitor
.visit_terminator(terminator
, location
);
148 visitor
.patch
.apply(body
);
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;
155 for (i
, (base
, elem
)) in place
.iter_projections().enumerate() {
156 let base_ty
= base
.ty(&body
.local_decls
, tcx
).ty
;
158 if elem
== PlaceElem
::Deref
&& base_ty
.is_box() {
159 let new_projections
= new_projections
.get_or_insert_default();
161 let (unique_ty
, nonnull_ty
, ptr_ty
) =
162 build_ptr_tys(tcx
, base_ty
.boxed_ty(), unique_did
, nonnull_did
);
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
,
168 new_projections
.push(PlaceElem
::Deref
);
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
);
181 // box is not present, this pass doesn't need to do anything