14
14
//! of MIR building, and only after this pass we think of the program has having the
15
15
//! normal MIR semantics.
16
16
17
+ use syntax_pos:: Span ;
18
+ use syntax:: ast:: NodeId ;
17
19
use rustc:: ty:: { self , TyCtxt , RegionKind } ;
18
20
use rustc:: hir;
19
21
use rustc:: mir:: * ;
@@ -80,15 +82,78 @@ fn lval_context<'a, 'tcx, D>(
80
82
}
81
83
}
82
84
85
+ /// Check if this function contains an unsafe block or is an unsafe function.
86
+ fn fn_contains_unsafe < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > , src : MirSource ) -> bool {
87
+ use rustc:: hir:: intravisit:: { self , Visitor } ;
88
+
89
+ let fn_node_id = match src {
90
+ MirSource :: Fn ( node_id) => node_id,
91
+ _ => return false , // only functions can have unsafe
92
+ } ;
93
+ let fn_item = tcx. hir . expect_item ( fn_node_id) ;
94
+
95
+ struct FindUnsafe < ' b , ' tcx > where ' tcx : ' b {
96
+ map : & ' b hir:: map:: Map < ' tcx > ,
97
+ found_unsafe : bool ,
98
+ }
99
+ let mut finder = FindUnsafe { map : & tcx. hir , found_unsafe : false } ;
100
+ finder. visit_item ( fn_item) ;
101
+
102
+ impl < ' b , ' tcx > Visitor < ' tcx > for FindUnsafe < ' b , ' tcx > {
103
+ fn nested_visit_map < ' this > ( & ' this mut self ) -> intravisit:: NestedVisitorMap < ' this , ' tcx > {
104
+ intravisit:: NestedVisitorMap :: OnlyBodies ( self . map )
105
+ }
106
+
107
+ fn visit_fn ( & mut self , fk : intravisit:: FnKind < ' tcx > , fd : & ' tcx hir:: FnDecl ,
108
+ b : hir:: BodyId , s : Span , id : NodeId )
109
+ {
110
+ assert ! ( !self . found_unsafe, "We should never see more than one fn" ) ;
111
+ let is_unsafe = match fk {
112
+ intravisit:: FnKind :: ItemFn ( _, _, unsafety, ..) => unsafety == hir:: Unsafety :: Unsafe ,
113
+ intravisit:: FnKind :: Method ( _, sig, ..) => sig. unsafety == hir:: Unsafety :: Unsafe ,
114
+ intravisit:: FnKind :: Closure ( _) => false ,
115
+ } ;
116
+ if is_unsafe {
117
+ // This is unsafe, and we are done.
118
+ self . found_unsafe = true ;
119
+ } else {
120
+ // Go on searching.
121
+ intravisit:: walk_fn ( self , fk, fd, b, s, id)
122
+ }
123
+ }
124
+
125
+ fn visit_block ( & mut self , b : & ' tcx hir:: Block ) {
126
+ use rustc:: hir:: BlockCheckMode :: * ;
127
+
128
+ if self . found_unsafe { return ; } // short-circuit
129
+
130
+ match b. rules {
131
+ UnsafeBlock ( _) | PushUnsafeBlock ( _) => {
132
+ // We found an unsafe block.
133
+ self . found_unsafe = true ;
134
+ }
135
+ DefaultBlock | PopUnsafeBlock ( _) => {
136
+ // No unsafe block here, go on searching.
137
+ intravisit:: walk_block ( self , b) ;
138
+ }
139
+ } ;
140
+ }
141
+ }
142
+
143
+ finder. found_unsafe
144
+ }
145
+
83
146
impl MirPass for AddValidation {
84
147
fn run_pass < ' a , ' tcx > ( & self ,
85
148
tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
86
- _: MirSource ,
87
- mir : & mut Mir < ' tcx > ) {
88
- if !tcx. sess . opts . debugging_opts . mir_emit_validate {
149
+ src : MirSource ,
150
+ mir : & mut Mir < ' tcx > )
151
+ {
152
+ let emit_validate = tcx. sess . opts . debugging_opts . mir_emit_validate ;
153
+ if emit_validate == 0 {
89
154
return ;
90
155
}
91
-
156
+ let restricted_validation = emit_validate == 1 && fn_contains_unsafe ( tcx , src ) ;
92
157
let local_decls = mir. local_decls . clone ( ) ; // FIXME: Find a way to get rid of this clone.
93
158
94
159
// Convert an lvalue to a validation operand.
@@ -98,22 +163,40 @@ impl MirPass for AddValidation {
98
163
ValidationOperand { lval, ty, re, mutbl }
99
164
} ;
100
165
166
+ // Emit an Acquire at the beginning of the given block. If we are in restricted emission mode
167
+ // (mir_emit_validate=1), also emit a Release immediately after the Acquire.
168
+ let emit_acquire = |block : & mut BasicBlockData < ' tcx > , source_info, operands : Vec < _ > | {
169
+ if operands. len ( ) == 0 {
170
+ return ; // Nothing to do
171
+ }
172
+ // Emit the release first, to avoid cloning if we do not emit it
173
+ if restricted_validation {
174
+ let release_stmt = Statement {
175
+ source_info,
176
+ kind : StatementKind :: Validate ( ValidationOp :: Release , operands. clone ( ) ) ,
177
+ } ;
178
+ block. statements . insert ( 0 , release_stmt) ;
179
+ }
180
+ // Now, the acquire
181
+ let acquire_stmt = Statement {
182
+ source_info,
183
+ kind : StatementKind :: Validate ( ValidationOp :: Acquire , operands) ,
184
+ } ;
185
+ block. statements . insert ( 0 , acquire_stmt) ;
186
+ } ;
187
+
101
188
// PART 1
102
189
// Add an AcquireValid at the beginning of the start block.
103
- if mir. arg_count > 0 {
104
- let acquire_stmt = Statement {
105
- source_info : SourceInfo {
106
- scope : ARGUMENT_VISIBILITY_SCOPE ,
107
- span : mir. span , // FIXME: Consider using just the span covering the function
108
- // argument declaration.
109
- } ,
110
- kind : StatementKind :: Validate ( ValidationOp :: Acquire ,
111
- // Skip return value, go over all the arguments
112
- mir. local_decls . iter_enumerated ( ) . skip ( 1 ) . take ( mir. arg_count )
113
- . map ( |( local, _) | lval_to_operand ( Lvalue :: Local ( local) ) ) . collect ( )
114
- )
190
+ {
191
+ let source_info = SourceInfo {
192
+ scope : ARGUMENT_VISIBILITY_SCOPE ,
193
+ span : mir. span , // FIXME: Consider using just the span covering the function
194
+ // argument declaration.
115
195
} ;
116
- mir. basic_blocks_mut ( ) [ START_BLOCK ] . statements . insert ( 0 , acquire_stmt) ;
196
+ // Gather all arguments, skip return value.
197
+ let operands = mir. local_decls . iter_enumerated ( ) . skip ( 1 ) . take ( mir. arg_count )
198
+ . map ( |( local, _) | lval_to_operand ( Lvalue :: Local ( local) ) ) . collect ( ) ;
199
+ emit_acquire ( & mut mir. basic_blocks_mut ( ) [ START_BLOCK ] , source_info, operands) ;
117
200
}
118
201
119
202
// PART 2
@@ -125,18 +208,20 @@ impl MirPass for AddValidation {
125
208
Some ( Terminator { kind : TerminatorKind :: Call { ref args, ref destination, .. } ,
126
209
source_info } ) => {
127
210
// Before the call: Release all arguments
128
- let release_stmt = Statement {
129
- source_info,
130
- kind : StatementKind :: Validate ( ValidationOp :: Release ,
131
- args. iter ( ) . filter_map ( |op| {
132
- match op {
133
- & Operand :: Consume ( ref lval) =>
134
- Some ( lval_to_operand ( lval. clone ( ) ) ) ,
135
- & Operand :: Constant ( ..) => { None } ,
136
- }
137
- } ) . collect ( ) )
138
- } ;
139
- block_data. statements . push ( release_stmt) ;
211
+ if !restricted_validation {
212
+ let release_stmt = Statement {
213
+ source_info,
214
+ kind : StatementKind :: Validate ( ValidationOp :: Release ,
215
+ args. iter ( ) . filter_map ( |op| {
216
+ match op {
217
+ & Operand :: Consume ( ref lval) =>
218
+ Some ( lval_to_operand ( lval. clone ( ) ) ) ,
219
+ & Operand :: Constant ( ..) => { None } ,
220
+ }
221
+ } ) . collect ( ) )
222
+ } ;
223
+ block_data. statements . push ( release_stmt) ;
224
+ }
140
225
// Remember the return destination for later
141
226
if let & Some ( ref destination) = destination {
142
227
returns. push ( ( source_info, destination. 0 . clone ( ) , destination. 1 ) ) ;
@@ -147,12 +232,14 @@ impl MirPass for AddValidation {
147
232
Some ( Terminator { kind : TerminatorKind :: DropAndReplace { location : ref lval, .. } ,
148
233
source_info } ) => {
149
234
// Before the call: Release all arguments
150
- let release_stmt = Statement {
151
- source_info,
152
- kind : StatementKind :: Validate ( ValidationOp :: Release ,
153
- vec ! [ lval_to_operand( lval. clone( ) ) ] ) ,
154
- } ;
155
- block_data. statements . push ( release_stmt) ;
235
+ if !restricted_validation {
236
+ let release_stmt = Statement {
237
+ source_info,
238
+ kind : StatementKind :: Validate ( ValidationOp :: Release ,
239
+ vec ! [ lval_to_operand( lval. clone( ) ) ] ) ,
240
+ } ;
241
+ block_data. statements . push ( release_stmt) ;
242
+ }
156
243
// drop doesn't return anything, so we need no acquire.
157
244
}
158
245
_ => {
@@ -162,18 +249,21 @@ impl MirPass for AddValidation {
162
249
}
163
250
// Now we go over the returns we collected to acquire the return values.
164
251
for ( source_info, dest_lval, dest_block) in returns {
165
- let acquire_stmt = Statement {
252
+ emit_acquire (
253
+ & mut mir. basic_blocks_mut ( ) [ dest_block] ,
166
254
source_info,
167
- kind : StatementKind :: Validate ( ValidationOp :: Acquire ,
168
- vec ! [ lval_to_operand( dest_lval) ] ) ,
169
- } ;
170
- mir. basic_blocks_mut ( ) [ dest_block] . statements . insert ( 0 , acquire_stmt) ;
255
+ vec ! [ lval_to_operand( dest_lval) ]
256
+ ) ;
257
+ }
258
+
259
+ if restricted_validation {
260
+ // No part 3 for us.
261
+ return ;
171
262
}
172
263
173
264
// PART 3
174
265
// Add ReleaseValid/AcquireValid around Ref and Cast. Again an iterator does not seem very
175
- // suited
176
- // as we need to add new statements before and after each Ref.
266
+ // suited as we need to add new statements before and after each Ref.
177
267
for block_data in mir. basic_blocks_mut ( ) {
178
268
// We want to insert statements around Ref commands as we iterate. To this end, we
179
269
// iterate backwards using indices.
0 commit comments