]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/add_retag.rs
Rollup merge of #63684 - GrayJack:const_list_new, r=Centril
[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 rustc::ty::{self, Ty, TyCtxt};
8 use rustc::mir::*;
9 use crate::transform::{MirPass, MirSource};
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(
18     place: PlaceRef<'_, '_>,
19 ) -> bool {
20     if let Some(proj) = &place.projection {
21         match proj.elem {
22             // Which place this evaluates to can change with any memory write,
23             // so cannot assume this to be stable.
24             ProjectionElem::Deref =>
25                 false,
26             // Array indices are intersting, but MIR building generates a *fresh*
27             // temporary for every array access, so the index cannot be changed as
28             // a side-effect.
29             ProjectionElem::Index { .. } |
30             // The rest is completely boring, they just offset by a constant.
31             ProjectionElem::Field { .. } |
32             ProjectionElem::ConstantIndex { .. } |
33             ProjectionElem::Subslice { .. } |
34             ProjectionElem::Downcast { .. } =>
35                 is_stable(PlaceRef {
36                     base: place.base,
37                     projection: &proj.base,
38                 }),
39         }
40     } else {
41         true
42     }
43 }
44
45 /// Determine whether this type may be a reference (or box), and thus needs retagging.
46 fn may_be_reference<'tcx>(ty: Ty<'tcx>) -> bool {
47     match ty.sty {
48         // Primitive types that are not references
49         ty::Bool | ty::Char |
50         ty::Float(_) | ty::Int(_) | ty::Uint(_) |
51         ty::RawPtr(..) | ty::FnPtr(..) |
52         ty::Str | ty::FnDef(..) | ty::Never =>
53             false,
54         // References
55         ty::Ref(..) => true,
56         ty::Adt(..) if ty.is_box() => true,
57         // Compound types are not references
58         ty::Array(..) |
59         ty::Slice(..) |
60         ty::Tuple(..) |
61         ty::Adt(..) =>
62             false,
63         // Conservative fallback
64         _ => true,
65     }
66 }
67
68 impl<'tcx> MirPass<'tcx> for AddRetag {
69     fn run_pass(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) {
70         if !tcx.sess.opts.debugging_opts.mir_emit_retag {
71             return;
72         }
73         let (span, arg_count) = (body.span, body.arg_count);
74         let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
75         let needs_retag = |place: &Place<'tcx>| {
76             // FIXME: Instead of giving up for unstable places, we should introduce
77             // a temporary and retag on that.
78             is_stable(place.as_ref())
79                 && may_be_reference(place.ty(&*local_decls, tcx).ty)
80         };
81
82         // PART 1
83         // Retag arguments at the beginning of the start block.
84         {
85             let source_info = SourceInfo {
86                 scope: OUTERMOST_SOURCE_SCOPE,
87                 span: span, // FIXME: Consider using just the span covering the function
88                             // argument declaration.
89             };
90             // Gather all arguments, skip return value.
91             let places = local_decls.iter_enumerated().skip(1).take(arg_count)
92                     .map(|(local, _)| Place::from(local))
93                     .filter(needs_retag)
94                     .collect::<Vec<_>>();
95             // Emit their retags.
96             basic_blocks[START_BLOCK].statements.splice(0..0,
97                 places.into_iter().map(|place| Statement {
98                     source_info,
99                     kind: StatementKind::Retag(RetagKind::FnEntry, place),
100                 })
101             );
102         }
103
104         // PART 2
105         // Retag return values of functions.  Also escape-to-raw the argument of `drop`.
106         // We collect the return destinations because we cannot mutate while iterating.
107         let mut returns: Vec<(SourceInfo, Place<'tcx>, BasicBlock)> = Vec::new();
108         for block_data in basic_blocks.iter_mut() {
109             match block_data.terminator().kind {
110                 TerminatorKind::Call { ref destination, .. } => {
111                     // Remember the return destination for later
112                     if let Some(ref destination) = destination {
113                         if needs_retag(&destination.0) {
114                             returns.push((
115                                 block_data.terminator().source_info,
116                                 destination.0.clone(),
117                                 destination.1,
118                             ));
119                         }
120                     }
121                 }
122                 TerminatorKind::Drop { .. } |
123                 TerminatorKind::DropAndReplace { .. } => {
124                     // `Drop` is also a call, but it doesn't return anything so we are good.
125                 }
126                 _ => {
127                     // Not a block ending in a Call -> ignore.
128                 }
129             }
130         }
131         // Now we go over the returns we collected to retag the return values.
132         for (source_info, dest_place, dest_block) in returns {
133             basic_blocks[dest_block].statements.insert(0, Statement {
134                 source_info,
135                 kind: StatementKind::Retag(RetagKind::Default, dest_place),
136             });
137         }
138
139         // PART 3
140         // Add retag after assignment.
141         for block_data in basic_blocks {
142             // We want to insert statements as we iterate.  To this end, we
143             // iterate backwards using indices.
144             for i in (0..block_data.statements.len()).rev() {
145                 let (retag_kind, place) = match block_data.statements[i].kind {
146                     // If we are casting *from* a reference, we may have to retag-as-raw.
147                     StatementKind::Assign(ref place, box Rvalue::Cast(
148                         CastKind::Misc,
149                         ref src,
150                         dest_ty,
151                     )) => {
152                         let src_ty = src.ty(&*local_decls, tcx);
153                         if src_ty.is_region_ptr() {
154                             // The only `Misc` casts on references are those creating raw pointers.
155                             assert!(dest_ty.is_unsafe_ptr());
156                             (RetagKind::Raw, place.clone())
157                         } else {
158                             // Some other cast, no retag
159                             continue
160                         }
161                     }
162                     // Assignments of reference or ptr type are the ones where we may have
163                     // to update tags.  This includes `x = &[mut] ...` and hence
164                     // we also retag after taking a reference!
165                     StatementKind::Assign(ref place, box ref rvalue) if needs_retag(place) => {
166                         let kind = match rvalue {
167                             Rvalue::Ref(_, borrow_kind, _)
168                                 if borrow_kind.allows_two_phase_borrow()
169                             =>
170                                 RetagKind::TwoPhase,
171                             _ =>
172                                 RetagKind::Default,
173                         };
174                         (kind, place.clone())
175                     }
176                     // Do nothing for the rest
177                     _ => continue,
178                 };
179                 // Insert a retag after the statement.
180                 let source_info = block_data.statements[i].source_info;
181                 block_data.statements.insert(i+1, Statement {
182                     source_info,
183                     kind: StatementKind::Retag(retag_kind, place),
184                 });
185             }
186         }
187     }
188 }