Skip to content

Commit f85fb2e

Browse files
dingxiangfei2009ehuss
authored andcommitted
apply suggestions
1 parent 9d90f66 commit f85fb2e

File tree

6 files changed

+135
-62
lines changed

6 files changed

+135
-62
lines changed

compiler/rustc_middle/src/mir/syntax.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ pub enum StatementKind<'tcx> {
440440
/// No-op. Useful for deleting instructions without affecting statement indices.
441441
Nop,
442442

443-
/// Marker statement for backward-incompatible drops in incoming future editions.
443+
/// Marker statement indicating where `place` would be dropped.
444444
/// This is semantically equivalent to `Nop`, so codegen and MIRI should interpret this
445445
/// statement as such.
446446
/// The only use case of this statement is for linting in MIR to detect temporary lifetime

compiler/rustc_middle/src/query/mod.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1447,12 +1447,20 @@ rustc_queries! {
14471447
cache_on_disk_if { false }
14481448
}
14491449

1450-
/// A list of types where a type requires drop if and only if any of those types
1451-
/// has significant drop. A type marked with the attribute `rustc_insignificant_dtor`
1452-
/// is considered to not be significant. A drop is significant if it is implemented
1453-
/// by the user or does anything that will have any observable behavior (other than
1454-
/// freeing up memory). If the type is known to have a significant destructor then
1455-
/// `Err(AlwaysRequiresDrop)` is returned.
1450+
/// Returns a list of types which (a) have a potentially significant destructor
1451+
/// and (b) may be dropped as a result of dropping a value of some type `ty`
1452+
/// (in the given environment).
1453+
///
1454+
/// The idea of "significant" drop is somewhat informal and is used only for
1455+
/// diagnostics and edition migrations. The idea is that a significant drop may have
1456+
/// some visible side-effect on execution; freeing memory is NOT considered a side-effect.
1457+
/// The rules are as follows:
1458+
/// * Type with no explicit drop impl do not have significant drop.
1459+
/// * Types with a drop impl are assumed to have significant drop unless they have a `#[rustc_insignificant_dtor]` annotation.
1460+
///
1461+
/// Note that insignificant drop is a "shallow" property. A type like `Vec<LockGuard>` does not
1462+
/// have significant drop but the type `LockGuard` does, and so if `ty = Vec<LockGuard>`
1463+
/// then the return value would be `&[LockGuard]`.
14561464
/// *IMPORTANT*: *DO NOT* run this query before promoted MIR body is constructed,
14571465
/// because this query partially depends on that query.
14581466
/// Otherwise, there is a risk of query cycles.

compiler/rustc_mir_transform/messages.ftl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,19 @@ mir_transform_tail_expr_drop_order = relative drop order changing in Rust 2024
2929
.temporaries = in Rust 2024, this temporary value will be dropped first
3030
.observers = in Rust 2024, this local variable or temporary value will be dropped second
3131
.note_dtors =
32-
dropping the temporary value runs this custom `Drop` impl, which will run first in Rust 2024
32+
dropping the temporary value runs this custom `Drop` impl, which we could not prove to be side-effect free
3333
.note_observer_dtors =
34-
dropping the local runs this custom `Drop` impl, which will run second in Rust 2024
34+
dropping the local runs this custom `Drop` impl, which we could not prove to be side-effect free
3535
.drop_location =
3636
now the temporary value is dropped here, before the local variables in the block or statement
37-
.note_epilogue = most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects
37+
.note_epilogue = most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages
3838
.label_local_epilogue = {$is_dropped_first_edition_2024 ->
3939
[true] up until Edition 2021 `{$name}` is dropped last but will be dropped earlier in Edition 2024
40-
*[false] `{$name}` will be dropped later since Edition 2024
40+
*[false] `{$name}` will be dropped later as of Edition 2024
4141
}
4242
4343
mir_transform_tail_expr_dtor = {$dtor_kind ->
44-
[dyn] `{$name}` may invoke a custom destructor because contains a trait object
44+
[dyn] `{$name}` may invoke a custom destructor because it contains a trait object
4545
*[concrete] `{$name}` invokes this custom destructor
4646
}
4747

compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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)]
3435
enum 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`.
146159
fn 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))]
204219
fn 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))]
241259
fn 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.
294315
fn 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`
312334
pub(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
547610
fn 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
559623
fn 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
602667
impl Subdiagnostic for LocalLabel<'_> {
603668
fn add_to_diag_with<
604669
G: rustc_errors::EmissionGuarantee,

tests/ui/drop/lint-tail-expr-drop-order.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ impl LoudDropper {
3737
fn should_lint() -> i32 {
3838
let x = LoudDropper;
3939
//~^ NOTE: `x` calls a custom destructor
40-
//~| NOTE: `x` will be dropped later since Edition 2024
40+
//~| NOTE: `x` will be dropped later as of Edition 2024
4141
// Should lint
4242
x.get() + LoudDropper.get()
4343
//~^ ERROR: relative drop order changing in Rust 2024
@@ -62,7 +62,7 @@ fn should_lint_in_nested_items() {
6262
fn should_lint_me() -> i32 {
6363
let x = LoudDropper;
6464
//~^ NOTE: `x` calls a custom destructor
65-
//~| NOTE: `x` will be dropped later since Edition 2024
65+
//~| NOTE: `x` will be dropped later as of Edition 2024
6666
// Should lint
6767
x.get() + LoudDropper.get()
6868
//~^ ERROR: relative drop order changing in Rust 2024
@@ -90,7 +90,7 @@ fn should_not_lint() -> i32 {
9090
fn should_lint_in_nested_block() -> i32 {
9191
let x = LoudDropper;
9292
//~^ NOTE: `x` calls a custom destructor
93-
//~| NOTE: `x` will be dropped later since Edition 2024
93+
//~| NOTE: `x` will be dropped later as of Edition 2024
9494
{ LoudDropper.get() }
9595
//~^ ERROR: relative drop order changing in Rust 2024
9696
//~| NOTE: this value will be stored in a temporary; let us call it `#1`
@@ -143,7 +143,7 @@ fn should_lint_into_async_body() -> i32 {
143143

144144
let future = f();
145145
//~^ NOTE: `future` calls a custom destructor
146-
//~| NOTE: `future` will be dropped later since Edition 2024
146+
//~| NOTE: `future` will be dropped later as of Edition 2024
147147
LoudDropper.get()
148148
//~^ ERROR: relative drop order changing in Rust 2024
149149
//~| WARN: this changes meaning in Rust 2024
@@ -160,7 +160,7 @@ fn should_lint_generics<T: Default>() -> &'static str {
160160
}
161161
let x = T::default();
162162
//~^ NOTE: `x` calls a custom destructor
163-
//~| NOTE: `x` will be dropped later since Edition 2024
163+
//~| NOTE: `x` will be dropped later as of Edition 2024
164164
extract(&T::default())
165165
//~^ ERROR: relative drop order changing in Rust 2024
166166
//~| WARN: this changes meaning in Rust 2024
@@ -174,7 +174,7 @@ fn should_lint_generics<T: Default>() -> &'static str {
174174
fn should_lint_adt() -> i32 {
175175
let x: Result<LoudDropper, ()> = Ok(LoudDropper);
176176
//~^ NOTE: `x` calls a custom destructor
177-
//~| NOTE: `x` will be dropped later since Edition 2024
177+
//~| NOTE: `x` will be dropped later as of Edition 2024
178178
LoudDropper.get()
179179
//~^ ERROR: relative drop order changing in Rust 2024
180180
//~| WARN: this changes meaning in Rust 2024
@@ -218,7 +218,7 @@ fn should_lint_with_dtor_span() -> i32 {
218218

219219
let x = LoudDropper2;
220220
//~^ NOTE: `x` calls a custom destructor
221-
//~| NOTE: `x` will be dropped later since Edition 2024
221+
//~| NOTE: `x` will be dropped later as of Edition 2024
222222
LoudDropper3.get()
223223
//~^ ERROR: relative drop order changing in Rust 2024
224224
//~| NOTE: this value will be stored in a temporary; let us call it `#1`
@@ -243,7 +243,7 @@ fn should_lint_with_transient_drops() {
243243
{
244244
let _x = LoudDropper;
245245
//~^ NOTE: `_x` calls a custom destructor
246-
//~| NOTE: `_x` will be dropped later since Edition 2024
246+
//~| NOTE: `_x` will be dropped later as of Edition 2024
247247
},
248248
));
249249
//~^ NOTE: now the temporary value is dropped here, before the local variables in the block or statement

0 commit comments

Comments
 (0)