134134use super :: * ;
135135
136136use crate :: framework:: { Analysis , Results , ResultsCursor } ;
137+ use crate :: impls:: MaybeBorrowedLocals ;
137138use crate :: {
138139 AnalysisDomain , Backward , CallReturnPlaces , GenKill , GenKillAnalysis , ResultsRefCursor ,
139140} ;
@@ -145,6 +146,7 @@ use rustc_middle::mir::*;
145146use rustc_middle:: ty;
146147
147148use either:: Either ;
149+ use std:: cell:: RefCell ;
148150
149151#[ derive( Copy , Clone , Debug ) ]
150152enum NodeKind {
@@ -234,6 +236,68 @@ impl<'a, 'tcx> BorrowDependencies<'a, 'tcx> {
234236 node_idx
235237 }
236238 }
239+
240+ /// Panics if `local` doesn't have a `Node` in `self.dep_graph`.
241+ fn get_node_idx_for_local ( & self , local : Local ) -> NodeIndex {
242+ let node_idx = self
243+ . locals_to_node_indexes
244+ . get ( & local)
245+ . unwrap_or_else ( || bug ! ( "expected {:?} to have a Node" , local) ) ;
246+
247+ * node_idx
248+ }
249+
250+ #[ instrument( skip( self ) , level = "debug" ) ]
251+ fn local_is_ref_ptr_or_localwithrefs ( & self , place : & Place < ' tcx > ) -> bool {
252+ let place_ty = place. ty ( self . local_decls , self . tcx ) . ty ;
253+ let is_ref_or_ptr = matches ! ( place_ty. kind( ) , ty:: Ref ( ..) | ty:: RawPtr ( ..) ) ;
254+
255+ // Also account for `LocalWithRef`s
256+ let is_local_with_refs =
257+ if let Some ( node_idx) = self . locals_to_node_indexes . get ( & place. local ) {
258+ let node_for_place = self . dep_graph . node ( * node_idx) ;
259+ debug ! ( ?node_for_place. data) ;
260+ matches ! ( node_for_place. data, NodeKind :: LocalWithRefs ( _) )
261+ } else {
262+ false
263+ } ;
264+
265+ debug ! ( ?is_ref_or_ptr, ?is_local_with_refs) ;
266+ is_ref_or_ptr || is_local_with_refs
267+ }
268+
269+ #[ instrument( skip( self ) , level = "debug" ) ]
270+ fn maybe_create_edges_for_operands ( & mut self , args : & Vec < Operand < ' tcx > > ) {
271+ for i in 0 ..args. len ( ) {
272+ let outer_operand = & args[ i] ;
273+ debug ! ( ?outer_operand) ;
274+ match outer_operand {
275+ Operand :: Copy ( outer_place) | Operand :: Move ( outer_place) => {
276+ if self . local_is_ref_ptr_or_localwithrefs ( outer_place) {
277+ for j in i + 1 ..args. len ( ) {
278+ let inner_operand = & args[ j] ;
279+ debug ! ( ?inner_operand) ;
280+ match inner_operand {
281+ Operand :: Copy ( inner_place) | Operand :: Move ( inner_place) => {
282+ if self . local_is_ref_ptr_or_localwithrefs ( inner_place) {
283+ let node_idx_outer =
284+ self . get_node_idx_for_local ( outer_place. local ) ;
285+ let node_idx_inner =
286+ self . get_node_idx_for_local ( inner_place. local ) ;
287+
288+ self . dep_graph . add_edge ( node_idx_outer, node_idx_inner, ( ) ) ;
289+ self . dep_graph . add_edge ( node_idx_inner, node_idx_outer, ( ) ) ;
290+ }
291+ }
292+ Operand :: Constant ( _) => { }
293+ }
294+ }
295+ }
296+ }
297+ Operand :: Constant ( _) => { }
298+ }
299+ }
300+ }
237301}
238302
239303impl < ' a , ' tcx > Visitor < ' tcx > for BorrowDependencies < ' a , ' tcx > {
@@ -427,6 +491,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BorrowDependencies<'a, 'tcx> {
427491 self . visit_operand ( arg, location) ;
428492 }
429493
494+ // Additionally we have to introduce edges between borrowed operands, since we could
495+ // mutate those in the call (either through mutable references or interior mutability)
496+ self . maybe_create_edges_for_operands ( args) ;
497+
430498 self . current_local = None ;
431499 }
432500 TerminatorKind :: Yield { resume_arg, value, .. } => {
@@ -444,7 +512,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BorrowDependencies<'a, 'tcx> {
444512 }
445513}
446514
447- pub struct BorrowedLocalsResults < ' mir , ' tcx > {
515+ pub struct BorrowedLocalsResults < ' a , ' mir , ' tcx > {
448516 // the results of the liveness analysis of `LiveBorrows`
449517 borrows_analysis_results : Results < ' tcx , LiveBorrows < ' mir , ' tcx > > ,
450518
@@ -453,28 +521,49 @@ pub struct BorrowedLocalsResults<'mir, 'tcx> {
453521 // exposed pointers or a composite value that might include refs, pointers or exposed pointers)
454522 // to the set of `Local`s that are borrowed through those references, pointers or composite values.
455523 borrowed_local_to_locals_to_keep_alive : FxHashMap < Local , Vec < Local > > ,
524+
525+ maybe_borrowed_locals_results_cursor : RefCell <
526+ ResultsCursor < ' mir , ' tcx , MaybeBorrowedLocals , & ' a Results < ' tcx , MaybeBorrowedLocals > > ,
527+ > ,
456528}
457529
458- impl < ' mir , ' tcx > BorrowedLocalsResults < ' mir , ' tcx >
530+ impl < ' a , ' mir , ' tcx > BorrowedLocalsResults < ' a , ' mir , ' tcx >
459531where
460532 ' tcx : ' mir ,
533+ ' tcx : ' a ,
461534{
462- fn new ( borrows_analysis_results : Results < ' tcx , LiveBorrows < ' mir , ' tcx > > ) -> Self {
535+ fn new (
536+ borrows_analysis_results : Results < ' tcx , LiveBorrows < ' mir , ' tcx > > ,
537+ maybe_borrowed_locals_results_cursor : ResultsCursor <
538+ ' mir ,
539+ ' tcx ,
540+ MaybeBorrowedLocals ,
541+ & ' a Results < ' tcx , MaybeBorrowedLocals > ,
542+ > ,
543+ ) -> Self {
463544 let dep_graph = & borrows_analysis_results. analysis . borrow_deps . dep_graph ;
464545 let borrowed_local_to_locals_to_keep_alive = Self :: get_locals_to_keep_alive_map ( dep_graph) ;
465- Self { borrows_analysis_results, borrowed_local_to_locals_to_keep_alive }
546+ Self {
547+ borrows_analysis_results,
548+ borrowed_local_to_locals_to_keep_alive,
549+ maybe_borrowed_locals_results_cursor : RefCell :: new (
550+ maybe_borrowed_locals_results_cursor,
551+ ) ,
552+ }
466553 }
467554
468555 /// Uses the dependency graph to find all locals that we need to keep live for a given
469556 /// `Node` (or more specically the `Local` corresponding to that `Node`).
470- fn get_locals_to_keep_alive_map < ' a > (
471- dep_graph : & ' a Graph < NodeKind , ( ) > ,
557+ #[ instrument( skip( dep_graph) , level = "debug" ) ]
558+ fn get_locals_to_keep_alive_map < ' b > (
559+ dep_graph : & ' b Graph < NodeKind , ( ) > ,
472560 ) -> FxHashMap < Local , Vec < Local > > {
473561 let mut borrows_to_locals: FxHashMap < Local , Vec < Local > > = Default :: default ( ) ;
474562 let mut locals_visited = vec ! [ ] ;
475563
476564 for ( node_idx, node) in dep_graph. enumerated_nodes ( ) {
477565 let current_local = node. data . get_local ( ) ;
566+ debug ! ( ?current_local) ;
478567 if borrows_to_locals. get ( & current_local) . is_none ( ) {
479568 Self :: dfs_for_node (
480569 node_idx,
@@ -489,6 +578,7 @@ where
489578 borrows_to_locals
490579 }
491580
581+ #[ instrument( skip( dep_graph) , level = "debug" ) ]
492582 fn dfs_for_node (
493583 node_idx : NodeIndex ,
494584 borrows_to_locals : & mut FxHashMap < Local , Vec < Local > > ,
@@ -499,6 +589,7 @@ where
499589 let current_local = src_node. data . get_local ( ) ;
500590 locals_visited. push ( current_local) ;
501591 if let Some ( locals_to_keep_alive) = borrows_to_locals. get ( & current_local) {
592+ debug ! ( "already prev. calculated: {:?}" , locals_to_keep_alive) ;
502593 // already traversed this node
503594 return ( * locals_to_keep_alive) . clone ( ) ;
504595 }
@@ -510,7 +601,17 @@ where
510601 let target_node_idx = edge. target ( ) ;
511602 let target_node = dep_graph. node ( target_node_idx) ;
512603 let target_local = target_node. data . get_local ( ) ;
604+
605+ // necessary to prevent loops
513606 if locals_visited. contains ( & target_local) {
607+ if let Some ( locals_to_keep_alive) = borrows_to_locals. get ( & target_local) {
608+ debug ! (
609+ "prev. calculated locals to keep alive for {:?}: {:?}" ,
610+ target_local, locals_to_keep_alive
611+ ) ;
612+ locals_for_node. append ( & mut locals_to_keep_alive. clone ( ) ) ;
613+ }
614+
514615 continue ;
515616 }
516617
@@ -537,18 +638,25 @@ where
537638 NodeKind :: Borrow ( _) => { }
538639 }
539640
641+ debug ! ( "locals for {:?}: {:?}" , current_local, locals_for_node) ;
540642 borrows_to_locals. insert ( current_local, locals_for_node. clone ( ) ) ;
541643 locals_for_node
542644 }
543645}
544646
545647/// The function gets the results of the borrowed locals analysis in this module. See the module
546648/// doc-comment for information on what exactly this analysis does.
547- #[ instrument( skip( tcx) , level = "debug" ) ]
548- pub fn get_borrowed_locals_results < ' mir , ' tcx > (
649+ #[ instrument( skip( tcx, maybe_borrowed_locals_cursor , body ) , level = "debug" ) ]
650+ pub fn get_borrowed_locals_results < ' a , ' mir , ' tcx > (
549651 body : & ' mir Body < ' tcx > ,
550652 tcx : TyCtxt < ' tcx > ,
551- ) -> BorrowedLocalsResults < ' mir , ' tcx > {
653+ maybe_borrowed_locals_cursor : ResultsCursor <
654+ ' mir ,
655+ ' tcx ,
656+ MaybeBorrowedLocals ,
657+ & ' a Results < ' tcx , MaybeBorrowedLocals > ,
658+ > ,
659+ ) -> BorrowedLocalsResults < ' a , ' mir , ' tcx > {
552660 debug ! ( "body: {:#?}" , body) ;
553661
554662 let mut borrow_deps = BorrowDependencies :: new ( body. local_decls ( ) , tcx) ;
@@ -585,7 +693,7 @@ pub fn get_borrowed_locals_results<'mir, 'tcx>(
585693 let results =
586694 live_borrows. into_engine ( tcx, body) . pass_name ( "borrowed_locals" ) . iterate_to_fixpoint ( ) ;
587695
588- BorrowedLocalsResults :: new ( results)
696+ BorrowedLocalsResults :: new ( results, maybe_borrowed_locals_cursor )
589697}
590698
591699/// The `ResultsCursor` equivalent for the borrowed locals analysis. Since this analysis doesn't
@@ -602,10 +710,15 @@ pub struct BorrowedLocalsResultsCursor<'a, 'mir, 'tcx> {
602710 // corresponding to `NodeKind::LocalWithRefs` since they might contain refs, ptrs or
603711 // exposed pointers and need to be treated equivalently to refs/ptrs
604712 borrowed_local_to_locals_to_keep_alive : & ' a FxHashMap < Local , Vec < Local > > ,
713+
714+ // the cursor of the conservative borrowed locals analysis
715+ maybe_borrowed_locals_results_cursor : & ' a RefCell <
716+ ResultsCursor < ' mir , ' tcx , MaybeBorrowedLocals , & ' a Results < ' tcx , MaybeBorrowedLocals > > ,
717+ > ,
605718}
606719
607720impl < ' a , ' mir , ' tcx > BorrowedLocalsResultsCursor < ' a , ' mir , ' tcx > {
608- pub fn new ( body : & ' mir Body < ' tcx > , results : & ' a BorrowedLocalsResults < ' mir , ' tcx > ) -> Self {
721+ pub fn new ( body : & ' mir Body < ' tcx > , results : & ' a BorrowedLocalsResults < ' a , ' mir , ' tcx > ) -> Self {
609722 let mut cursor = ResultsCursor :: new ( body, & results. borrows_analysis_results ) ;
610723
611724 // We don't care about the order of the blocks, only about the result at a given location.
@@ -617,6 +730,7 @@ impl<'a, 'mir, 'tcx> BorrowedLocalsResultsCursor<'a, 'mir, 'tcx> {
617730 body,
618731 borrows_analysis_cursor : cursor,
619732 borrowed_local_to_locals_to_keep_alive : & results. borrowed_local_to_locals_to_keep_alive ,
733+ maybe_borrowed_locals_results_cursor : & results. maybe_borrowed_locals_results_cursor ,
620734 }
621735 }
622736
@@ -662,6 +776,16 @@ impl<'a, 'mir, 'tcx> BorrowedLocalsResultsCursor<'a, 'mir, 'tcx> {
662776 Either :: Left ( _) => { }
663777 }
664778
779+ // use results of conservative analysis as an "upper bound" on the borrowed locals. This
780+ // is necessary since to guarantee soundness for this analysis requires us to be more conservative
781+ // in some cases than the analysis performed by `MaybeBorrowedLocals`.
782+ let mut maybe_borrowed_locals_cursor =
783+ self . maybe_borrowed_locals_results_cursor . borrow_mut ( ) ;
784+ maybe_borrowed_locals_cursor. allow_unreachable ( ) ;
785+ maybe_borrowed_locals_cursor. seek_before_primary_effect ( loc) ;
786+ let upper_bound_borrowed_locals = maybe_borrowed_locals_cursor. get ( ) ;
787+ borrowed_locals. intersect ( upper_bound_borrowed_locals) ;
788+
665789 debug ! ( ?borrowed_locals) ;
666790 borrowed_locals
667791 }
@@ -821,7 +945,14 @@ where
821945 debug ! ( "gen {:?}" , local) ;
822946 self . _trans . gen ( local) ;
823947 }
824- _ => { }
948+ _ => {
949+ if let Some ( node_idx) = self . borrow_deps . locals_to_node_indexes . get ( & local) {
950+ let node = self . borrow_deps . dep_graph . node ( * node_idx) ;
951+ if matches ! ( node. data, NodeKind :: LocalWithRefs ( _) ) {
952+ self . _trans . gen ( local) ;
953+ }
954+ }
955+ }
825956 }
826957
827958 self . super_place ( place, context, location) ;
0 commit comments