1
- use std:: collections:: BTreeSet ;
1
+ use std:: cell:: RefCell ;
2
+ use std:: collections:: hash_map;
3
+ use std:: rc:: Rc ;
2
4
3
- use rustc_data_structures:: fx:: FxHashSet ;
5
+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
4
6
use rustc_data_structures:: unord:: UnordSet ;
5
7
use rustc_hir:: CRATE_HIR_ID ;
6
8
use 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 } ;
8
11
use rustc_macros:: LintDiagnostic ;
9
12
use rustc_middle:: mir:: {
10
13
BasicBlock , Body , ClearCrossCrate , Local , Location , Place , ProjectionElem , StatementKind ,
11
14
TerminatorKind , dump_mir,
12
15
} ;
13
16
use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
14
17
use 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 } ;
17
20
use rustc_session:: lint;
18
21
use rustc_span:: Span ;
19
22
use rustc_type_ir:: data_structures:: IndexMap ;
@@ -25,48 +28,83 @@ fn place_has_common_prefix<'tcx>(left: &Place<'tcx>, right: &Place<'tcx>) -> boo
25
28
&& left. projection . iter ( ) . zip ( right. projection ) . all ( |( left, right) | left == right)
26
29
}
27
30
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 {
41
71
continue ;
42
72
}
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 ( )
47
81
}
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 ( )
64
84
}
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)
65
105
}
66
106
}
67
- new_blocks = next_front;
68
107
}
69
- reachable
70
108
}
71
109
72
110
#[ 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<
226
264
let move_data = MoveData :: gather_moves ( body, tcx, |_| true ) ;
227
265
let maybe_init = MaybeInitializedPlaces :: new ( tcx, body, & move_data) ;
228
266
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 ( ) ) ;
229
268
for ( & block, candidates) in & bid_per_block {
230
269
let mut all_locals_dropped = ChunkedBitSet :: new_empty ( move_data. move_paths . len ( ) ) ;
231
- let mut linted_candidates = BTreeSet :: new ( ) ;
232
270
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 ( ) ,
237
282
} ;
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) ;
262
287
}
263
288
{
264
289
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<
292
317
continue ;
293
318
}
294
319
}
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 ) {
300
321
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 {
302
323
to_exclude. insert ( path_idx) ;
303
324
}
304
325
}
0 commit comments