@@ -30,10 +30,14 @@ fn place_has_common_prefix<'tcx>(left: &Place<'tcx>, right: &Place<'tcx>) -> boo
3030 && left. projection . iter ( ) . zip ( right. projection ) . all ( |( left, right) | left == right)
3131}
3232
33+ /// Cache entry of `drop` at a `BasicBlock`
3334#[ derive( Debug , Clone , Copy ) ]
3435enum MovePathIndexAtBlock {
36+ /// We know nothing yet
3537 Unknown ,
38+ /// We know that the `drop` here has no effect
3639 None ,
40+ /// We know that the `drop` here will invoke a destructor
3741 Some ( MovePathIndex ) ,
3842}
3943
@@ -80,9 +84,14 @@ impl<'a, 'mir, 'tcx> DropsReachable<'a, 'mir, 'tcx> {
8084 block,
8185 statement_index : data. statements . len ( ) ,
8286 } ) ;
87+
88+ // Check if the drop of `place` under inspection is really in effect.
89+ // This is true only when `place` may have been initialized along a control flow path from a BID to the drop program point today.
90+ // In other words, this is where the drop of `place` will happen in the future instead.
8391 if let MaybeReachable :: Reachable ( maybe_init) = self . maybe_init . get ( )
8492 && maybe_init. contains ( idx)
8593 {
94+ // We also cache the drop information, so that we do not need to check on data-flow cursor again
8695 self . block_drop_value_info [ block] = MovePathIndexAtBlock :: Some ( idx) ;
8796 dropped_local_here. borrow_mut ( ) . insert ( idx) ;
8897 } else {
@@ -143,6 +152,10 @@ impl<'a, 'mir, 'tcx> DropsReachable<'a, 'mir, 'tcx> {
143152 }
144153}
145154
155+ /// An additional filter to exclude well-known types from the ecosystem
156+ /// because their drops are trivial.
157+ /// This returns additional types to check if the drops are delegated to those.
158+ /// A typical example is `hashbrown::HashMap<K, V>`, whose drop is delegated to `K` and `V`.
146159fn true_significant_drop_ty < ' tcx > (
147160 tcx : TyCtxt < ' tcx > ,
148161 ty : Ty < ' tcx > ,
@@ -200,6 +213,8 @@ fn true_significant_drop_ty<'tcx>(
200213 }
201214}
202215
216+ /// Returns the list of types with a "potentially sigificant" that may be dropped
217+ /// by dropping a value of type `ty`.
203218#[ instrument( level = "debug" , skip( tcx, param_env) ) ]
204219fn extract_component_raw < ' tcx > (
205220 tcx : TyCtxt < ' tcx > ,
@@ -237,6 +252,9 @@ fn extract_component_with_significant_dtor<'tcx>(
237252 tys. into_iter ( ) . collect ( )
238253}
239254
255+ /// Extract the span of the custom destructor of a type
256+ /// especially the span of the `impl Drop` header or its entire block
257+ /// when we are working with current local crate.
240258#[ instrument( level = "debug" , skip( tcx) ) ]
241259fn ty_dtor_span < ' tcx > ( tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > ) -> Option < Span > {
242260 match ty. kind ( ) {
@@ -291,6 +309,9 @@ fn ty_dtor_span<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Span> {
291309 }
292310}
293311
312+ /// Check if a moved place at `idx` is a part of a BID.
313+ /// The use of this check is that we will consider drops on these
314+ /// as a drop of the overall BID and, thus, we can exclude it from the diagnosis.
294315fn place_descendent_of_bids < ' tcx > (
295316 mut idx : MovePathIndex ,
296317 move_data : & MoveData < ' tcx > ,
@@ -309,6 +330,7 @@ fn place_descendent_of_bids<'tcx>(
309330 }
310331}
311332
333+ /// The core of the lint `tail-expr-drop-order`
312334pub ( crate ) fn run_lint < ' tcx > ( tcx : TyCtxt < ' tcx > , def_id : LocalDefId , body : & Body < ' tcx > ) {
313335 if matches ! ( tcx. def_kind( def_id) , rustc_hir:: def:: DefKind :: SyntheticCoroutineBody ) {
314336 // A synthetic coroutine has no HIR body and it is enough to just analyse the original body
@@ -326,8 +348,10 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
326348 return ;
327349 }
328350 // ## About BIDs in blocks ##
329- // We are using blocks to identify locals with the same scope targeted by backwards-incompatible drops (BID)
330- // because they tend to be scheduled in the same drop ladder block.
351+ // Track the set of blocks that contain a backwards-incompatible drop (BID)
352+ // and, for each block, the vector of locations.
353+ //
354+ // We group them per-block because they tend to scheduled in the same drop ladder block.
331355 let mut bid_per_block = IndexMap :: default ( ) ;
332356 let mut bid_places = UnordSet :: new ( ) ;
333357 let param_env = tcx. param_env ( def_id) . with_reveal_all_normalized ( tcx) ;
@@ -358,6 +382,10 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
358382 dump_mir ( tcx, false , "lint_tail_expr_drop_order" , & 0 as _ , body, |_, _| Ok ( ( ) ) ) ;
359383 let locals_with_user_names = collect_user_names ( body) ;
360384 let is_closure_like = tcx. is_closure_like ( def_id. to_def_id ( ) ) ;
385+
386+ // Compute the "maybe initialized" information for this body.
387+ // When we encounter a DROP of some place P we only care
388+ // about the drop if `P` may be initialized.
361389 let move_data = MoveData :: gather_moves ( body, tcx, |_| true ) ;
362390 let maybe_init = MaybeInitializedPlaces :: new ( tcx, body, & move_data) ;
363391 let mut maybe_init = maybe_init. iterate_to_fixpoint ( tcx, body, None ) . into_results_cursor ( body) ;
@@ -370,6 +398,12 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
370398 let mut drop_span = None ;
371399 for & ( _, place) in candidates. iter ( ) {
372400 let mut collected_drops = ChunkedBitSet :: new_empty ( move_data. move_paths . len ( ) ) ;
401+ // ## On detecting change in relative drop order ##
402+ // Iterate through each BID-containing block `block`.
403+ // If the place `P` targeted by the BID is "maybe initialized",
404+ // then search forward to find the actual `DROP(P)` point.
405+ // Everything dropped between the BID and the actual drop point
406+ // is something whose relative drop order will change.
373407 DropsReachable {
374408 body,
375409 place,
@@ -381,37 +415,62 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
381415 visited : Default :: default ( ) ,
382416 }
383417 . visit ( block) ;
384-
418+ // Compute the set `all_locals_dropped` of local variables that are dropped
419+ // after the BID point but before the current drop point.
420+ //
421+ // These are the variables whose drop impls will be reordered with respect
422+ // to `place`.
385423 all_locals_dropped. union ( & collected_drops) ;
386424 }
425+
426+ // We shall now exclude some local bindings for the following cases.
387427 {
388428 let mut to_exclude = ChunkedBitSet :: new_empty ( all_locals_dropped. domain_size ( ) ) ;
389429 // We will now do subtraction from the candidate dropped locals, because of the following reasons.
390430 for path_idx in all_locals_dropped. iter ( ) {
391431 let move_path = & move_data. move_paths [ path_idx] ;
392432 let dropped_local = move_path. place . local ;
393433 // a) A return value _0 will eventually be used
434+ // Example:
435+ // fn f() -> Droppy {
436+ // let _x = Droppy;
437+ // Droppy
438+ // }
439+ // _0 holds the literal `Droppy` and rightfully `_x` has to be dropped first
394440 if dropped_local == Local :: ZERO {
395441 debug ! ( ?dropped_local, "skip return value" ) ;
396442 to_exclude. insert ( path_idx) ;
397443 continue ;
398444 }
399445 // b) If we are analysing a closure, the captures are still dropped last.
400446 // This is part of the closure capture lifetime contract.
447+ // They are similar to the return value _0 with respect to lifetime rules.
401448 if is_closure_like && matches ! ( dropped_local, ty:: CAPTURE_STRUCT_LOCAL ) {
402449 debug ! ( ?dropped_local, "skip closure captures" ) ;
403450 to_exclude. insert ( path_idx) ;
404451 continue ;
405452 }
406453 // c) Sometimes we collect places that are projections into the BID locals,
407454 // so they are considered dropped now.
455+ // Example:
456+ // struct NotVeryDroppy(Droppy);
457+ // impl Drop for Droppy {..}
458+ // fn f() -> NotVeryDroppy {
459+ // let x = NotVeryDroppy(droppy());
460+ // {
461+ // let y: Droppy = x.0;
462+ // NotVeryDroppy(y)
463+ // }
464+ // }
465+ // `y` takes `x.0`, which invalidates `x` as a complete `NotVeryDroppy`
466+ // so there is no point in linting against `x` any more.
408467 if place_descendent_of_bids ( path_idx, & move_data, & bid_places) {
409468 debug ! ( ?dropped_local, "skip descendent of bids" ) ;
410469 to_exclude. insert ( path_idx) ;
411470 continue ;
412471 }
413472 let observer_ty = move_path. place . ty ( body, tcx) . ty ;
414- // d) The collect local has no custom destructor.
473+ // d) The collected local has no custom destructor that passes our ecosystem filter .
415474 if ty_dropped_components
416475 . entry ( observer_ty)
417476 . or_insert_with ( || {
@@ -440,6 +499,9 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
440499 // No drop effect is observable, so let us move on.
441500 continue ;
442501 }
502+
503+ // ## The final work to assemble the diagnosis ##
504+ // First collect or generate fresh names for local variable bindings and temporary values.
443505 let local_names = assign_observables_names (
444506 all_locals_dropped
445507 . iter ( )
@@ -544,6 +606,7 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
544606 }
545607}
546608
609+ /// Extract binding names if available for diagnosis
547610fn collect_user_names ( body : & Body < ' _ > ) -> IndexMap < Local , Symbol > {
548611 let mut names = IndexMap :: default ( ) ;
549612 for var_debug_info in & body. var_debug_info {
@@ -556,6 +619,7 @@ fn collect_user_names(body: &Body<'_>) -> IndexMap<Local, Symbol> {
556619 names
557620}
558621
622+ /// Assign names for anonymous or temporary values for diagnosis
559623fn assign_observables_names (
560624 locals : impl IntoIterator < Item = Local > ,
561625 user_names : & IndexMap < Local , Symbol > ,
@@ -599,6 +663,7 @@ struct LocalLabel<'a> {
599663 destructors : Vec < DestructorLabel < ' a > > ,
600664}
601665
666+ /// A custom `Subdiagnostic` implementation so that the notes are delivered in a specific order
602667impl Subdiagnostic for LocalLabel < ' _ > {
603668 fn add_to_diag_with <
604669 G : rustc_errors:: EmissionGuarantee ,
0 commit comments