Skip to content

Commit 1b7f4e5

Browse files
Rollup merge of rust-lang#156067 - P8L1:async-drop-box-fix, r=oli-obk
Fix async drop glue for Box<T> Fixes rust-lang#143658. This fixes async drop behavior for boxed values so that async drop glue reaches the boxed value’s async destructor in async drop context. The change updates async-drop needs-drop analysis so `Box<T>` is handled specially for async drop by considering the boxed pointee and allocator when deciding whether async drop glue is needed. This PR intentionally does not change the broader AsyncDrop design. It only fixes behavior under the existing `#![feature(async_drop)]` implementation. Reviewer notes: - `async-drop-box-allocator.rs` already covers async-dropping the allocator of a `Box`; this PR adds distinct coverage for the boxed value itself. - Boxed dyn pointees are intentionally not pulled into this fix, preserving existing dynamic async-drop limitations. - The change is isolated to async drop analysis; sync `needs_drop` behavior is unchanged. @rustbot label F-async_drop
2 parents ec2d516 + 2bc5e90 commit 1b7f4e5

3 files changed

Lines changed: 87 additions & 27 deletions

File tree

compiler/rustc_ty_utils/src/needs_drop.rs

Lines changed: 70 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,16 @@ fn needs_drop_raw<'tcx>(
2323
// needs drop.
2424
let adt_has_dtor =
2525
|adt_def: ty::AdtDef<'tcx>| adt_def.destructor(tcx).map(|_| DtorType::Significant);
26-
let res = drop_tys_helper(tcx, query.value, query.typing_env, adt_has_dtor, false, false)
27-
.filter(filter_array_elements(tcx, query.typing_env))
28-
.next()
29-
.is_some();
26+
let res = drop_tys_helper(
27+
tcx,
28+
query.value,
29+
query.typing_env,
30+
adt_has_dtor,
31+
DropTysOptions::default(),
32+
)
33+
.filter(filter_array_elements(tcx, query.typing_env))
34+
.next()
35+
.is_some();
3036

3137
debug!("needs_drop_raw({:?}) = {:?}", query, res);
3238
res
@@ -41,10 +47,16 @@ fn needs_async_drop_raw<'tcx>(
4147
// it needs async drop.
4248
let adt_has_async_dtor =
4349
|adt_def: ty::AdtDef<'tcx>| adt_def.async_destructor(tcx).map(|_| DtorType::Significant);
44-
let res = drop_tys_helper(tcx, query.value, query.typing_env, adt_has_async_dtor, false, false)
45-
.filter(filter_array_elements_async(tcx, query.typing_env))
46-
.next()
47-
.is_some();
50+
let res = drop_tys_helper(
51+
tcx,
52+
query.value,
53+
query.typing_env,
54+
adt_has_async_dtor,
55+
DropTysOptions::default().recurse_into_box_for_async_drop(),
56+
)
57+
.filter(filter_array_elements_async(tcx, query.typing_env))
58+
.next()
59+
.is_some();
4860

4961
debug!("needs_async_drop_raw({:?}) = {:?}", query, res);
5062
res
@@ -88,8 +100,7 @@ fn has_significant_drop_raw<'tcx>(
88100
query.value,
89101
query.typing_env,
90102
adt_consider_insignificant_dtor(tcx),
91-
true,
92-
false,
103+
DropTysOptions::default().only_significant(),
93104
)
94105
.filter(filter_array_elements(tcx, query.typing_env))
95106
.next()
@@ -320,6 +331,30 @@ enum DtorType {
320331
Significant,
321332
}
322333

334+
#[derive(Copy, Clone, Default)]
335+
struct DropTysOptions {
336+
only_significant: bool,
337+
exhaustive: bool,
338+
async_drop_recurses_into_box: bool,
339+
}
340+
341+
impl DropTysOptions {
342+
fn only_significant(mut self) -> Self {
343+
self.only_significant = true;
344+
self
345+
}
346+
347+
fn exhaustive(mut self) -> Self {
348+
self.exhaustive = true;
349+
self
350+
}
351+
352+
fn recurse_into_box_for_async_drop(mut self) -> Self {
353+
self.async_drop_recurses_into_box = true;
354+
self
355+
}
356+
}
357+
323358
// This is a helper function for `adt_drop_tys` and `adt_significant_drop_tys`.
324359
// Depending on the implantation of `adt_has_dtor`, it is used to check if the
325360
// ADT has a destructor or if the ADT only has a significant destructor. For
@@ -329,8 +364,7 @@ fn drop_tys_helper<'tcx>(
329364
ty: Ty<'tcx>,
330365
typing_env: ty::TypingEnv<'tcx>,
331366
adt_has_dtor: impl Fn(ty::AdtDef<'tcx>) -> Option<DtorType>,
332-
only_significant: bool,
333-
exhaustive: bool,
367+
options: DropTysOptions,
334368
) -> impl Iterator<Item = NeedsDropResult<Ty<'tcx>>> {
335369
fn with_query_cache<'tcx>(
336370
tcx: TyCtxt<'tcx>,
@@ -353,6 +387,24 @@ fn drop_tys_helper<'tcx>(
353387
if adt_def.is_manually_drop() {
354388
debug!("drop_tys_helper: `{:?}` is manually drop", adt_def);
355389
Ok(Vec::new())
390+
} else if options.async_drop_recurses_into_box && adt_def.is_box() {
391+
let box_components = match args.as_slice() {
392+
[boxed_ty, allocator_ty] => {
393+
let boxed_ty = boxed_ty.expect_ty();
394+
let allocator_ty = allocator_ty.expect_ty();
395+
match boxed_ty.kind() {
396+
// FIXME(async_drop): boxed dyn pointees are deliberately skipped here
397+
// because async drop glue does not yet dispatch through dyn metadata.
398+
// Once that is supported, this should include the boxed pointee too.
399+
ty::Dynamic(..) | ty::Error(_) => vec![allocator_ty],
400+
_ => vec![boxed_ty, allocator_ty],
401+
}
402+
}
403+
_ => {
404+
bug!("drop_tys_helper: `Box` has unexpected generic args: {args:?}");
405+
}
406+
};
407+
Ok(box_components)
356408
} else if let Some(dtor_info) = adt_has_dtor(adt_def) {
357409
match dtor_info {
358410
DtorType::Significant => {
@@ -380,7 +432,7 @@ fn drop_tys_helper<'tcx>(
380432
);
381433
r
382434
});
383-
if only_significant {
435+
if options.only_significant {
384436
// We can't recurse through the query system here because we might induce a cycle
385437
Ok(field_tys.collect())
386438
} else {
@@ -394,7 +446,7 @@ fn drop_tys_helper<'tcx>(
394446
.map(|v| v.into_iter())
395447
};
396448

397-
NeedsDropTypes::new(tcx, typing_env, ty, exhaustive, adt_components)
449+
NeedsDropTypes::new(tcx, typing_env, ty, options.exhaustive, adt_components)
398450
}
399451

