@@ -10,8 +10,8 @@ use rustc_index::bit_set::ChunkedBitSet;
10
10
use rustc_index:: { IndexSlice , IndexVec } ;
11
11
use rustc_macros:: LintDiagnostic ;
12
12
use rustc_middle:: mir:: {
13
- BasicBlock , Body , ClearCrossCrate , Local , Location , Place , ProjectionElem , StatementKind ,
14
- TerminatorKind , dump_mir,
13
+ BasicBlock , Body , ClearCrossCrate , Local , Location , Place , StatementKind , TerminatorKind ,
14
+ dump_mir,
15
15
} ;
16
16
use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
17
17
use rustc_mir_dataflow:: impls:: MaybeInitializedPlaces ;
@@ -43,16 +43,27 @@ impl<'a, 'mir, 'tcx> DropsReachable<'a, 'mir, 'tcx> {
43
43
fn visit ( & mut self , block : BasicBlock ) {
44
44
let move_set_size = self . move_data . move_paths . len ( ) ;
45
45
let make_new_path_set = || Rc :: new ( RefCell :: new ( ChunkedBitSet :: new_empty ( move_set_size) ) ) ;
46
+
46
47
let data = & self . body . basic_blocks [ block] ;
47
48
let Some ( terminator) = & data. terminator else { return } ;
49
+ // Given that we observe these dropped locals here at `block` so far,
50
+ // we will try to update the successor blocks.
51
+ // An occupied entry at `block` in `self.visited` signals that we have visited `block` before.
48
52
let dropped_local_here =
49
53
self . visited . entry ( block) . or_insert_with ( make_new_path_set) . clone ( ) ;
54
+ // We could have invoked reverse lookup for a `MovePathIndex` every time, but unfortunately it is expensive.
55
+ // Let's cache them in `self.block_drop_value_info`.
50
56
if let Some ( dropped) = self . block_drop_value_info [ block] {
51
57
dropped_local_here. borrow_mut ( ) . insert ( dropped) ;
52
58
} else if let TerminatorKind :: Drop { place, .. } = & terminator. kind
53
59
&& let LookupResult :: Exact ( idx) | LookupResult :: Parent ( Some ( idx) ) =
54
60
self . move_data . rev_lookup . find ( place. as_ref ( ) )
55
61
{
62
+ // Since we are working with MIRs at a very early stage,
63
+ // observing a `drop` terminator is not indicative enough that
64
+ // the drop will definitely happen.
65
+ // That is decided in the drop elaboration pass instead.
66
+ // Therefore, we need to consult with the maybe-initialization information.
56
67
self . maybe_init . seek_before_primary_effect ( Location {
57
68
block,
58
69
statement_index : data. statements . len ( ) ,
@@ -71,10 +82,13 @@ impl<'a, 'mir, 'tcx> DropsReachable<'a, 'mir, 'tcx> {
71
82
continue ;
72
83
}
73
84
74
- // As long as we are passing through a new block, or new dropped place to propagate, we will proceed
85
+ // As long as we are passing through a new block, or new dropped places to propagate,
86
+ // we will proceed with `succ`
75
87
let dropped_local_there = match self . visited . entry ( succ) {
76
88
hash_map:: Entry :: Occupied ( occupied_entry) => {
77
89
if !occupied_entry. get ( ) . borrow_mut ( ) . union ( & * dropped_local_here. borrow ( ) ) {
90
+ // `succ` has been visited but no new drops observed so far,
91
+ // so we can bail on `succ` until new drop information arrives
78
92
continue ;
79
93
}
80
94
occupied_entry. get ( ) . clone ( )
@@ -92,8 +106,12 @@ impl<'a, 'mir, 'tcx> DropsReachable<'a, 'mir, 'tcx> {
92
106
} = & terminator. kind
93
107
&& place_has_common_prefix ( dropped_place, self . place )
94
108
{
109
+ // We have now reached the current drop of the `place`.
110
+ // Let's check the observed dropped places in.
95
111
self . collected_drops . union ( & * dropped_local_there. borrow ( ) ) ;
96
112
if self . drop_span . is_none ( ) {
113
+ // FIXME(@dingxiangfei2009): it turns out that `self.body.source_scopes` are still a bit wonky.
114
+ // There is a high chance that this span still points to a block rather than a statement semicolon.
97
115
* self . drop_span =
98
116
Some ( self . body . source_scopes [ terminator. source_info . scope ] . span ) ;
99
117
}
@@ -239,6 +257,7 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
239
257
{
240
258
return ;
241
259
}
260
+ // ## About BIDs in blocks ##
242
261
// We are using blocks to identify locals with the same scope targeted by backwards-incompatible drops (BID)
243
262
// because they tend to be scheduled in the same drop ladder block.
244
263
let mut bid_per_block = IndexMap :: default ( ) ;
@@ -270,7 +289,7 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
270
289
let mut drop_span = None ;
271
290
for & ( _, place) in candidates. iter ( ) {
272
291
let mut collected_drops = ChunkedBitSet :: new_empty ( move_data. move_paths . len ( ) ) ;
273
- let mut search = DropsReachable {
292
+ DropsReachable {
274
293
body,
275
294
place,
276
295
drop_span : & mut drop_span,
@@ -279,44 +298,48 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
279
298
block_drop_value_info : & mut block_drop_value_info,
280
299
collected_drops : & mut collected_drops,
281
300
visited : Default :: default ( ) ,
282
- } ;
283
- search. visit ( block) ;
284
- let _ = search;
301
+ }
302
+ . visit ( block) ;
285
303
286
304
all_locals_dropped. union ( & collected_drops) ;
287
305
}
288
306
{
289
307
let mut to_exclude = ChunkedBitSet :: new_empty ( all_locals_dropped. domain_size ( ) ) ;
308
+ // We will now do subtraction from the candidate dropped locals, because of the following reasons.
290
309
for path_idx in all_locals_dropped. iter ( ) {
291
310
let move_path = & move_data. move_paths [ path_idx] ;
292
311
let dropped_local = move_path. place . local ;
312
+ // a) A return value _0 will eventually be used
293
313
if dropped_local == Local :: ZERO {
294
314
debug ! ( ?dropped_local, "skip return value" ) ;
295
315
to_exclude. insert ( path_idx) ;
296
316
continue ;
297
317
}
318
+ // b) If we are analysing a closure, the captures are still dropped last.
319
+ // This is part of the closure capture lifetime contract.
298
320
if is_closure_like && matches ! ( dropped_local, ty:: CAPTURE_STRUCT_LOCAL ) {
299
321
debug ! ( ?dropped_local, "skip closure captures" ) ;
300
322
to_exclude. insert ( path_idx) ;
301
323
continue ;
302
324
}
303
- if let [ .., ProjectionElem :: Downcast ( _, _) ] = * * move_path. place . projection {
304
- debug ! ( ?move_path. place, "skip downcast which is not a real place" ) ;
305
- to_exclude. insert ( path_idx) ;
306
- continue ;
307
- }
325
+ // c) Sometimes we collect places that are projections into the BID locals,
326
+ // so they are considered dropped now.
308
327
if place_descendent_of_bids ( path_idx, & move_data, & bid_places) {
309
328
debug ! ( ?dropped_local, "skip descendent of bids" ) ;
310
329
to_exclude. insert ( path_idx) ;
311
330
continue ;
312
331
}
313
332
let observer_ty = move_path. place . ty ( body, tcx) . ty ;
333
+ // d) The collect local has no custom destructor.
314
334
if !observer_ty. has_significant_drop ( tcx, param_env) {
315
335
debug ! ( ?dropped_local, "skip non-droppy types" ) ;
316
336
to_exclude. insert ( path_idx) ;
317
337
continue ;
318
338
}
319
339
}
340
+ // Suppose that all BIDs point into the same local,
341
+ // we can remove the this local from the observed drops,
342
+ // so that we can focus our diagnosis more on the others.
320
343
if candidates. iter ( ) . all ( |& ( _, place) | candidates[ 0 ] . 1 . local == place. local ) {
321
344
for path_idx in all_locals_dropped. iter ( ) {
322
345
if move_data. move_paths [ path_idx] . place . local == candidates[ 0 ] . 1 . local {
@@ -333,24 +356,28 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
333
356
let mut lint_root = None ;
334
357
let mut linted_spans = Vec :: with_capacity ( candidates. len ( ) ) ;
335
358
let mut tys = Vec :: with_capacity ( candidates. len ( ) ) ;
359
+ // We now collect the types with custom destructors.
336
360
for & ( _, place) in candidates {
337
361
let linted_local_decl = & body. local_decls [ place. local ] ;
338
362
linted_spans. push ( linted_local_decl. source_info . span ) ;
339
- if lint_root . is_none ( ) {
340
- lint_root =
341
- match & body . source_scopes [ linted_local_decl . source_info . scope ] . local_data {
342
- ClearCrossCrate :: Set ( data ) => Some ( data . lint_root ) ,
343
- _ => continue ,
344
- } ;
363
+
364
+ if lint_root. is_none ( )
365
+ && let ClearCrossCrate :: Set ( data ) =
366
+ & body . source_scopes [ linted_local_decl . source_info . scope ] . local_data
367
+ {
368
+ lint_root = Some ( data . lint_root ) ;
345
369
}
370
+
346
371
tys. extend ( extract_component_with_significant_dtor (
347
372
tcx,
348
373
param_env,
349
374
linted_local_decl. ty ,
350
375
) ) ;
351
376
}
377
+ // Collect spans of the custom destructors.
352
378
let linted_dtors = tys. into_iter ( ) . filter_map ( |ty| ty_dtor_span ( tcx, ty) ) . collect ( ) ;
353
379
380
+ // Similarly, custom destructors of the observed drops.
354
381
let mut observer_spans = Vec :: with_capacity ( all_locals_dropped. count ( ) ) ;
355
382
let mut observer_tys = Vec :: with_capacity ( all_locals_dropped. count ( ) ) ;
356
383
for path_idx in all_locals_dropped. iter ( ) {
0 commit comments