@@ -12,6 +12,7 @@ use rustc_lint::{LateContext, LateLintPass};
12
12
use rustc_middle:: mir:: {
13
13
self , traversal,
14
14
visit:: { MutatingUseContext , NonMutatingUseContext , PlaceContext , Visitor as _} ,
15
+ Mutability ,
15
16
} ;
16
17
use rustc_middle:: ty:: { self , fold:: TypeVisitor , Ty } ;
17
18
use rustc_mir:: dataflow:: { Analysis , AnalysisDomain , GenKill , GenKillAnalysis , ResultsCursor } ;
@@ -87,13 +88,18 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
87
88
88
89
let mir = cx. tcx . optimized_mir ( def_id. to_def_id ( ) ) ;
89
90
91
+ let possible_borrowed = {
92
+ let mut vis = PossibleBorrowedVisitor :: new ( mir) ;
93
+ vis. visit_body ( mir) ;
94
+ vis. into_map ( cx)
95
+ } ;
90
96
let maybe_storage_live_result = MaybeStorageLive
91
97
. into_engine ( cx. tcx , mir)
92
98
. pass_name ( "redundant_clone" )
93
99
. iterate_to_fixpoint ( )
94
100
. into_results_cursor ( mir) ;
95
101
let mut possible_borrower = {
96
- let mut vis = PossibleBorrowerVisitor :: new ( cx, mir) ;
102
+ let mut vis = PossibleBorrowerVisitor :: new ( cx, mir, possible_borrowed ) ;
97
103
vis. visit_body ( mir) ;
98
104
vis. into_map ( cx, maybe_storage_live_result)
99
105
} ;
@@ -509,14 +515,20 @@ struct PossibleBorrowerVisitor<'a, 'tcx> {
509
515
possible_borrower : TransitiveRelation < mir:: Local > ,
510
516
body : & ' a mir:: Body < ' tcx > ,
511
517
cx : & ' a LateContext < ' tcx > ,
518
+ possible_borrowed : FxHashMap < mir:: Local , HybridBitSet < mir:: Local > > ,
512
519
}
513
520
514
521
impl < ' a , ' tcx > PossibleBorrowerVisitor < ' a , ' tcx > {
515
- fn new ( cx : & ' a LateContext < ' tcx > , body : & ' a mir:: Body < ' tcx > ) -> Self {
522
+ fn new (
523
+ cx : & ' a LateContext < ' tcx > ,
524
+ body : & ' a mir:: Body < ' tcx > ,
525
+ possible_borrowed : FxHashMap < mir:: Local , HybridBitSet < mir:: Local > > ,
526
+ ) -> Self {
516
527
Self {
517
528
possible_borrower : TransitiveRelation :: default ( ) ,
518
529
cx,
519
530
body,
531
+ possible_borrowed,
520
532
}
521
533
}
522
534
@@ -585,21 +597,108 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> {
585
597
..
586
598
} = & terminator. kind
587
599
{
600
+ // TODO add doc
588
601
// If the call returns something with lifetimes,
589
602
// let's conservatively assume the returned value contains lifetime of all the arguments.
590
603
// For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`.
591
- if ContainsRegion . visit_ty ( self . body . local_decls [ * dest ] . ty ) . is_continue ( ) {
592
- return ;
593
- }
604
+
605
+ let mut immutable_borrowers = vec ! [ ] ;
606
+ let mut mutable_borrowers = vec ! [ ] ;
594
607
595
608
for op in args {
596
609
match op {
597
610
mir:: Operand :: Copy ( p) | mir:: Operand :: Move ( p) => {
598
- self . possible_borrower . add ( p. local , * dest) ;
611
+ if let ty:: Ref ( _, _, Mutability :: Mut ) = self . body . local_decls [ p. local ] . ty . kind ( ) {
612
+ mutable_borrowers. push ( p. local ) ;
613
+ } else {
614
+ immutable_borrowers. push ( p. local ) ;
615
+ }
599
616
} ,
600
617
mir:: Operand :: Constant ( ..) => ( ) ,
601
618
}
602
619
}
620
+
621
+ let mut mutable_variables: Vec < mir:: Local > = mutable_borrowers
622
+ . iter ( )
623
+ . filter_map ( |r| self . possible_borrowed . get ( r) )
624
+ . flat_map ( |r| r. iter ( ) )
625
+ . collect ( ) ;
626
+
627
+ if ContainsRegion . visit_ty ( self . body . local_decls [ * dest] . ty ) . is_break ( ) {
628
+ mutable_variables. push ( * dest) ;
629
+ }
630
+
631
+ for y in mutable_variables {
632
+ for x in & immutable_borrowers {
633
+ self . possible_borrower . add ( * x, y) ;
634
+ }
635
+ for x in & mutable_borrowers {
636
+ self . possible_borrower . add ( * x, y) ;
637
+ }
638
+ }
639
+ }
640
+ }
641
+ }
642
+
643
+ /// Collect possible borrowed for every `&mut` local.
644
+ /// For exampel, `_1 = &mut _2` generate _1: {_2,...}
645
+ /// Known Problems: not sure all borrowed are tracked
646
+ struct PossibleBorrowedVisitor < ' a , ' tcx > {
647
+ possible_borrowed : TransitiveRelation < mir:: Local > ,
648
+ body : & ' a mir:: Body < ' tcx > ,
649
+ }
650
+
651
+ impl < ' a , ' tcx > PossibleBorrowedVisitor < ' a , ' tcx > {
652
+ fn new ( body : & ' a mir:: Body < ' tcx > ) -> Self {
653
+ Self {
654
+ possible_borrowed : TransitiveRelation :: default ( ) ,
655
+ body,
656
+ }
657
+ }
658
+
659
+ fn into_map ( self , cx : & LateContext < ' tcx > ) -> FxHashMap < mir:: Local , HybridBitSet < mir:: Local > > {
660
+ let mut map = FxHashMap :: default ( ) ;
661
+ for row in ( 1 ..self . body . local_decls . len ( ) ) . map ( mir:: Local :: from_usize) {
662
+ if is_copy ( cx, self . body . local_decls [ row] . ty ) {
663
+ continue ;
664
+ }
665
+
666
+ let borrowers = self . possible_borrowed . reachable_from ( & row) ;
667
+ if !borrowers. is_empty ( ) {
668
+ let mut bs = HybridBitSet :: new_empty ( self . body . local_decls . len ( ) ) ;
669
+ for & c in borrowers {
670
+ if c != mir:: Local :: from_usize ( 0 ) {
671
+ bs. insert ( c) ;
672
+ }
673
+ }
674
+
675
+ if !bs. is_empty ( ) {
676
+ map. insert ( row, bs) ;
677
+ }
678
+ }
679
+ }
680
+ map
681
+ }
682
+ }
683
+
684
+ impl < ' a , ' tcx > mir:: visit:: Visitor < ' tcx > for PossibleBorrowedVisitor < ' a , ' tcx > {
685
+ fn visit_assign ( & mut self , place : & mir:: Place < ' tcx > , rvalue : & mir:: Rvalue < ' _ > , _location : mir:: Location ) {
686
+ let lhs = place. local ;
687
+ match rvalue {
688
+ // Only consider `&mut`, which can modify origin place
689
+ mir:: Rvalue :: Ref ( _, rustc_middle:: mir:: BorrowKind :: Mut { .. } , borrowed) => {
690
+ self . possible_borrowed . add ( lhs, borrowed. local ) ;
691
+ } ,
692
+ // _2: &mut _;
693
+ // _3 = move _2
694
+ mir:: Rvalue :: Use ( mir:: Operand :: Move ( borrowed) ) => {
695
+ self . possible_borrowed . add ( lhs, borrowed. local ) ;
696
+ } ,
697
+ // _3 = move _2 as &mut _;
698
+ mir:: Rvalue :: Cast ( _, mir:: Operand :: Move ( borrowed) , _) => {
699
+ self . possible_borrowed . add ( lhs, borrowed. local ) ;
700
+ } ,
701
+ _ => { } ,
603
702
}
604
703
}
605
704
}
0 commit comments