134
134
use super :: * ;
135
135
136
136
use crate :: framework:: { Analysis , Results , ResultsCursor } ;
137
+ use crate :: impls:: MaybeBorrowedLocals ;
137
138
use crate :: {
138
139
AnalysisDomain , Backward , CallReturnPlaces , GenKill , GenKillAnalysis , ResultsRefCursor ,
139
140
} ;
@@ -145,6 +146,7 @@ use rustc_middle::mir::*;
145
146
use rustc_middle:: ty;
146
147
147
148
use either:: Either ;
149
+ use std:: cell:: RefCell ;
148
150
149
151
#[ derive( Copy , Clone , Debug ) ]
150
152
enum NodeKind {
@@ -234,6 +236,68 @@ impl<'a, 'tcx> BorrowDependencies<'a, 'tcx> {
234
236
node_idx
235
237
}
236
238
}
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
+ }
237
301
}
238
302
239
303
impl < ' a , ' tcx > Visitor < ' tcx > for BorrowDependencies < ' a , ' tcx > {
@@ -427,6 +491,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BorrowDependencies<'a, 'tcx> {
427
491
self . visit_operand ( arg, location) ;
428
492
}
429
493
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
+
430
498
self . current_local = None ;
431
499
}
432
500
TerminatorKind :: Yield { resume_arg, value, .. } => {
@@ -444,7 +512,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BorrowDependencies<'a, 'tcx> {
444
512
}
445
513
}
446
514
447
- pub struct BorrowedLocalsResults < ' mir , ' tcx > {
515
+ pub struct BorrowedLocalsResults < ' a , ' mir , ' tcx > {
448
516
// the results of the liveness analysis of `LiveBorrows`
449
517
borrows_analysis_results : Results < ' tcx , LiveBorrows < ' mir , ' tcx > > ,
450
518
@@ -453,28 +521,49 @@ pub struct BorrowedLocalsResults<'mir, 'tcx> {
453
521
// exposed pointers or a composite value that might include refs, pointers or exposed pointers)
454
522
// to the set of `Local`s that are borrowed through those references, pointers or composite values.
455
523
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
+ > ,
456
528
}
457
529
458
- impl < ' mir , ' tcx > BorrowedLocalsResults < ' mir , ' tcx >
530
+ impl < ' a , ' mir , ' tcx > BorrowedLocalsResults < ' a , ' mir , ' tcx >
459
531
where
460
532
' tcx : ' mir ,
533
+ ' tcx : ' a ,
461
534
{
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 {
463
544
let dep_graph = & borrows_analysis_results. analysis . borrow_deps . dep_graph ;
464
545
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
+ }
466
553
}
467
554
468
555
/// Uses the dependency graph to find all locals that we need to keep live for a given
469
556
/// `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 , ( ) > ,
472
560
) -> FxHashMap < Local , Vec < Local > > {
473
561
let mut borrows_to_locals: FxHashMap < Local , Vec < Local > > = Default :: default ( ) ;
474
562
let mut locals_visited = vec ! [ ] ;
475
563
476
564
for ( node_idx, node) in dep_graph. enumerated_nodes ( ) {
477
565
let current_local = node. data . get_local ( ) ;
566
+ debug ! ( ?current_local) ;
478
567
if borrows_to_locals. get ( & current_local) . is_none ( ) {
479
568
Self :: dfs_for_node (
480
569
node_idx,
@@ -489,6 +578,7 @@ where
489
578
borrows_to_locals
490
579
}
491
580
581
+ #[ instrument( skip( dep_graph) , level = "debug" ) ]
492
582
fn dfs_for_node (
493
583
node_idx : NodeIndex ,
494
584
borrows_to_locals : & mut FxHashMap < Local , Vec < Local > > ,
@@ -499,6 +589,7 @@ where
499
589
let current_local = src_node. data . get_local ( ) ;
500
590
locals_visited. push ( current_local) ;
501
591
if let Some ( locals_to_keep_alive) = borrows_to_locals. get ( & current_local) {
592
+ debug ! ( "already prev. calculated: {:?}" , locals_to_keep_alive) ;
502
593
// already traversed this node
503
594
return ( * locals_to_keep_alive) . clone ( ) ;
504
595
}
@@ -510,7 +601,17 @@ where
510
601
let target_node_idx = edge. target ( ) ;
511
602
let target_node = dep_graph. node ( target_node_idx) ;
512
603
let target_local = target_node. data . get_local ( ) ;
604
+
605
+ // necessary to prevent loops
513
606
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
+
514
615
continue ;
515
616
}
516
617
@@ -537,18 +638,25 @@ where
537
638
NodeKind :: Borrow ( _) => { }
538
639
}
539
640
641
+ debug ! ( "locals for {:?}: {:?}" , current_local, locals_for_node) ;
540
642
borrows_to_locals. insert ( current_local, locals_for_node. clone ( ) ) ;
541
643
locals_for_node
542
644
}
543
645
}
544
646
545
647
/// The function gets the results of the borrowed locals analysis in this module. See the module
546
648
/// 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 > (
549
651
body : & ' mir Body < ' tcx > ,
550
652
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 > {
552
660
debug ! ( "body: {:#?}" , body) ;
553
661
554
662
let mut borrow_deps = BorrowDependencies :: new ( body. local_decls ( ) , tcx) ;
@@ -585,7 +693,7 @@ pub fn get_borrowed_locals_results<'mir, 'tcx>(
585
693
let results =
586
694
live_borrows. into_engine ( tcx, body) . pass_name ( "borrowed_locals" ) . iterate_to_fixpoint ( ) ;
587
695
588
- BorrowedLocalsResults :: new ( results)
696
+ BorrowedLocalsResults :: new ( results, maybe_borrowed_locals_cursor )
589
697
}
590
698
591
699
/// The `ResultsCursor` equivalent for the borrowed locals analysis. Since this analysis doesn't
@@ -602,10 +710,15 @@ pub struct BorrowedLocalsResultsCursor<'a, 'mir, 'tcx> {
602
710
// corresponding to `NodeKind::LocalWithRefs` since they might contain refs, ptrs or
603
711
// exposed pointers and need to be treated equivalently to refs/ptrs
604
712
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
+ > ,
605
718
}
606
719
607
720
impl < ' 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 {
609
722
let mut cursor = ResultsCursor :: new ( body, & results. borrows_analysis_results ) ;
610
723
611
724
// 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> {
617
730
body,
618
731
borrows_analysis_cursor : cursor,
619
732
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 ,
620
734
}
621
735
}
622
736
@@ -662,6 +776,16 @@ impl<'a, 'mir, 'tcx> BorrowedLocalsResultsCursor<'a, 'mir, 'tcx> {
662
776
Either :: Left ( _) => { }
663
777
}
664
778
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
+
665
789
debug ! ( ?borrowed_locals) ;
666
790
borrowed_locals
667
791
}
@@ -821,7 +945,14 @@ where
821
945
debug ! ( "gen {:?}" , local) ;
822
946
self . _trans . gen ( local) ;
823
947
}
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
+ }
825
956
}
826
957
827
958
self . super_place ( place, context, location) ;
0 commit comments