]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/add_retag.rs
pin docs: add some forward references
[rust.git] / src / librustc_mir / transform / add_retag.rs
1 //! This pass adds validation calls (AcquireValid, ReleaseValid) where appropriate.
2 //! It has to be run really early, before transformations like inlining, because
3 //! introducing these calls *adds* UB -- so, conceptually, this pass is actually part
4 //! of MIR building, and only after this pass we think of the program has having the
5 //! normal MIR semantics.
6
7 use crate::transform::{MirPass, MirSource};
8 use rustc_middle::mir::*;
9 use rustc_middle::ty::{self, Ty, TyCtxt};
10
11 pub struct AddRetag;
12
13 /// Determines whether this place is "stable": Whether, if we evaluate it again
14 /// after the assignment, we can be sure to obtain the same place value.
15 /// (Concurrent accesses by other threads are no problem as these are anyway non-atomic
16 /// copies.  Data races are UB.)
17 fn is_stable(place: PlaceRef<'_>) -> bool {
18     place.projection.iter().all(|elem| {
19         match elem {
20             // Which place this evaluates to can change with any memory write,
21             // so cannot assume this to be stable.
22             ProjectionElem::Deref => false,
23             // Array indices are interesting, but MIR building generates a *fresh*
24             // temporary for every array access, so the index cannot be changed as
25             // a side-effect.
26             ProjectionElem::Index { .. } |
27             // The rest is completely boring, they just offset by a constant.
28             ProjectionElem::Field { .. } |
29             ProjectionElem::ConstantIndex { .. } |
30             ProjectionElem::Subslice { .. } |
31             ProjectionElem::Downcast { .. } => true,
32         }
33     })
34 }
35
36 /// Determine whether this type may be a reference (or box), and thus needs retagging.
37 fn may_be_reference(ty: Ty<'tcx>) -> bool {
38     match ty.kind {
39         // Primitive types that are not references
40         ty::Bool
41         | ty::Char
42         | ty::Float(_)
43         | ty::Int(_)
44         | ty::Uint(_)
45         | ty::RawPtr(..)
46         | ty::FnPtr(..)
47         | ty::Str
48         | ty::FnDef(..)
49         | ty::Never => false,
50         // References
51         ty::Ref(..) => true,
52         ty::Adt(..) if ty.is_box() => true,
53         // Compound types are not references
54         ty::Array(..) | ty::Slice(..) | ty::Tuple(..) | ty::Adt(..) => false,
55         // Conservative fallback
56         _ => true,
57     }
58 }
59
60 impl<'tcx> MirPass<'tcx> for AddRetag {
61     fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
62         if !tcx.sess.opts.debugging_opts.mir_emit_retag {
63             return;
64         }
65
66         // We need an `AllCallEdges` pass before we can do any work.
67         super::add_call_guards::AllCallEdges.run_pass(tcx, src, body);
68
69         let (span, arg_count) = (body.span, body.arg_count);
70         let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
71         let needs_retag = |place: &Place<'tcx>| {
72             // FIXME: Instead of giving up for unstable places, we should introduce
73             // a temporary and retag on that.
74             is_stable(place.as_ref()) && may_be_reference(place.ty(&*local_decls, tcx).ty)
75         };
76
77         // PART 1
78         // Retag arguments at the beginning of the start block.
79         {
80             // FIXME: Consider using just the span covering the function
81             // argument declaration.
82             let source_info = SourceInfo::outermost(span);
83             // Gather all arguments, skip return value.
84             let places = local_decls
85                 .iter_enumerated()
86                 .skip(1)
87                 .take(arg_count)
88                 .map(|(local, _)| Place::from(local))
89                 .filter(needs_retag)
90                 .collect::<Vec<_>>();
91             // Emit their retags.
92             basic_blocks[START_BLOCK].statements.splice(
93                 0..0,
94                 places.into_iter().map(|place| Statement {
95                     source_info,
96                     kind: StatementKind::Retag(RetagKind::FnEntry, box (place)),
97                 }),
98             );
99         }
100
101         // PART 2
102         // Retag return values of functions.  Also escape-to-raw the argument of `drop`.
103         // We collect the return destinations because we cannot mutate while iterating.
104         let mut returns: Vec<(SourceInfo, Place<'tcx>, BasicBlock)> = Vec::new();
105         for block_data in basic_blocks.iter_mut() {
106             match block_data.terminator().kind {
107                 TerminatorKind::Call { ref destination, .. } => {
108                     // Remember the return destination for later
109                     if let Some(ref destination) = destination {
110                         if needs_retag(&destination.0) {
111                             returns.push((
112                                 block_data.terminator().source_info,
113                                 destination.0,
114                                 destination.1,
115                             ));
116                         }
117                     }
118                 }
119                 TerminatorKind::Drop { .. } | TerminatorKind::DropAndReplace { .. } => {
120                     // `Drop` is also a call, but it doesn't return anything so we are good.
121                 }
122                 _ => {
123                     // Not a block ending in a Call -> ignore.
124                 }
125             }
126         }
127         // Now we go over the returns we collected to retag the return values.
128         for (source_info, dest_place, dest_block) in returns {
129             basic_blocks[dest_block].statements.insert(
130                 0,
131                 Statement {
132                     source_info,
133                     kind: StatementKind::Retag(RetagKind::Default, box (dest_place)),
134                 },
135             );
136         }
137
138         // PART 3
139         // Add retag after assignment.
140         for block_data in basic_blocks {
141             // We want to insert statements as we iterate.  To this end, we
142             // iterate backwards using indices.
143             for i in (0..block_data.statements.len()).rev() {
144                 let (retag_kind, place) = match block_data.statements[i].kind {
145                     // Retag-as-raw after escaping to a raw pointer.
146                     StatementKind::Assign(box (place, Rvalue::AddressOf(..))) => {
147                         (RetagKind::Raw, place)
148                     }
149                     // Assignments of reference or ptr type are the ones where we may have
150                     // to update tags.  This includes `x = &[mut] ...` and hence
151                     // we also retag after taking a reference!
152                     StatementKind::Assign(box (ref place, ref rvalue)) if needs_retag(place) => {
153                         let kind = match rvalue {
154                             Rvalue::Ref(_, borrow_kind, _)
155                                 if borrow_kind.allows_two_phase_borrow() =>
156                             {
157                                 RetagKind::TwoPhase
158                             }
159                             _ => RetagKind::Default,
160                         };
161                         (kind, *place)
162                     }
163                     // Do nothing for the rest
164                     _ => continue,
165                 };
166                 // Insert a retag after the statement.
167                 let source_info = block_data.statements[i].source_info;
168                 block_data.statements.insert(
169                     i + 1,
170                     Statement { source_info, kind: StatementKind::Retag(retag_kind, box (place)) },
171                 );
172             }
173         }
174     }
175 }