1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//! This pass transforms derefs of Box into a deref of the pointer inside Box.
//!
//! Box is not actually a pointer so it is incorrect to dereference it directly.

use rustc_hir::def_id::DefId;
use rustc_middle::mir::patch::MirPatch;
use rustc_middle::mir::visit::MutVisitor;
use rustc_middle::mir::*;
use rustc_middle::ty::{Ty, TyCtxt};
use rustc_target::abi::FieldIdx;

/// Constructs the types used when accessing a Box's pointer
pub fn build_ptr_tys<'tcx>(
    tcx: TyCtxt<'tcx>,
    pointee: Ty<'tcx>,
    unique_did: DefId,
    nonnull_did: DefId,
) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
    let args = tcx.mk_args(&[pointee.into()]);
    let unique_ty = tcx.type_of(unique_did).instantiate(tcx, args);
    let nonnull_ty = tcx.type_of(nonnull_did).instantiate(tcx, args);
    let ptr_ty = Ty::new_imm_ptr(tcx, pointee);

    (unique_ty, nonnull_ty, ptr_ty)
}

/// Constructs the projection needed to access a Box's pointer
pub fn build_projection<'tcx>(
    unique_ty: Ty<'tcx>,
    nonnull_ty: Ty<'tcx>,
    ptr_ty: Ty<'tcx>,
) -> [PlaceElem<'tcx>; 3] {
    [
        PlaceElem::Field(FieldIdx::ZERO, unique_ty),
        PlaceElem::Field(FieldIdx::ZERO, nonnull_ty),
        PlaceElem::Field(FieldIdx::ZERO, ptr_ty),
    ]
}

struct ElaborateBoxDerefVisitor<'tcx, 'a> {
    tcx: TyCtxt<'tcx>,
    unique_did: DefId,
    nonnull_did: DefId,
    local_decls: &'a mut LocalDecls<'tcx>,
    patch: MirPatch<'tcx>,
}

impl<'tcx, 'a> MutVisitor<'tcx> for ElaborateBoxDerefVisitor<'tcx, 'a> {
    fn tcx(&self) -> TyCtxt<'tcx> {
        self.tcx
    }

    fn visit_place(
        &mut self,
        place: &mut Place<'tcx>,
        context: visit::PlaceContext,
        location: Location,
    ) {
        let tcx = self.tcx;

        let base_ty = self.local_decls[place.local].ty;

        // Derefer ensures that derefs are always the first projection
        if place.projection.first() == Some(&PlaceElem::Deref) && base_ty.is_box() {
            let source_info = self.local_decls[place.local].source_info;

            let (unique_ty, nonnull_ty, ptr_ty) =
                build_ptr_tys(tcx, base_ty.boxed_ty(), self.unique_did, self.nonnull_did);

            let ptr_local = self.patch.new_temp(ptr_ty, source_info.span);

            self.patch.add_assign(
                location,
                Place::from(ptr_local),
                Rvalue::Use(Operand::Copy(
                    Place::from(place.local)
                        .project_deeper(&build_projection(unique_ty, nonnull_ty, ptr_ty), tcx),
                )),
            );

            place.local = ptr_local;
        }

        self.super_place(place, context, location);
    }
}

pub struct ElaborateBoxDerefs;

impl<'tcx> MirPass<'tcx> for ElaborateBoxDerefs {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
        if let Some(def_id) = tcx.lang_items().owned_box() {
            let unique_did = tcx.adt_def(def_id).non_enum_variant().fields[FieldIdx::ZERO].did;

            let Some(nonnull_def) = tcx.type_of(unique_did).instantiate_identity().ty_adt_def()
            else {
                span_bug!(tcx.def_span(unique_did), "expected Box to contain Unique")
            };

            let nonnull_did = nonnull_def.non_enum_variant().fields[FieldIdx::ZERO].did;

            let patch = MirPatch::new(body);

            let local_decls = &mut body.local_decls;

            let mut visitor =
                ElaborateBoxDerefVisitor { tcx, unique_did, nonnull_did, local_decls, patch };

            for (block, data) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() {
                visitor.visit_basic_block_data(block, data);
            }

            visitor.patch.apply(body);

            for debug_info in body.var_debug_info.iter_mut() {
                if let VarDebugInfoContents::Place(place) = &mut debug_info.value {
                    let mut new_projections: Option<Vec<_>> = None;
                    let mut last_deref = 0;

                    for (i, (base, elem)) in place.iter_projections().enumerate() {
                        let base_ty = base.ty(&body.local_decls, tcx).ty;

                        if elem == PlaceElem::Deref && base_ty.is_box() {
                            let new_projections = new_projections.get_or_insert_default();

                            let (unique_ty, nonnull_ty, ptr_ty) =
                                build_ptr_tys(tcx, base_ty.boxed_ty(), unique_did, nonnull_did);

                            new_projections.extend_from_slice(&base.projection[last_deref..]);
                            new_projections.extend_from_slice(&build_projection(
                                unique_ty, nonnull_ty, ptr_ty,
                            ));
                            new_projections.push(PlaceElem::Deref);

                            last_deref = i;
                        }
                    }

                    if let Some(mut new_projections) = new_projections {
                        new_projections.extend_from_slice(&place.projection[last_deref..]);
                        place.projection = tcx.mk_place_elems(&new_projections);
                    }
                }
            }
        } else {
            // box is not present, this pass doesn't need to do anything
        }
    }
}