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.
7 use rustc::ty::{self, Ty, TyCtxt};
9 use crate::transform::{MirPass, MirSource};
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.)
18 place: PlaceRef<'_, '_>,
20 if let Some(proj) = &place.projection {
22 // Which place this evaluates to can change with any memory write,
23 // so cannot assume this to be stable.
24 ProjectionElem::Deref =>
26 // Array indices are intersting, but MIR building generates a *fresh*
27 // temporary for every array access, so the index cannot be changed as
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 { .. } =>
37 projection: &proj.base,
45 /// Determine whether this type may have a reference in it, recursing below compound types but
46 /// not below references.
47 fn may_have_reference<'tcx>(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> bool {
49 // Primitive types that are not references
51 ty::Float(_) | ty::Int(_) | ty::Uint(_) |
52 ty::RawPtr(..) | ty::FnPtr(..) |
53 ty::Str | ty::FnDef(..) | ty::Never =>
57 ty::Adt(..) if ty.is_box() => true,
59 ty::Array(ty, ..) | ty::Slice(ty) =>
60 may_have_reference(ty, tcx),
62 tys.iter().any(|ty| may_have_reference(ty.expect_ty(), tcx)),
63 ty::Adt(adt, substs) =>
64 adt.variants.iter().any(|v| v.fields.iter().any(|f|
65 may_have_reference(f.ty(tcx, substs), tcx)
67 // Conservative fallback
72 impl MirPass for AddRetag {
73 fn run_pass<'tcx>(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) {
74 if !tcx.sess.opts.debugging_opts.mir_emit_retag {
77 let (span, arg_count) = (body.span, body.arg_count);
78 let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
79 let needs_retag = |place: &Place<'tcx>| {
80 // FIXME: Instead of giving up for unstable places, we should introduce
81 // a temporary and retag on that.
82 is_stable(place.as_ref())
83 && may_have_reference(place.ty(&*local_decls, tcx).ty, tcx)
87 // Retag arguments at the beginning of the start block.
89 let source_info = SourceInfo {
90 scope: OUTERMOST_SOURCE_SCOPE,
91 span: span, // FIXME: Consider using just the span covering the function
92 // argument declaration.
94 // Gather all arguments, skip return value.
95 let places = local_decls.iter_enumerated().skip(1).take(arg_count)
96 .map(|(local, _)| Place::from(local))
100 basic_blocks[START_BLOCK].statements.splice(0..0,
101 places.into_iter().map(|place| Statement {
103 kind: StatementKind::Retag(RetagKind::FnEntry, place),
109 // Retag return values of functions. Also escape-to-raw the argument of `drop`.
110 // We collect the return destinations because we cannot mutate while iterating.
111 let mut returns: Vec<(SourceInfo, Place<'tcx>, BasicBlock)> = Vec::new();
112 for block_data in basic_blocks.iter_mut() {
113 match block_data.terminator().kind {
114 TerminatorKind::Call { ref destination, .. } => {
115 // Remember the return destination for later
116 if let Some(ref destination) = destination {
117 if needs_retag(&destination.0) {
119 block_data.terminator().source_info,
120 destination.0.clone(),
126 TerminatorKind::Drop { .. } |
127 TerminatorKind::DropAndReplace { .. } => {
128 // `Drop` is also a call, but it doesn't return anything so we are good.
131 // Not a block ending in a Call -> ignore.
135 // Now we go over the returns we collected to retag the return values.
136 for (source_info, dest_place, dest_block) in returns {
137 basic_blocks[dest_block].statements.insert(0, Statement {
139 kind: StatementKind::Retag(RetagKind::Default, dest_place),
144 // Add retag after assignment.
145 for block_data in basic_blocks {
146 // We want to insert statements as we iterate. To this end, we
147 // iterate backwards using indices.
148 for i in (0..block_data.statements.len()).rev() {
149 let (retag_kind, place) = match block_data.statements[i].kind {
150 // If we are casting *from* a reference, we may have to retag-as-raw.
151 StatementKind::Assign(ref place, box Rvalue::Cast(
156 let src_ty = src.ty(&*local_decls, tcx);
157 if src_ty.is_region_ptr() {
158 // The only `Misc` casts on references are those creating raw pointers.
159 assert!(dest_ty.is_unsafe_ptr());
160 (RetagKind::Raw, place.clone())
162 // Some other cast, no retag
166 // Assignments of reference or ptr type are the ones where we may have
167 // to update tags. This includes `x = &[mut] ...` and hence
168 // we also retag after taking a reference!
169 StatementKind::Assign(ref place, box ref rvalue) if needs_retag(place) => {
170 let kind = match rvalue {
171 Rvalue::Ref(_, borrow_kind, _)
172 if borrow_kind.allows_two_phase_borrow()
178 (kind, place.clone())
180 // Do nothing for the rest
183 // Insert a retag after the statement.
184 let source_info = block_data.statements[i].source_info;
185 block_data.statements.insert(i+1, Statement {
187 kind: StatementKind::Retag(retag_kind, place),