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.)
20 use rustc
::mir
::Place
::*;
23 // Locals and statics have stable addresses, for sure
28 // Recurse for projections
29 Projection(ref proj
) => {
31 // Which place this evaluates to can change with any memory write,
32 // so cannot assume this to be stable.
33 ProjectionElem
::Deref
=>
35 // Array indices are intersting, but MIR building generates a *fresh*
36 // temporary for every array access, so the index cannot be changed as
38 ProjectionElem
::Index { .. }
|
39 // The rest is completely boring, they just offset by a constant.
40 ProjectionElem
::Field { .. }
|
41 ProjectionElem
::ConstantIndex { .. }
|
42 ProjectionElem
::Subslice { .. }
|
43 ProjectionElem
::Downcast { .. }
=>
44 is_stable(&proj
.base
),
50 /// Determine whether this type may have a reference in it, recursing below compound types but
51 /// not below references.
52 fn may_have_reference
<'a
, 'gcx
, 'tcx
>(ty
: Ty
<'tcx
>, tcx
: TyCtxt
<'a
, 'gcx
, 'tcx
>) -> bool
{
54 // Primitive types that are not references
56 ty
::Float(_
) | ty
::Int(_
) | ty
::Uint(_
) |
57 ty
::RawPtr(..) | ty
::FnPtr(..) |
58 ty
::Str
| ty
::FnDef(..) | ty
::Never
=>
62 ty
::Adt(..) if ty
.is_box() => true,
64 ty
::Array(ty
, ..) | ty
::Slice(ty
) =>
65 may_have_reference(ty
, tcx
),
67 tys
.iter().any(|ty
| may_have_reference(ty
, tcx
)),
68 ty
::Adt(adt
, substs
) =>
69 adt
.variants
.iter().any(|v
| v
.fields
.iter().any(|f
|
70 may_have_reference(f
.ty(tcx
, substs
), tcx
)
72 // Conservative fallback
77 impl MirPass
for AddRetag
{
78 fn run_pass
<'a
, 'tcx
>(&self,
79 tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
80 _src
: MirSource
<'tcx
>,
83 if !tcx
.sess
.opts
.debugging_opts
.mir_emit_retag
{
86 let (span
, arg_count
) = (mir
.span
, mir
.arg_count
);
87 let (basic_blocks
, local_decls
) = mir
.basic_blocks_and_local_decls_mut();
88 let needs_retag
= |place
: &Place
<'tcx
>| {
89 // FIXME: Instead of giving up for unstable places, we should introduce
90 // a temporary and retag on that.
91 is_stable(place
) && may_have_reference(place
.ty(&*local_decls
, tcx
).to_ty(tcx
), tcx
)
95 // Retag arguments at the beginning of the start block.
97 let source_info
= SourceInfo
{
98 scope
: OUTERMOST_SOURCE_SCOPE
,
99 span
: span
, // FIXME: Consider using just the span covering the function
100 // argument declaration.
102 // Gather all arguments, skip return value.
103 let places
= local_decls
.iter_enumerated().skip(1).take(arg_count
)
104 .map(|(local
, _
)| Place
::Local(local
))
106 .collect
::<Vec
<_
>>();
107 // Emit their retags.
108 basic_blocks
[START_BLOCK
].statements
.splice(0..0,
109 places
.into_iter().map(|place
| Statement
{
111 kind
: StatementKind
::Retag(RetagKind
::FnEntry
, place
),
117 // Retag return values of functions. Also escape-to-raw the argument of `drop`.
118 // We collect the return destinations because we cannot mutate while iterating.
119 let mut returns
: Vec
<(SourceInfo
, Place
<'tcx
>, BasicBlock
)> = Vec
::new();
120 for block_data
in basic_blocks
.iter_mut() {
121 match block_data
.terminator().kind
{
122 TerminatorKind
::Call { ref destination, .. }
=> {
123 // Remember the return destination for later
124 if let Some(ref destination
) = destination
{
125 if needs_retag(&destination
.0) {
127 block_data
.terminator().source_info
,
128 destination
.0.clone(),
134 TerminatorKind
::Drop { .. }
|
135 TerminatorKind
::DropAndReplace { .. }
=> {
136 // `Drop` is also a call, but it doesn't return anything so we are good.
139 // Not a block ending in a Call -> ignore.
143 // Now we go over the returns we collected to retag the return values.
144 for (source_info
, dest_place
, dest_block
) in returns
{
145 basic_blocks
[dest_block
].statements
.insert(0, Statement
{
147 kind
: StatementKind
::Retag(RetagKind
::Default
, dest_place
),
152 // Add retag after assignment.
153 for block_data
in basic_blocks
{
154 // We want to insert statements as we iterate. To this end, we
155 // iterate backwards using indices.
156 for i
in (0..block_data
.statements
.len()).rev() {
157 let (retag_kind
, place
) = match block_data
.statements
[i
].kind
{
158 // If we are casting *from* a reference, we may have to retag-as-raw.
159 StatementKind
::Assign(ref place
, box Rvalue
::Cast(
164 let src_ty
= src
.ty(&*local_decls
, tcx
);
165 if src_ty
.is_region_ptr() {
166 // The only `Misc` casts on references are those creating raw pointers.
167 assert
!(dest_ty
.is_unsafe_ptr());
168 (RetagKind
::Raw
, place
)
170 // Some other cast, no retag
174 // Assignments of reference or ptr type are the ones where we may have
175 // to update tags. This includes `x = &[mut] ...` and hence
176 // we also retag after taking a reference!
177 StatementKind
::Assign(ref place
, box ref rvalue
) if needs_retag(place
) => {
178 let kind
= match rvalue
{
179 Rvalue
::Ref(_
, borrow_kind
, _
)
180 if borrow_kind
.allows_two_phase_borrow()
188 // Do nothing for the rest
191 // Insert a retag after the statement.
192 let source_info
= block_data
.statements
[i
].source_info
;
193 block_data
.statements
.insert(i
+1, Statement
{
195 kind
: StatementKind
::Retag(retag_kind
, place
.clone()),