1- use std:: collections:: BTreeSet ;
1+ use std:: cell:: RefCell ;
2+ use std:: collections:: hash_map;
3+ use std:: rc:: Rc ;
24
3- use rustc_data_structures:: fx:: FxHashSet ;
5+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
46use rustc_data_structures:: unord:: UnordSet ;
57use rustc_hir:: CRATE_HIR_ID ;
68use rustc_hir:: def_id:: { DefId , LocalDefId } ;
7- use rustc_index:: bit_set:: { BitSet , ChunkedBitSet } ;
9+ use rustc_index:: bit_set:: ChunkedBitSet ;
10+ use rustc_index:: { IndexSlice , IndexVec } ;
811use rustc_macros:: LintDiagnostic ;
912use rustc_middle:: mir:: {
1013 BasicBlock , Body , ClearCrossCrate , Local , Location , Place , ProjectionElem , StatementKind ,
1114 TerminatorKind , dump_mir,
1215} ;
1316use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
1417use rustc_mir_dataflow:: impls:: MaybeInitializedPlaces ;
15- use rustc_mir_dataflow:: move_paths:: { MoveData , MovePathIndex } ;
16- use rustc_mir_dataflow:: { Analysis , MaybeReachable } ;
18+ use rustc_mir_dataflow:: move_paths:: { LookupResult , MoveData , MovePathIndex } ;
19+ use rustc_mir_dataflow:: { Analysis , MaybeReachable , ResultsCursor } ;
1720use rustc_session:: lint;
1821use rustc_span:: Span ;
1922use rustc_type_ir:: data_structures:: IndexMap ;
@@ -25,48 +28,83 @@ fn place_has_common_prefix<'tcx>(left: &Place<'tcx>, right: &Place<'tcx>) -> boo
2528 && left. projection . iter ( ) . zip ( right. projection ) . all ( |( left, right) | left == right)
2629}
2730
28- fn drops_reachable_from_location < ' tcx > (
29- body : & Body < ' tcx > ,
30- block : BasicBlock ,
31- place : & Place < ' tcx > ,
32- ) -> BitSet < BasicBlock > {
33- let mut reachable = BitSet :: new_empty ( body. basic_blocks . len ( ) ) ;
34- let mut visited = reachable. clone ( ) ;
35- let mut new_blocks = reachable. clone ( ) ;
36- new_blocks. insert ( block) ;
37- while !new_blocks. is_empty ( ) {
38- let mut next_front = BitSet :: new_empty ( new_blocks. domain_size ( ) ) ;
39- for bb in new_blocks. iter ( ) {
40- if !visited. insert ( bb) {
31+ struct DropsReachable < ' a , ' mir , ' tcx > {
32+ body : & ' a Body < ' tcx > ,
33+ place : & ' a Place < ' tcx > ,
34+ drop_span : & ' a mut Option < Span > ,
35+ move_data : & ' a MoveData < ' tcx > ,
36+ maybe_init : & ' a mut ResultsCursor < ' mir , ' tcx , MaybeInitializedPlaces < ' mir , ' tcx > > ,
37+ block_drop_value_info : & ' a mut IndexSlice < BasicBlock , Option < MovePathIndex > > ,
38+ collected_drops : & ' a mut ChunkedBitSet < MovePathIndex > ,
39+ visited : FxHashMap < BasicBlock , Rc < RefCell < ChunkedBitSet < MovePathIndex > > > > ,
40+ }
41+
42+ impl < ' a , ' mir , ' tcx > DropsReachable < ' a , ' mir , ' tcx > {
43+ fn visit ( & mut self , block : BasicBlock ) {
44+ let move_set_size = self . move_data . move_paths . len ( ) ;
45+ let make_new_path_set = || Rc :: new ( RefCell :: new ( ChunkedBitSet :: new_empty ( move_set_size) ) ) ;
46+ let data = & self . body . basic_blocks [ block] ;
47+ let Some ( terminator) = & data. terminator else { return } ;
48+ let dropped_local_here =
49+ self . visited . entry ( block) . or_insert_with ( make_new_path_set) . clone ( ) ;
50+ if let Some ( dropped) = self . block_drop_value_info [ block] {
51+ dropped_local_here. borrow_mut ( ) . insert ( dropped) ;
52+ } else if let TerminatorKind :: Drop { place, .. } = & terminator. kind
53+ && let LookupResult :: Exact ( idx) | LookupResult :: Parent ( Some ( idx) ) =
54+ self . move_data . rev_lookup . find ( place. as_ref ( ) )
55+ {
56+ self . maybe_init . seek_before_primary_effect ( Location {
57+ block,
58+ statement_index : data. statements . len ( ) ,
59+ } ) ;
60+ if let MaybeReachable :: Reachable ( maybe_init) = self . maybe_init . get ( )
61+ && maybe_init. contains ( idx)
62+ {
63+ self . block_drop_value_info [ block] = Some ( idx) ;
64+ dropped_local_here. borrow_mut ( ) . insert ( idx) ;
65+ }
66+ }
67+
68+ for succ in terminator. successors ( ) {
69+ let target = & self . body . basic_blocks [ succ] ;
70+ if target. is_cleanup {
4171 continue ;
4272 }
43- for succ in body. basic_blocks [ bb] . terminator . iter ( ) . flat_map ( |term| term. successors ( ) ) {
44- let target = & body. basic_blocks [ succ] ;
45- if target. is_cleanup {
46- continue ;
73+
74+ // As long as we are passing through a new block, or new dropped place to propagate, we will proceed
75+ let dropped_local_there = match self . visited . entry ( succ) {
76+ hash_map:: Entry :: Occupied ( occupied_entry) => {
77+ if !occupied_entry. get ( ) . borrow_mut ( ) . union ( & * dropped_local_here. borrow ( ) ) {
78+ continue ;
79+ }
80+ occupied_entry. get ( ) . clone ( )
4781 }
48- if !next_front. contains ( succ)
49- && let Some ( terminator) = & target. terminator
50- && let TerminatorKind :: Drop {
51- place : dropped_place,
52- target : _,
53- unwind : _,
54- replace : _,
55- } = & terminator. kind
56- && place_has_common_prefix ( dropped_place, place)
57- {
58- reachable. insert ( succ) ;
59- // Now we have discovered a simple control flow path from a future drop point
60- // to the current drop point.
61- // We will not continue here.
62- } else {
63- next_front. insert ( succ) ;
82+ hash_map:: Entry :: Vacant ( vacant_entry) => {
83+ vacant_entry. insert ( dropped_local_here. clone ( ) ) . clone ( )
6484 }
85+ } ;
86+ if let Some ( terminator) = & target. terminator
87+ && let TerminatorKind :: Drop {
88+ place : dropped_place,
89+ target : _,
90+ unwind : _,
91+ replace : _,
92+ } = & terminator. kind
93+ && place_has_common_prefix ( dropped_place, self . place )
94+ {
95+ self . collected_drops . union ( & * dropped_local_there. borrow ( ) ) ;
96+ if self . drop_span . is_none ( ) {
97+ * self . drop_span =
98+ Some ( self . body . source_scopes [ terminator. source_info . scope ] . span ) ;
99+ }
100+ // Now we have discovered a simple control flow path from a future drop point
101+ // to the current drop point.
102+ // We will not continue from there.
103+ } else {
104+ self . visit ( succ)
65105 }
66106 }
67- new_blocks = next_front;
68107 }
69- reachable
70108}
71109
72110#[ instrument( level = "debug" , skip( tcx, param_env) ) ]
@@ -226,39 +264,26 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
226264 let move_data = MoveData :: gather_moves ( body, tcx, |_| true ) ;
227265 let maybe_init = MaybeInitializedPlaces :: new ( tcx, body, & move_data) ;
228266 let mut maybe_init = maybe_init. iterate_to_fixpoint ( tcx, body, None ) . into_results_cursor ( body) ;
267+ let mut block_drop_value_info = IndexVec :: from_elem_n ( None , body. basic_blocks . len ( ) ) ;
229268 for ( & block, candidates) in & bid_per_block {
230269 let mut all_locals_dropped = ChunkedBitSet :: new_empty ( move_data. move_paths . len ( ) ) ;
231- let mut linted_candidates = BTreeSet :: new ( ) ;
232270 let mut drop_span = None ;
233- for ( idx, & ( candidate, place) ) in candidates. iter ( ) . enumerate ( ) {
234- maybe_init. seek_after_primary_effect ( candidate) ;
235- let MaybeReachable :: Reachable ( maybe_init_in_future) = maybe_init. get ( ) else {
236- continue ;
271+ for & ( _, place) in candidates. iter ( ) {
272+ let mut collected_drops = ChunkedBitSet :: new_empty ( move_data. move_paths . len ( ) ) ;
273+ let mut search = DropsReachable {
274+ body,
275+ place,
276+ drop_span : & mut drop_span,
277+ move_data : & move_data,
278+ maybe_init : & mut maybe_init,
279+ block_drop_value_info : & mut block_drop_value_info,
280+ collected_drops : & mut collected_drops,
281+ visited : Default :: default ( ) ,
237282 } ;
238- debug ! ( maybe_init_in_future = ?maybe_init_in_future. iter( ) . map( |idx| & move_data. move_paths[ idx] ) . collect:: <Vec <_>>( ) ) ;
239- let maybe_init_in_future = maybe_init_in_future. clone ( ) ;
240- for block in drops_reachable_from_location ( body, block, place) . iter ( ) {
241- let data = & body. basic_blocks [ block] ;
242- if drop_span. is_none ( ) {
243- drop_span = data
244- . terminator
245- . as_ref ( )
246- . map ( |term| body. source_scopes [ term. source_info . scope ] . span ) ;
247- }
248- debug ! ( ?candidate, "inspect" ) ;
249- maybe_init. seek_before_primary_effect ( Location {
250- block,
251- statement_index : data. statements . len ( ) ,
252- } ) ;
253- let MaybeReachable :: Reachable ( maybe_init_now) = maybe_init. get ( ) else { continue } ;
254- let mut locals_dropped = maybe_init_in_future. clone ( ) ;
255- debug ! ( maybe_init_now = ?maybe_init_now. iter( ) . map( |idx| & move_data. move_paths[ idx] ) . collect:: <Vec <_>>( ) ) ;
256- locals_dropped. subtract ( maybe_init_now) ;
257- debug ! ( locals_dropped = ?locals_dropped. iter( ) . map( |idx| & move_data. move_paths[ idx] ) . collect:: <Vec <_>>( ) ) ;
258- if all_locals_dropped. union ( & locals_dropped) {
259- linted_candidates. insert ( idx) ;
260- }
261- }
283+ search. visit ( block) ;
284+ let _ = search;
285+
286+ all_locals_dropped. union ( & collected_drops) ;
262287 }
263288 {
264289 let mut to_exclude = ChunkedBitSet :: new_empty ( all_locals_dropped. domain_size ( ) ) ;
@@ -292,13 +317,9 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
292317 continue ;
293318 }
294319 }
295- let mut iter = linted_candidates. iter ( ) ;
296- let Some ( first_linted_local) = iter. next ( ) . map ( |& idx| candidates[ idx] . 1 . local ) else {
297- continue ;
298- } ;
299- if iter. all ( |& idx| candidates[ idx] . 1 . local == first_linted_local) {
320+ if candidates. iter ( ) . all ( |& ( _, place) | candidates[ 0 ] . 1 . local == place. local ) {
300321 for path_idx in all_locals_dropped. iter ( ) {
301- if move_data. move_paths [ path_idx] . place . local == first_linted_local {
322+ if move_data. move_paths [ path_idx] . place . local == candidates [ 0 ] . 1 . local {
302323 to_exclude. insert ( path_idx) ;
303324 }
304325 }
0 commit comments