1
1
//! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations.
2
2
3
3
use std:: assert_matches:: assert_matches;
4
+ use std:: borrow:: Cow ;
4
5
use std:: mem;
5
6
use std:: ops:: Deref ;
6
7
@@ -15,7 +16,9 @@ use rustc_middle::mir::*;
15
16
use rustc_middle:: span_bug;
16
17
use rustc_middle:: ty:: adjustment:: PointerCoercion ;
17
18
use rustc_middle:: ty:: { self , Instance , InstanceKind , Ty , TyCtxt , TypeVisitableExt } ;
18
- use rustc_mir_dataflow:: Analysis ;
19
+ use rustc_mir_dataflow:: impls:: MaybeStorageLive ;
20
+ use rustc_mir_dataflow:: storage:: always_storage_live_locals;
21
+ use rustc_mir_dataflow:: { Analysis , ResultsCursor } ;
19
22
use rustc_span:: { sym, Span , Symbol , DUMMY_SP } ;
20
23
use rustc_trait_selection:: error_reporting:: InferCtxtErrorExt ;
21
24
use rustc_trait_selection:: traits:: { self , ObligationCauseCode , ObligationCtxt } ;
@@ -188,8 +191,9 @@ pub struct Checker<'mir, 'tcx> {
188
191
/// The span of the current statement.
189
192
span : Span ,
190
193
191
- /// A set that stores for each local whether it has a `StorageDead` for it somewhere.
192
- local_has_storage_dead : Option < BitSet < Local > > ,
194
+ /// A set that stores for each local whether it is "transient", i.e. guaranteed to be dead
195
+ /// when this MIR body returns.
196
+ transient_locals : Option < BitSet < Local > > ,
193
197
194
198
error_emitted : Option < ErrorGuaranteed > ,
195
199
secondary_errors : Vec < Diag < ' tcx > > ,
@@ -209,7 +213,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
209
213
span : ccx. body . span ,
210
214
ccx,
211
215
qualifs : Default :: default ( ) ,
212
- local_has_storage_dead : None ,
216
+ transient_locals : None ,
213
217
error_emitted : None ,
214
218
secondary_errors : Vec :: new ( ) ,
215
219
}
@@ -264,23 +268,47 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
264
268
}
265
269
}
266
270
267
- fn local_has_storage_dead ( & mut self , local : Local ) -> bool {
271
+ fn local_is_transient ( & mut self , local : Local ) -> bool {
268
272
let ccx = self . ccx ;
269
- self . local_has_storage_dead
273
+ self . transient_locals
270
274
. get_or_insert_with ( || {
271
- struct StorageDeads {
272
- locals : BitSet < Local > ,
275
+ // A local is "transient" if it is guaranteed dead at all `Return`.
276
+ // So first compute the say of "maybe live" locals at each program point.
277
+ let always_live_locals = & always_storage_live_locals ( & ccx. body ) ;
278
+ let maybe_storage_live = MaybeStorageLive :: new ( Cow :: Borrowed ( always_live_locals) )
279
+ . into_engine ( ccx. tcx , & ccx. body )
280
+ . iterate_to_fixpoint ( )
281
+ . into_results_cursor ( & ccx. body ) ;
282
+
283
+ // And then check all `Return` in the MIR, and if a local is "maybe live" at a
284
+ // `Return` then it is definitely not transient.
285
+ struct TransientLocalVisitor < ' a , ' tcx > {
286
+ maybe_storage_live : ResultsCursor < ' a , ' tcx , MaybeStorageLive < ' a > > ,
287
+ transient : BitSet < Local > ,
273
288
}
274
- impl < ' tcx > Visitor < ' tcx > for StorageDeads {
275
- fn visit_statement ( & mut self , stmt : & Statement < ' tcx > , _: Location ) {
276
- if let StatementKind :: StorageDead ( l) = stmt. kind {
277
- self . locals . insert ( l) ;
289
+ impl < ' a , ' tcx > Visitor < ' tcx > for TransientLocalVisitor < ' a , ' tcx > {
290
+ fn visit_terminator (
291
+ & mut self ,
292
+ terminator : & Terminator < ' tcx > ,
293
+ location : Location ,
294
+ ) {
295
+ if matches ! ( terminator. kind, TerminatorKind :: Return ) {
296
+ self . maybe_storage_live . seek_after_primary_effect ( location) ;
297
+ for local in self . maybe_storage_live . get ( ) . iter ( ) {
298
+ // If a local may be live here, it is definitely not transient.
299
+ self . transient . remove ( local) ;
300
+ }
278
301
}
279
302
}
280
303
}
281
- let mut v = StorageDeads { locals : BitSet :: new_empty ( ccx. body . local_decls . len ( ) ) } ;
282
- v. visit_body ( ccx. body ) ;
283
- v. locals
304
+
305
+ let mut v = TransientLocalVisitor {
306
+ maybe_storage_live,
307
+ transient : BitSet :: new_filled ( ccx. body . local_decls . len ( ) ) ,
308
+ } ;
309
+ v. visit_body ( & ccx. body ) ;
310
+
311
+ v. transient
284
312
} )
285
313
. contains ( local)
286
314
}
@@ -375,7 +403,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
375
403
// `StorageDead` in every control flow path leading to a `return` terminator.
376
404
// The good news is that interning will detect if any unexpected mutable
377
405
// pointer slips through.
378
- if place. is_indirect ( ) || self . local_has_storage_dead ( place. local ) {
406
+ if place. is_indirect ( ) || self . local_is_transient ( place. local ) {
379
407
self . check_op ( ops:: TransientMutBorrow ( kind) ) ;
380
408
} else {
381
409
self . check_op ( ops:: MutBorrow ( kind) ) ;
@@ -526,7 +554,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
526
554
// `StorageDead` in every control flow path leading to a `return` terminator.
527
555
// The good news is that interning will detect if any unexpected mutable
528
556
// pointer slips through.
529
- if self . local_has_storage_dead ( place. local ) {
557
+ if self . local_is_transient ( place. local ) {
530
558
self . check_op ( ops:: TransientCellBorrow ) ;
531
559
} else {
532
560
self . check_op ( ops:: CellBorrow ) ;
0 commit comments