rustc_mir_transform/
deref_separator.rs1use 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 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 Rvalue::Use(Operand::Copy(deref_place), WithRetag::No)
67 },
68 );
69 place_local = temp;
70 last_len = p_ref.projection.len();
71
72 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 PassPolicy::Required
108 }
109}