@@ -10,8 +10,8 @@ use rustc_index::bit_set::ChunkedBitSet;
1010use rustc_index:: { IndexSlice , IndexVec } ;
1111use rustc_macros:: LintDiagnostic ;
1212use 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,
1515} ;
1616use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
1717use rustc_mir_dataflow:: impls:: MaybeInitializedPlaces ;
@@ -43,16 +43,27 @@ impl<'a, 'mir, 'tcx> DropsReachable<'a, 'mir, 'tcx> {
4343 fn visit ( & mut self , block : BasicBlock ) {
4444 let move_set_size = self . move_data . move_paths . len ( ) ;
4545 let make_new_path_set = || Rc :: new ( RefCell :: new ( ChunkedBitSet :: new_empty ( move_set_size) ) ) ;
46+
4647 let data = & self . body . basic_blocks [ block] ;
4748 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.
4852 let dropped_local_here =
4953 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`.
5056 if let Some ( dropped) = self . block_drop_value_info [ block] {
5157 dropped_local_here. borrow_mut ( ) . insert ( dropped) ;
5258 } else if let TerminatorKind :: Drop { place, .. } = & terminator. kind
5359 && let LookupResult :: Exact ( idx) | LookupResult :: Parent ( Some ( idx) ) =
5460 self . move_data . rev_lookup . find ( place. as_ref ( ) )
5561 {
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.
5667 self . maybe_init . seek_before_primary_effect ( Location {
5768 block,
5869 statement_index : data. statements . len ( ) ,
@@ -71,10 +82,13 @@ impl<'a, 'mir, 'tcx> DropsReachable<'a, 'mir, 'tcx> {
7182 continue ;
7283 }
7384
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`
7587 let dropped_local_there = match self . visited . entry ( succ) {
7688 hash_map:: Entry :: Occupied ( occupied_entry) => {
7789 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
7892 continue ;
7993 }
8094 occupied_entry. get ( ) . clone ( )
@@ -92,8 +106,12 @@ impl<'a, 'mir, 'tcx> DropsReachable<'a, 'mir, 'tcx> {
92106 } = & terminator. kind
93107 && place_has_common_prefix ( dropped_place, self . place )
94108 {
109+ // We have now reached the current drop of the `place`.
110+ // Let's check the observed dropped places in.
95111 self . collected_drops . union ( & * dropped_local_there. borrow ( ) ) ;
96112 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.
97115 * self . drop_span =
98116 Some ( self . body . source_scopes [ terminator. source_info . scope ] . span ) ;
99117 }
@@ -239,6 +257,7 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
239257 {
240258 return ;
241259 }
260+ // ## About BIDs in blocks ##
242261 // We are using blocks to identify locals with the same scope targeted by backwards-incompatible drops (BID)
243262 // because they tend to be scheduled in the same drop ladder block.
244263 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<
270289 let mut drop_span = None ;
271290 for & ( _, place) in candidates. iter ( ) {
272291 let mut collected_drops = ChunkedBitSet :: new_empty ( move_data. move_paths . len ( ) ) ;
273- let mut search = DropsReachable {
292+ DropsReachable {
274293 body,
275294 place,
276295 drop_span : & mut drop_span,
@@ -279,44 +298,48 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
279298 block_drop_value_info : & mut block_drop_value_info,
280299 collected_drops : & mut collected_drops,
281300 visited : Default :: default ( ) ,
282- } ;
283- search. visit ( block) ;
284- let _ = search;
301+ }
302+ . visit ( block) ;
285303
286304 all_locals_dropped. union ( & collected_drops) ;
287305 }
288306 {
289307 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.
290309 for path_idx in all_locals_dropped. iter ( ) {
291310 let move_path = & move_data. move_paths [ path_idx] ;
292311 let dropped_local = move_path. place . local ;
312+ // a) A return value _0 will eventually be used
293313 if dropped_local == Local :: ZERO {
294314 debug ! ( ?dropped_local, "skip return value" ) ;
295315 to_exclude. insert ( path_idx) ;
296316 continue ;
297317 }
318+ // b) If we are analysing a closure, the captures are still dropped last.
319+ // This is part of the closure capture lifetime contract.
298320 if is_closure_like && matches ! ( dropped_local, ty:: CAPTURE_STRUCT_LOCAL ) {
299321 debug ! ( ?dropped_local, "skip closure captures" ) ;
300322 to_exclude. insert ( path_idx) ;
301323 continue ;
302324 }
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.
308327 if place_descendent_of_bids ( path_idx, & move_data, & bid_places) {
309328 debug ! ( ?dropped_local, "skip descendent of bids" ) ;
310329 to_exclude. insert ( path_idx) ;
311330 continue ;
312331 }
313332 let observer_ty = move_path. place . ty ( body, tcx) . ty ;
333+ // d) The collect local has no custom destructor.
314334 if !observer_ty. has_significant_drop ( tcx, param_env) {
315335 debug ! ( ?dropped_local, "skip non-droppy types" ) ;
316336 to_exclude. insert ( path_idx) ;
317337 continue ;
318338 }
319339 }
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.
320343 if candidates. iter ( ) . all ( |& ( _, place) | candidates[ 0 ] . 1 . local == place. local ) {
321344 for path_idx in all_locals_dropped. iter ( ) {
322345 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<
333356 let mut lint_root = None ;
334357 let mut linted_spans = Vec :: with_capacity ( candidates. len ( ) ) ;
335358 let mut tys = Vec :: with_capacity ( candidates. len ( ) ) ;
359+ // We now collect the types with custom destructors.
336360 for & ( _, place) in candidates {
337361 let linted_local_decl = & body. local_decls [ place. local ] ;
338362 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 ) ;
345369 }
370+
346371 tys. extend ( extract_component_with_significant_dtor (
347372 tcx,
348373 param_env,
349374 linted_local_decl. ty ,
350375 ) ) ;
351376 }
377+ // Collect spans of the custom destructors.
352378 let linted_dtors = tys. into_iter ( ) . filter_map ( |ty| ty_dtor_span ( tcx, ty) ) . collect ( ) ;
353379
380+ // Similarly, custom destructors of the observed drops.
354381 let mut observer_spans = Vec :: with_capacity ( all_locals_dropped. count ( ) ) ;
355382 let mut observer_tys = Vec :: with_capacity ( all_locals_dropped. count ( ) ) ;
356383 for path_idx in all_locals_dropped. iter ( ) {
0 commit comments