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 crate::transform::{MirPass, MirSource};
8 use rustc_middle::mir::*;
9 use rustc_middle::ty::{self, Ty, TyCtxt};
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| {
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
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,
36 /// Determine whether this type may be a reference (or box), and thus needs retagging.
37 fn may_be_reference(ty: Ty<'tcx>) -> bool {
39 // Primitive types that are not references
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
60 impl<'tcx> MirPass<'tcx> for AddRetag {
61 fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) {
62 if !tcx.sess.opts.debugging_opts.mir_emit_retag {
66 // We need an `AllCallEdges` pass before we can do any work.
67 super::add_call_guards::AllCallEdges.run_pass(tcx, src, body);
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)
78 // Retag arguments at the beginning of the start block.
80 let source_info = SourceInfo {
81 scope: OUTERMOST_SOURCE_SCOPE,
82 span, // FIXME: Consider using just the span covering the function
83 // argument declaration.
85 // Gather all arguments, skip return value.
86 let places = local_decls
90 .map(|(local, _)| Place::from(local))
94 basic_blocks[START_BLOCK].statements.splice(
96 places.into_iter().map(|place| Statement {
98 kind: StatementKind::Retag(RetagKind::FnEntry, box (place)),
104 // Retag return values of functions. Also escape-to-raw the argument of `drop`.
105 // We collect the return destinations because we cannot mutate while iterating.
106 let mut returns: Vec<(SourceInfo, Place<'tcx>, BasicBlock)> = Vec::new();
107 for block_data in basic_blocks.iter_mut() {
108 match block_data.terminator().kind {
109 TerminatorKind::Call { ref destination, .. } => {
110 // Remember the return destination for later
111 if let Some(ref destination) = destination {
112 if needs_retag(&destination.0) {
114 block_data.terminator().source_info,
121 TerminatorKind::Drop { .. } | TerminatorKind::DropAndReplace { .. } => {
122 // `Drop` is also a call, but it doesn't return anything so we are good.
125 // Not a block ending in a Call -> ignore.
129 // Now we go over the returns we collected to retag the return values.
130 for (source_info, dest_place, dest_block) in returns {
131 basic_blocks[dest_block].statements.insert(
135 kind: StatementKind::Retag(RetagKind::Default, box (dest_place)),
141 // Add retag after assignment.
142 for block_data in basic_blocks {
143 // We want to insert statements as we iterate. To this end, we
144 // iterate backwards using indices.
145 for i in (0..block_data.statements.len()).rev() {
146 let (retag_kind, place) = match block_data.statements[i].kind {
147 // Retag-as-raw after escaping to a raw pointer.
148 StatementKind::Assign(box (place, Rvalue::AddressOf(..))) => {
149 (RetagKind::Raw, place)
151 // Assignments of reference or ptr type are the ones where we may have
152 // to update tags. This includes `x = &[mut] ...` and hence
153 // we also retag after taking a reference!
154 StatementKind::Assign(box (ref place, ref rvalue)) if needs_retag(place) => {
155 let kind = match rvalue {
156 Rvalue::Ref(_, borrow_kind, _)
157 if borrow_kind.allows_two_phase_borrow() =>
161 _ => RetagKind::Default,
165 // Do nothing for the rest
168 // Insert a retag after the statement.
169 let source_info = block_data.statements[i].source_info;
170 block_data.statements.insert(
172 Statement { source_info, kind: StatementKind::Retag(retag_kind, box (place)) },