400452
fn adt_consider_insignificant_dtor<'tcx>(
@@ -433,8 +485,7 @@ fn adt_drop_tys<'tcx>(
433485
tcx.type_of(def_id).instantiate_identity().skip_norm_wip(),
434486
ty::TypingEnv::non_body_analysis(tcx, def_id),
435487
adt_has_dtor,
436-
false,
437-
false,
488+
DropTysOptions::default(),
438489
)
439490
.collect::<Result<Vec<_>, _>>()
440491
.map(|components| tcx.mk_type_list(&components))
@@ -453,8 +504,7 @@ fn adt_async_drop_tys<'tcx>(
453504
tcx.type_of(def_id).instantiate_identity().skip_norm_wip(),
454505
ty::TypingEnv::non_body_analysis(tcx, def_id),
455506
adt_has_dtor,
456-
false,
457-
false,
507+
DropTysOptions::default().recurse_into_box_for_async_drop(),
458508
)
459509
.collect::<Result<Vec<_>, _>>()
460510
.map(|components| tcx.mk_type_list(&components))
@@ -472,8 +522,7 @@ fn adt_significant_drop_tys(
472522
tcx.type_of(def_id).instantiate_identity().skip_norm_wip(), // identical to `tcx.make_adt(def, identity_args)`
473523
ty::TypingEnv::non_body_analysis(tcx, def_id),
474524
adt_consider_insignificant_dtor(tcx),
475-
true,
476-
false,
525+
DropTysOptions::default().only_significant(),
477526
)
478527
.collect::<Result<Vec<_>, _>>()
479528
.map(|components| tcx.mk_type_list(&components))
@@ -490,8 +539,7 @@ fn list_significant_drop_tys<'tcx>(
490539
key.value,
491540
key.typing_env,
492541
adt_consider_insignificant_dtor(tcx),
493-
true,
494-
true,
542+
DropTysOptions::default().only_significant().exhaustive(),
495543
)
496544
.filter_map(|res| res.ok())
497545
.collect::<Vec<_>>(),

tests/ui/async-await/async-drop/async-drop-box.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
//@ run-pass
22
//@ check-run-results
33
// struct `Foo` has both sync and async drop.
4-
// `Foo` is always inside `Box`
4+
// `Foo` is always inside `Box`.
55
// Sync version is called in sync context, async version is called in async function.
66

7-
//@ known-bug: #143658
8-
// async version is never actually called
9-
107
#![feature(async_drop)]
118
#![allow(incomplete_features)]
129

@@ -30,6 +27,10 @@ struct Foo {
3027
my_resource_handle: usize,
3128
}
3229

30+
trait DynFoo {}
31+
32+
impl DynFoo for Foo {}
33+
3334
impl Foo {
3435
fn new(my_resource_handle: usize) -> Self {
3536
let out = Foo {
@@ -58,13 +59,21 @@ fn main() {
5859
}
5960
println!("Middle");
6061
block_on(bar(10));
62+
println!("Dyn");
63+
block_on(bar_dyn(20));
6164
println!("Done")
6265
}
6366

6467
async fn bar(ident_base: usize) {
6568
let _first = Box::new(Foo::new(ident_base));
6669
}
6770

71+
async fn bar_dyn(ident_base: usize) {
72+
// FIXME(async_drop): boxed dyn pointees should eventually use async drop
73+
// glue in async contexts. For now this documents the sync-drop fallback.
74+
let _first: Box<dyn DynFoo> = Box::new(Foo::new(ident_base));
75+
}
76+
6877
fn block_on<F>(fut_unpin: F) -> F::Output
6978
where
7079
F: Future,

tests/ui/async-await/async-drop/async-drop-box.run.stdout

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,8 @@ Foo::new() : 7
22
Foo::drop() : 7
33
Middle
44
Foo::new() : 10
5-
Foo::drop() : 10
5+
Foo::async drop() : 10
6+
Dyn
7+
Foo::new() : 20
8+
Foo::drop() : 20
69
Done

0 commit comments

Comments
 (0)