Skip to main content

rustc_mir_transform/
deref_separator.rs

1use rustc_middle::mir::visit::NonUseContext::VarDebugInfo;
2use rustc_middle::mir::visit::{MutVisitor, PlaceContext};
3use rustc_middle::mir::*;
4use rustc_middle::ty::TyCtxt;
5
6use crate::PassPolicy;
7use crate::patch::MirPatch;
8
9pub(super) struct Derefer;
10
11struct DerefChecker<'a, 'tcx> {
12    tcx: TyCtxt<'tcx>,
13    patcher: MirPatch<'tcx>,
14    local_decls: &'a LocalDecls<'tcx>,
15    add_deref_metadata: bool,
16}
17
18impl<'a, 'tcx> MutVisitor<'tcx> for DerefChecker<'a, 'tcx> {
19    fn tcx(&self) -> TyCtxt<'tcx> {
20        self.tcx
21    }
22
23    fn visit_place(&mut self, place: &mut Place<'tcx>, cntxt: PlaceContext, loc: Location) {
24        if !place.projection.is_empty()
25            && cntxt != PlaceContext::NonUse(VarDebugInfo)
26            && place.projection[1..].contains(&ProjectionElem::Deref)
27        {
28            let mut place_local = place.local;
29            let mut last_len = 0;
30            let mut last_deref_idx = 0;
31
32            for (idx, elem) in place.projection[0..].iter().enumerate() {
33                if *elem == ProjectionElem::Deref {
34                    last_deref_idx = idx;
35                }
36            }
37
38            for (idx, (p_ref, p_elem)) in place.iter_projections().enumerate() {
39                if !p_ref.projection.is_empty() && p_elem == ProjectionElem::Deref {
40                    let ty = p_ref.ty(self.local_decls, self.tcx).ty;
41                    let temp = self.patcher.new_local_with_info(
42                        ty,
43                        self.local_decls[p_ref.local].source_info.span,
44                        if self.add_deref_metadata {
45                            LocalInfo::DerefTemp
46                        } else {
47                            LocalInfo::Boring
48                        },
49                    );
50
51                    // We are adding current p_ref's projections to our
52                    // temp value, excluding projections we already covered.
53                    let deref_place = Place::from(place_local)
54                        .project_deeper(&p_ref.projection[last_len..], self.tcx);
55
56                    self.patcher.add_assign(
57                        loc,
58                        Place::from(temp),
59                        if self.add_deref_metadata {
60                            Rvalue::CopyForDeref(deref_place)
61                        } else {
62                            // FIXME: Unfortunately, `add_deref_metadata` is not documented. So who
63                            // knows what is supposed to happen here -- retag or not? `CopyForDeref`
64                            // later turns into a no-retag assignment so probably maybe that's also
65                            // what we need here.
66                            Rvalue::Use(Operand::Copy(deref_place), WithRetag::No)
67                        },
68                    );
69                    place_local = temp;
70                    last_len = p_ref.projection.len();
71
72                    // Change `Place` only if we are actually at the Place's last deref
73                    if idx == last_deref_idx {
74                        let temp_place =
75                            Place::from(temp).project_deeper(&place.projection[idx..], self.tcx);
76                        *place = temp_place;
77                    }
78                }
79            }
80        }
81    }
82}
83
84pub(super) fn deref_finder<'tcx>(
85    tcx: TyCtxt<'tcx>,
86    body: &mut Body<'tcx>,
87    add_deref_metadata: bool,
88) {
89    let patch = MirPatch::new(body);
90    let mut checker =
91        DerefChecker { tcx, patcher: patch, local_decls: &body.local_decls, add_deref_metadata };
92
93    for (bb, data) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() {
94        checker.visit_basic_block_data(bb, data);
95    }
96
97    checker.patcher.apply(body);
98}
99
100impl<'tcx> crate::MirPass<'tcx> for Derefer {
101    fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
102        deref_finder(tcx, body, true);
103    }
104
105    fn policy(&self, _sess: &rustc_session::Session) -> PassPolicy {
106        // Later MIR stages expect derefs to only appear as the first place projection.
107        PassPolicy::Required
108    }
109}