@@ -30,10 +30,14 @@ fn place_has_common_prefix<'tcx>(left: &Place<'tcx>, right: &Place<'tcx>) -> boo
30
30
&& left. projection . iter ( ) . zip ( right. projection ) . all ( |( left, right) | left == right)
31
31
}
32
32
33
+ /// Cache entry of `drop` at a `BasicBlock`
33
34
#[ derive( Debug , Clone , Copy ) ]
34
35
enum MovePathIndexAtBlock {
36
+ /// We know nothing yet
35
37
Unknown ,
38
+ /// We know that the `drop` here has no effect
36
39
None ,
40
+ /// We know that the `drop` here will invoke a destructor
37
41
Some ( MovePathIndex ) ,
38
42
}
39
43
@@ -80,9 +84,14 @@ impl<'a, 'mir, 'tcx> DropsReachable<'a, 'mir, 'tcx> {
80
84
block,
81
85
statement_index : data. statements . len ( ) ,
82
86
} ) ;
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.
83
91
if let MaybeReachable :: Reachable ( maybe_init) = self . maybe_init . get ( )
84
92
&& maybe_init. contains ( idx)
85
93
{
94
+ // We also cache the drop information, so that we do not need to check on data-flow cursor again
86
95
self . block_drop_value_info [ block] = MovePathIndexAtBlock :: Some ( idx) ;
87
96
dropped_local_here. borrow_mut ( ) . insert ( idx) ;
88
97
} else {
@@ -143,6 +152,10 @@ impl<'a, 'mir, 'tcx> DropsReachable<'a, 'mir, 'tcx> {
143
152
}
144
153
}
145
154
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`.
146
159
fn true_significant_drop_ty < ' tcx > (
147
160
tcx : TyCtxt < ' tcx > ,
148
161
ty : Ty < ' tcx > ,
@@ -200,6 +213,8 @@ fn true_significant_drop_ty<'tcx>(
200
213
}
201
214
}
202
215
216
+ /// Returns the list of types with a "potentially sigificant" that may be dropped
217
+ /// by dropping a value of type `ty`.
203
218
#[ instrument( level = "debug" , skip( tcx, param_env) ) ]
204
219
fn extract_component_raw < ' tcx > (
205
220
tcx : TyCtxt < ' tcx > ,
@@ -237,6 +252,9 @@ fn extract_component_with_significant_dtor<'tcx>(
237
252
tys. into_iter ( ) . collect ( )
238
253
}
239
254
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.
240
258
#[ instrument( level = "debug" , skip( tcx) ) ]
241
259
fn ty_dtor_span < ' tcx > ( tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > ) -> Option < Span > {
242
260
match ty. kind ( ) {
@@ -291,6 +309,9 @@ fn ty_dtor_span<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Span> {
291
309
}
292
310
}
293
311
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.
294
315
fn place_descendent_of_bids < ' tcx > (
295
316
mut idx : MovePathIndex ,
296
317
move_data : & MoveData < ' tcx > ,
@@ -309,6 +330,7 @@ fn place_descendent_of_bids<'tcx>(
309
330
}
310
331
}
311
332
333
+ /// The core of the lint `tail-expr-drop-order`
312
334
pub ( crate ) fn run_lint < ' tcx > ( tcx : TyCtxt < ' tcx > , def_id : LocalDefId , body : & Body < ' tcx > ) {
313
335
if matches ! ( tcx. def_kind( def_id) , rustc_hir:: def:: DefKind :: SyntheticCoroutineBody ) {
314
336
// 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<
326
348
return ;
327
349
}
328
350
// ## 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.
331
355
let mut bid_per_block = IndexMap :: default ( ) ;
332
356
let mut bid_places = UnordSet :: new ( ) ;
333
357
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<
358
382
dump_mir ( tcx, false , "lint_tail_expr_drop_order" , & 0 as _ , body, |_, _| Ok ( ( ) ) ) ;
359
383
let locals_with_user_names = collect_user_names ( body) ;
360
384
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.
361
389
let move_data = MoveData :: gather_moves ( body, tcx, |_| true ) ;
362
390
let maybe_init = MaybeInitializedPlaces :: new ( tcx, body, & move_data) ;
363
391
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<
370
398
let mut drop_span = None ;
371
399
for & ( _, place) in candidates. iter ( ) {
372
400
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.
373
407
DropsReachable {
374
408
body,
375
409
place,
@@ -381,37 +415,62 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
381
415
visited : Default :: default ( ) ,
382
416
}
383
417
. 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`.
385
423
all_locals_dropped. union ( & collected_drops) ;
386
424
}
425
+
426
+ // We shall now exclude some local bindings for the following cases.
387
427
{
388
428
let mut to_exclude = ChunkedBitSet :: new_empty ( all_locals_dropped. domain_size ( ) ) ;
389
429
// We will now do subtraction from the candidate dropped locals, because of the following reasons.
390
430
for path_idx in all_locals_dropped. iter ( ) {
391
431
let move_path = & move_data. move_paths [ path_idx] ;
392
432
let dropped_local = move_path. place . local ;
393
433
// 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
394
440
if dropped_local == Local :: ZERO {
395
441
debug ! ( ?dropped_local, "skip return value" ) ;
396
442
to_exclude. insert ( path_idx) ;
397
443
continue ;
398
444
}
399
445
// b) If we are analysing a closure, the captures are still dropped last.
400
446
// This is part of the closure capture lifetime contract.
447
+ // They are similar to the return value _0 with respect to lifetime rules.
401
448
if is_closure_like && matches ! ( dropped_local, ty:: CAPTURE_STRUCT_LOCAL ) {
402
449
debug ! ( ?dropped_local, "skip closure captures" ) ;
403
450
to_exclude. insert ( path_idx) ;
404
451
continue ;
405
452
}
406
453
// c) Sometimes we collect places that are projections into the BID locals,
407
454
// 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.
408
467
if place_descendent_of_bids ( path_idx, & move_data, & bid_places) {
409
468
debug ! ( ?dropped_local, "skip descendent of bids" ) ;
410
469
to_exclude. insert ( path_idx) ;
411
470
continue ;
412
471
}
413
472
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 .
415
474
if ty_dropped_components
416
475
. entry ( observer_ty)
417
476
. or_insert_with ( || {
@@ -440,6 +499,9 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
440
499
// No drop effect is observable, so let us move on.
441
500
continue ;
442
501
}
502
+
503
+ // ## The final work to assemble the diagnosis ##
504
+ // First collect or generate fresh names for local variable bindings and temporary values.
443
505
let local_names = assign_observables_names (
444
506
all_locals_dropped
445
507
. iter ( )
@@ -544,6 +606,7 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
544
606
}
545
607
}
546
608
609
+ /// Extract binding names if available for diagnosis
547
610
fn collect_user_names ( body : & Body < ' _ > ) -> IndexMap < Local , Symbol > {
548
611
let mut names = IndexMap :: default ( ) ;
549
612
for var_debug_info in & body. var_debug_info {
@@ -556,6 +619,7 @@ fn collect_user_names(body: &Body<'_>) -> IndexMap<Local, Symbol> {
556
619
names
557
620
}
558
621
622
+ /// Assign names for anonymous or temporary values for diagnosis
559
623
fn assign_observables_names (
560
624
locals : impl IntoIterator < Item = Local > ,
561
625
user_names : & IndexMap < Local , Symbol > ,
@@ -599,6 +663,7 @@ struct LocalLabel<'a> {
599
663
destructors : Vec < DestructorLabel < ' a > > ,
600
664
}
601
665
666
+ /// A custom `Subdiagnostic` implementation so that the notes are delivered in a specific order
602
667
impl Subdiagnostic for LocalLabel < ' _ > {
603
668
fn add_to_diag_with <
604
669
G : rustc_errors:: EmissionGuarantee ,
0 commit comments