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);
74 self.patch.add_statement(location, StatementKind::StorageLive(ptr_local));
76 self.patch.add_assign(
78 Place::from(ptr_local),
79 Rvalue::Use(Operand::Copy(
80 Place::from(place.local)
81 .project_deeper(&build_projection(unique_ty, nonnull_ty, ptr_ty), tcx),
85 place.local = ptr_local;
87 self.patch.add_statement(
88 Location { block: location.block, statement_index: location.statement_index + 1 },
89 StatementKind::StorageDead(ptr_local),
93 self.super_place(place, context, location);
97 pub struct ElaborateBoxDerefs;
99 impl<'tcx> MirPass<'tcx> for ElaborateBoxDerefs {
100 fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
101 if let Some(def_id) = tcx.lang_items().owned_box() {
102 let unique_did = tcx.adt_def(def_id).non_enum_variant().fields[0].did;
104 let Some(nonnull_def) = tcx.type_of(unique_did).ty_adt_def() else {
105 span_bug!(tcx.def_span(unique_did), "expected Box to contain Unique")
108 let nonnull_did = nonnull_def.non_enum_variant().fields[0].did;
110 let patch = MirPatch::new(body);
112 let local_decls = &mut body.local_decls;
115 ElaborateBoxDerefVisitor { tcx, unique_did, nonnull_did, local_decls, patch };
117 for (block, BasicBlockData { statements, terminator, .. }) in
118 body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut()
121 for statement in statements {
122 let location = Location { block, statement_index: index };
123 visitor.visit_statement(statement, location);
127 let location = Location { block, statement_index: index };
129 // yielding into a box is handled when lowering generators
130 Some(Terminator { kind: TerminatorKind::Yield { value, .. }, .. }) => {
131 visitor.visit_operand(value, location);
133 Some(terminator) => {
134 visitor.visit_terminator(terminator, location);
140 visitor.patch.apply(body);
142 for debug_info in body.var_debug_info.iter_mut() {
143 if let VarDebugInfoContents::Place(place) = &mut debug_info.value {
144 let mut new_projections: Option<Vec<_>> = None;
145 let mut last_deref = 0;
147 for (i, (base, elem)) in place.iter_projections().enumerate() {
148 let base_ty = base.ty(&body.local_decls, tcx).ty;
150 if elem == PlaceElem::Deref && base_ty.is_box() {
151 let new_projections = new_projections.get_or_insert_default();
153 let (unique_ty, nonnull_ty, ptr_ty) =
154 build_ptr_tys(tcx, base_ty.boxed_ty(), unique_did, nonnull_did);
156 new_projections.extend_from_slice(&base.projection[last_deref..]);
157 new_projections.extend_from_slice(&build_projection(
158 unique_ty, nonnull_ty, ptr_ty,
160 new_projections.push(PlaceElem::Deref);
166 if let Some(mut new_projections) = new_projections {
167 new_projections.extend_from_slice(&place.projection[last_deref..]);
168 place.projection = tcx.intern_place_elems(&new_projections);
173 // box is not present, this pass doesn't need to do anything