@@ -15,6 +15,7 @@ use rustc_hir::def_id::DefId;
15
15
use rustc_hir:: hir_id:: HirIdSet ;
16
16
use rustc_hir:: intravisit:: { self , Visitor } ;
17
17
use rustc_hir:: { Arm , Expr , ExprKind , Guard , HirId , Pat , PatKind } ;
18
+ use rustc_middle:: hir:: place:: { Place , PlaceBase } ;
18
19
use rustc_middle:: middle:: region:: { self , YieldData } ;
19
20
use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
20
21
use rustc_span:: symbol:: sym;
@@ -222,30 +223,37 @@ pub fn resolve_interior<'a, 'tcx>(
222
223
) {
223
224
let body = fcx. tcx . hir ( ) . body ( body_id) ;
224
225
225
- let mut drop_range_visitor = DropRangeVisitor :: default ( ) ;
226
-
227
- // Run ExprUseVisitor to find where values are consumed.
228
- ExprUseVisitor :: new (
229
- & mut drop_range_visitor,
230
- & fcx. infcx ,
231
- def_id. expect_local ( ) ,
232
- fcx. param_env ,
233
- & fcx. typeck_results . borrow ( ) ,
234
- )
235
- . consume_body ( body) ;
236
- intravisit:: walk_body ( & mut drop_range_visitor, body) ;
237
-
238
- let mut visitor = InteriorVisitor {
239
- fcx,
240
- types : FxIndexSet :: default ( ) ,
241
- region_scope_tree : fcx. tcx . region_scope_tree ( def_id) ,
242
- expr_count : 0 ,
243
- kind,
244
- prev_unresolved_span : None ,
245
- guard_bindings : <_ >:: default ( ) ,
246
- guard_bindings_set : <_ >:: default ( ) ,
247
- linted_values : <_ >:: default ( ) ,
248
- drop_ranges : drop_range_visitor. drop_ranges ,
226
+ let mut visitor = {
227
+ let mut drop_range_visitor = DropRangeVisitor {
228
+ consumed_places : <_ >:: default ( ) ,
229
+ borrowed_places : <_ >:: default ( ) ,
230
+ drop_ranges : vec ! [ <_>:: default ( ) ] ,
231
+ expr_count : 0 ,
232
+ } ;
233
+
234
+ // Run ExprUseVisitor to find where values are consumed.
235
+ ExprUseVisitor :: new (
236
+ & mut drop_range_visitor,
237
+ & fcx. infcx ,
238
+ def_id. expect_local ( ) ,
239
+ fcx. param_env ,
240
+ & fcx. typeck_results . borrow ( ) ,
241
+ )
242
+ . consume_body ( body) ;
243
+ intravisit:: walk_body ( & mut drop_range_visitor, body) ;
244
+
245
+ InteriorVisitor {
246
+ fcx,
247
+ types : FxIndexSet :: default ( ) ,
248
+ region_scope_tree : fcx. tcx . region_scope_tree ( def_id) ,
249
+ expr_count : 0 ,
250
+ kind,
251
+ prev_unresolved_span : None ,
252
+ guard_bindings : <_ >:: default ( ) ,
253
+ guard_bindings_set : <_ >:: default ( ) ,
254
+ linted_values : <_ >:: default ( ) ,
255
+ drop_ranges : drop_range_visitor. drop_ranges . pop ( ) . unwrap ( ) ,
256
+ }
249
257
} ;
250
258
intravisit:: walk_body ( & mut visitor, body) ;
251
259
@@ -656,17 +664,37 @@ fn check_must_not_suspend_def(
656
664
}
657
665
658
666
/// This struct facilitates computing the ranges for which a place is uninitialized.
659
- #[ derive( Default ) ]
660
667
struct DropRangeVisitor {
661
668
consumed_places : HirIdSet ,
662
- drop_ranges : HirIdMap < DropRange > ,
669
+ borrowed_places : HirIdSet ,
670
+ drop_ranges : Vec < HirIdMap < DropRange > > ,
663
671
expr_count : usize ,
664
672
}
665
673
666
674
impl DropRangeVisitor {
667
675
fn record_drop ( & mut self , hir_id : HirId ) {
668
- debug ! ( "marking {:?} as dropped at {}" , hir_id, self . expr_count) ;
669
- self . drop_ranges . insert ( hir_id, DropRange { dropped_at : self . expr_count } ) ;
676
+ let drop_ranges = self . drop_ranges . last_mut ( ) . unwrap ( ) ;
677
+ if self . borrowed_places . contains ( & hir_id) {
678
+ debug ! ( "not marking {:?} as dropped because it is borrowed at some point" , hir_id) ;
679
+ } else if self . consumed_places . contains ( & hir_id) {
680
+ debug ! ( "marking {:?} as dropped at {}" , hir_id, self . expr_count) ;
681
+ drop_ranges. insert ( hir_id, DropRange { dropped_at : self . expr_count } ) ;
682
+ }
683
+ }
684
+
685
+ fn push_drop_scope ( & mut self ) {
686
+ self . drop_ranges . push ( <_ >:: default ( ) ) ;
687
+ }
688
+
689
+ fn pop_and_merge_drop_scope ( & mut self ) {
690
+ let mut old_last = self . drop_ranges . pop ( ) . unwrap ( ) ;
691
+ let drop_ranges = self . drop_ranges . last_mut ( ) . unwrap ( ) ;
692
+ for ( k, v) in old_last. drain ( ) {
693
+ match drop_ranges. get ( & k) . cloned ( ) {
694
+ Some ( v2) => drop_ranges. insert ( k, v. intersect ( & v2) ) ,
695
+ None => drop_ranges. insert ( k, v) ,
696
+ } ;
697
+ }
670
698
}
671
699
672
700
/// ExprUseVisitor's consume callback doesn't go deep enough for our purposes in all
@@ -685,6 +713,14 @@ impl DropRangeVisitor {
685
713
}
686
714
}
687
715
716
+ fn place_hir_id ( place : & Place < ' _ > ) -> Option < HirId > {
717
+ match place. base {
718
+ PlaceBase :: Rvalue | PlaceBase :: StaticItem => None ,
719
+ PlaceBase :: Local ( hir_id)
720
+ | PlaceBase :: Upvar ( ty:: UpvarId { var_path : ty:: UpvarPath { hir_id } , .. } ) => Some ( hir_id) ,
721
+ }
722
+ }
723
+
688
724
impl < ' tcx > expr_use_visitor:: Delegate < ' tcx > for DropRangeVisitor {
689
725
fn consume (
690
726
& mut self ,
@@ -693,14 +729,16 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for DropRangeVisitor {
693
729
) {
694
730
debug ! ( "consume {:?}; diag_expr_id={:?}" , place_with_id, diag_expr_id) ;
695
731
self . consumed_places . insert ( place_with_id. hir_id ) ;
732
+ place_hir_id ( & place_with_id. place ) . map ( |place| self . consumed_places . insert ( place) ) ;
696
733
}
697
734
698
735
fn borrow (
699
736
& mut self ,
700
- _place_with_id : & expr_use_visitor:: PlaceWithHirId < ' tcx > ,
737
+ place_with_id : & expr_use_visitor:: PlaceWithHirId < ' tcx > ,
701
738
_diag_expr_id : hir:: HirId ,
702
739
_bk : rustc_middle:: ty:: BorrowKind ,
703
740
) {
741
+ place_hir_id ( & place_with_id. place ) . map ( |place| self . borrowed_places . insert ( place) ) ;
704
742
}
705
743
706
744
fn mutate (
@@ -726,17 +764,44 @@ impl<'tcx> Visitor<'tcx> for DropRangeVisitor {
726
764
NestedVisitorMap :: None
727
765
}
728
766
729
- fn visit_expr ( & mut self , expr : & Expr < ' _ > ) {
730
- intravisit:: walk_expr ( self , expr) ;
767
+ fn visit_expr ( & mut self , expr : & ' tcx Expr < ' tcx > ) {
768
+ match expr. kind {
769
+ ExprKind :: AssignOp ( _, lhs, rhs) => {
770
+ // These operations are weird because their order of evaluation depends on whether
771
+ // the operator is overloaded. In a perfect world, we'd just ask the type checker
772
+ // whether this is a method call, but we also need to match the expression IDs
773
+ // from RegionResolutionVisitor. RegionResolutionVisitor doesn't know the order,
774
+ // so it runs both orders and picks the most conservative. We'll mirror that here.
775
+ let mut old_count = self . expr_count ;
776
+ intravisit:: walk_expr ( self , lhs) ;
777
+ intravisit:: walk_expr ( self , rhs) ;
778
+
779
+ self . push_drop_scope ( ) ;
780
+ std:: mem:: swap ( & mut old_count, & mut self . expr_count ) ;
781
+ intravisit:: walk_expr ( self , rhs) ;
782
+ intravisit:: walk_expr ( self , lhs) ;
783
+
784
+ // We should have visited the same number of expressions in either order.
785
+ assert_eq ! ( old_count, self . expr_count) ;
786
+
787
+ self . pop_and_merge_drop_scope ( ) ;
788
+ }
789
+ _ => intravisit:: walk_expr ( self , expr) ,
790
+ }
731
791
732
792
self . expr_count += 1 ;
793
+ self . consume_expr ( expr) ;
794
+ }
733
795
734
- if self . consumed_places . contains ( & expr. hir_id ) {
735
- self . consume_expr ( expr) ;
736
- }
796
+ fn visit_pat ( & mut self , pat : & ' tcx Pat < ' tcx > ) {
797
+ intravisit:: walk_pat ( self , pat) ;
798
+
799
+ // Increment expr_count here to match what InteriorVisitor expects.
800
+ self . expr_count += 1 ;
737
801
}
738
802
}
739
803
804
+ #[ derive( Clone ) ]
740
805
struct DropRange {
741
806
/// The post-order id of the point where this expression is dropped.
742
807
///
@@ -745,7 +810,11 @@ struct DropRange {
745
810
}
746
811
747
812
impl DropRange {
813
+ fn intersect ( & self , other : & Self ) -> Self {
814
+ Self { dropped_at : self . dropped_at . max ( other. dropped_at ) }
815
+ }
816
+
748
817
fn contains ( & self , id : usize ) -> bool {
749
- id >= self . dropped_at
818
+ id > self . dropped_at
750
819
}
751
820
}
0 commit comments