11//! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations.
22
33use std:: assert_matches:: assert_matches;
4+ use std:: borrow:: Cow ;
45use std:: mem;
56use std:: ops:: Deref ;
67
@@ -15,7 +16,9 @@ use rustc_middle::mir::*;
1516use rustc_middle:: span_bug;
1617use rustc_middle:: ty:: adjustment:: PointerCoercion ;
1718use 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 } ;
1922use rustc_span:: { sym, Span , Symbol , DUMMY_SP } ;
2023use rustc_trait_selection:: error_reporting:: InferCtxtErrorExt ;
2124use rustc_trait_selection:: traits:: { self , ObligationCauseCode , ObligationCtxt } ;
@@ -188,8 +191,9 @@ pub struct Checker<'mir, 'tcx> {
188191 /// The span of the current statement.
189192 span : Span ,
190193
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 > > ,
193197
194198 error_emitted : Option < ErrorGuaranteed > ,
195199 secondary_errors : Vec < Diag < ' tcx > > ,
@@ -209,7 +213,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
209213 span : ccx. body . span ,
210214 ccx,
211215 qualifs : Default :: default ( ) ,
212- local_has_storage_dead : None ,
216+ transient_locals : None ,
213217 error_emitted : None ,
214218 secondary_errors : Vec :: new ( ) ,
215219 }
@@ -264,23 +268,47 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
264268 }
265269 }
266270
267- fn local_has_storage_dead ( & mut self , local : Local ) -> bool {
271+ fn local_is_transient ( & mut self , local : Local ) -> bool {
268272 let ccx = self . ccx ;
269- self . local_has_storage_dead
273+ self . transient_locals
270274 . 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 > ,
273288 }
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+ }
278301 }
279302 }
280303 }
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
284312 } )
285313 . contains ( local)
286314 }
@@ -375,7 +403,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
375403 // `StorageDead` in every control flow path leading to a `return` terminator.
376404 // The good news is that interning will detect if any unexpected mutable
377405 // 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 ) {
379407 self . check_op ( ops:: TransientMutBorrow ( kind) ) ;
380408 } else {
381409 self . check_op ( ops:: MutBorrow ( kind) ) ;
@@ -526,7 +554,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
526554 // `StorageDead` in every control flow path leading to a `return` terminator.
527555 // The good news is that interning will detect if any unexpected mutable
528556 // pointer slips through.
529- if self . local_has_storage_dead ( place. local ) {
557+ if self . local_is_transient ( place. local ) {
530558 self . check_op ( ops:: TransientCellBorrow ) ;
531559 } else {
532560 self . check_op ( ops:: CellBorrow ) ;
0 commit comments