Skip to content

Commit acd25e5

Browse files
committed
Optimize async drop glue for some old types
1 parent 55a2c2d commit acd25e5

File tree

11 files changed

+161
-89
lines changed

11 files changed

+161
-89
lines changed

compiler/rustc_hir/src/lang_items.rs

+1
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ language_item_table! {
170170
AsyncDropSlice, sym::async_drop_slice, async_drop_slice_fn, Target::Fn, GenericRequirement::Exact(1);
171171
AsyncDropChain, sym::async_drop_chain, async_drop_chain_fn, Target::Fn, GenericRequirement::Exact(2);
172172
AsyncDropNoop, sym::async_drop_noop, async_drop_noop_fn, Target::Fn, GenericRequirement::Exact(0);
173+
AsyncDropDeferredDropInPlace, sym::async_drop_deferred_drop_in_place, async_drop_deferred_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1);
173174
AsyncDropFuse, sym::async_drop_fuse, async_drop_fuse_fn, Target::Fn, GenericRequirement::Exact(1);
174175
AsyncDropDefer, sym::async_drop_defer, async_drop_defer_fn, Target::Fn, GenericRequirement::Exact(1);
175176
AsyncDropEither, sym::async_drop_either, async_drop_either_fn, Target::Fn, GenericRequirement::Exact(3);

compiler/rustc_middle/src/ty/adt.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -578,8 +578,8 @@ impl<'tcx> AdtDef<'tcx> {
578578
tcx.adt_destructor(self.did())
579579
}
580580

581-
// FIXME(zetanumbers): consider supporting this method in same places where
582-
// `destructor` is referenced
581+
// FIXME: consider combining this method with `AdtDef::destructor` and removing
582+
// this version
583583
pub fn async_destructor(self, tcx: TyCtxt<'tcx>) -> Option<AsyncDestructor> {
584584
tcx.adt_async_destructor(self.did())
585585
}

compiler/rustc_middle/src/ty/sty.rs

+24-24
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use std::assert_matches::debug_assert_matches;
2626
use std::borrow::Cow;
2727
use std::iter;
2828
use std::ops::{ControlFlow, Deref, Range};
29-
use ty::util::IntTypeExt;
29+
use ty::util::{AsyncDropGlueMorphology, IntTypeExt};
3030

3131
use rustc_type_ir::BoundVar;
3232
use rustc_type_ir::CollectAndApply;
@@ -2318,11 +2318,22 @@ impl<'tcx> Ty<'tcx> {
23182318
}
23192319

23202320
/// Returns the type of the async destructor of this type.
2321-
pub fn async_destructor_ty(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Ty<'tcx> {
2322-
if self.is_async_destructor_noop(tcx, param_env) || matches!(self.kind(), ty::Error(_)) {
2323-
return Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop)
2324-
.instantiate_identity();
2321+
pub fn async_destructor_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
2322+
match self.async_drop_glue_morphology(tcx) {
2323+
AsyncDropGlueMorphology::Noop => {
2324+
return Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop)
2325+
.instantiate_identity();
2326+
}
2327+
AsyncDropGlueMorphology::DeferredDropInPlace => {
2328+
let drop_in_place =
2329+
Ty::async_destructor_combinator(tcx, LangItem::AsyncDropDeferredDropInPlace)
2330+
.instantiate(tcx, &[self.into()]);
2331+
return Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse)
2332+
.instantiate(tcx, &[drop_in_place.into()]);
2333+
}
2334+
AsyncDropGlueMorphology::Custom => (),
23252335
}
2336+
23262337
match *self.kind() {
23272338
ty::Param(_) | ty::Alias(..) | ty::Infer(ty::TyVar(_)) => {
23282339
let assoc_items = tcx
@@ -2341,19 +2352,13 @@ impl<'tcx> Ty<'tcx> {
23412352
.adt_async_destructor_ty(
23422353
tcx,
23432354
adt_def.variants().iter().map(|v| v.fields.iter().map(|f| f.ty(tcx, args))),
2344-
param_env,
23452355
),
2346-
ty::Tuple(tys) => self.adt_async_destructor_ty(tcx, iter::once(tys), param_env),
2347-
ty::Closure(_, args) => self.adt_async_destructor_ty(
2348-
tcx,
2349-
iter::once(args.as_closure().upvar_tys()),
2350-
param_env,
2351-
),
2352-
ty::CoroutineClosure(_, args) => self.adt_async_destructor_ty(
2353-
tcx,
2354-
iter::once(args.as_coroutine_closure().upvar_tys()),
2355-
param_env,
2356-
),
2356+
ty::Tuple(tys) => self.adt_async_destructor_ty(tcx, iter::once(tys)),
2357+
ty::Closure(_, args) => {
2358+
self.adt_async_destructor_ty(tcx, iter::once(args.as_closure().upvar_tys()))
2359+
}
2360+
ty::CoroutineClosure(_, args) => self
2361+
.adt_async_destructor_ty(tcx, iter::once(args.as_coroutine_closure().upvar_tys())),
23572362

23582363
ty::Adt(adt_def, _) => {
23592364
assert!(adt_def.is_union());
@@ -2375,17 +2380,12 @@ impl<'tcx> Ty<'tcx> {
23752380
}
23762381
}
23772382

2378-
fn adt_async_destructor_ty<I>(
2379-
self,
2380-
tcx: TyCtxt<'tcx>,
2381-
variants: I,
2382-
param_env: ParamEnv<'tcx>,
2383-
) -> Ty<'tcx>
2383+
fn adt_async_destructor_ty<I>(self, tcx: TyCtxt<'tcx>, variants: I) -> Ty<'tcx>
23842384
where
23852385
I: Iterator + ExactSizeIterator,
23862386
I::Item: IntoIterator<Item = Ty<'tcx>>,
23872387
{
2388-
debug_assert!(!self.is_async_destructor_noop(tcx, param_env));
2388+
debug_assert_eq!(self.async_drop_glue_morphology(tcx), AsyncDropGlueMorphology::Custom);
23892389

23902390
let defer = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropDefer);
23912391
let chain = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropChain);

compiler/rustc_middle/src/ty/util.rs

+86-53
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,25 @@ impl<'tcx> TyCtxt<'tcx> {
420420
Some(ty::AsyncDestructor { future, ctor })
421421
}
422422

423+
/// Returns async drop glue morphology for a definition. To get async drop
424+
/// glue morphology for a type see [`Ty::async_drop_glue_morphology`].
425+
//
426+
// FIXME: consider making this a query
427+
pub fn async_drop_glue_morphology(self, did: DefId) -> AsyncDropGlueMorphology {
428+
let ty: Ty<'tcx> = self.type_of(did).instantiate_identity();
429+
430+
// Async drop glue morphology is an internal detail, so reveal_all probably
431+
// should be fine
432+
let param_env = ty::ParamEnv::reveal_all();
433+
if ty.needs_async_drop(self, param_env) {
434+
AsyncDropGlueMorphology::Custom
435+
} else if ty.needs_drop(self, param_env) {
436+
AsyncDropGlueMorphology::DeferredDropInPlace
437+
} else {
438+
AsyncDropGlueMorphology::Noop
439+
}
440+
}
441+
423442
/// Returns the set of types that are required to be alive in
424443
/// order to run the destructor of `def` (see RFCs 769 and
425444
/// 1238).
@@ -1176,6 +1195,18 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for WeakAliasTypeExpander<'tcx> {
11761195
}
11771196
}
11781197

1198+
/// Indicates the form of `AsyncDestruct::Destructor`. Used to simplify async
1199+
/// drop glue for types not using async drop.
1200+
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
1201+
pub enum AsyncDropGlueMorphology {
1202+
/// Async destructor simply does nothing
1203+
Noop,
1204+
/// Async destructor simply runs `drop_in_place`
1205+
DeferredDropInPlace,
1206+
/// Async destructor has custom logic
1207+
Custom,
1208+
}
1209+
11791210
impl<'tcx> Ty<'tcx> {
11801211
/// Returns the `Size` for primitive types (bool, uint, int, char, float).
11811212
pub fn primitive_size(self, tcx: TyCtxt<'tcx>) -> Size {
@@ -1342,27 +1373,16 @@ impl<'tcx> Ty<'tcx> {
13421373
}
13431374
}
13441375

1345-
/// Checks whether values of this type `T` implement has noop async destructor.
1376+
/// Get morphology of the async drop glue, needed for types which do not
1377+
/// use async drop. To get async drop glue morphology for a definition see
1378+
/// [`TyCtxt::async_drop_glue_morphology`]. Used for `AsyncDestruct::Destructor`
1379+
/// type construction.
13461380
//
1347-
// FIXME: implement optimization to make ADTs, which do not need drop,
1348-
// to skip fields or to have noop async destructor, use `needs_(async_)drop`
1349-
pub fn is_async_destructor_noop(
1350-
self,
1351-
tcx: TyCtxt<'tcx>,
1352-
param_env: ty::ParamEnv<'tcx>,
1353-
) -> bool {
1354-
// TODO: check on the most generic version of your type
1355-
self.is_async_destructor_trivially_noop()
1356-
|| self.needs_async_drop(tcx, param_env)
1357-
|| self.needs_drop(tcx, param_env)
1358-
}
1359-
1360-
/// Fast path helper for testing if a type has noop async destructor.
1361-
///
1362-
/// Returning `true` means the type is known to have noop async destructor
1363-
/// implementation. Returning `true` means nothing -- could be
1364-
/// `Drop`, might not be.
1365-
fn is_async_destructor_trivially_noop(self) -> bool {
1381+
// FIXME: implement optimization to not instantiate a certain morphology of
1382+
// async drop glue too soon to allow per type optimizations, see array case
1383+
// for more info. Perhaps then remove this method and use `needs_(async_)drop`
1384+
// instead.
1385+
pub fn async_drop_glue_morphology(self, tcx: TyCtxt<'tcx>) -> AsyncDropGlueMorphology {
13661386
match self.kind() {
13671387
ty::Int(_)
13681388
| ty::Uint(_)
@@ -1374,37 +1394,43 @@ impl<'tcx> Ty<'tcx> {
13741394
| ty::Ref(..)
13751395
| ty::RawPtr(..)
13761396
| ty::FnDef(..)
1377-
| ty::FnPtr(_) => true,
1378-
ty::Tuple(tys) => tys.is_empty(),
1379-
ty::Adt(adt_def, _) => adt_def.is_manually_drop(),
1380-
ty::Bool => todo!(),
1381-
ty::Char => todo!(),
1382-
ty::Int(_) => todo!(),
1383-
ty::Uint(_) => todo!(),
1384-
ty::Float(_) => todo!(),
1385-
ty::Adt(_, _) => todo!(),
1386-
ty::Foreign(_) => todo!(),
1387-
ty::Str => todo!(),
1388-
ty::Array(_, _) => todo!(),
1389-
ty::Pat(_, _) => todo!(),
1390-
ty::Slice(_) => todo!(),
1391-
ty::RawPtr(_, _) => todo!(),
1392-
ty::Ref(_, _, _) => todo!(),
1393-
ty::FnDef(_, _) => todo!(),
1394-
ty::FnPtr(_) => todo!(),
1395-
ty::Dynamic(_, _, _) => todo!(),
1396-
ty::Closure(_, _) => todo!(),
1397-
ty::CoroutineClosure(_, _) => todo!(),
1398-
ty::Coroutine(_, _) => todo!(),
1399-
ty::CoroutineWitness(_, _) => todo!(),
1400-
ty::Never => todo!(),
1401-
ty::Tuple(_) => todo!(),
1402-
ty::Alias(_, _) => todo!(),
1403-
ty::Param(_) => todo!(),
1404-
ty::Bound(_, _) => todo!(),
1405-
ty::Placeholder(_) => todo!(),
1406-
ty::Infer(_) => todo!(),
1407-
ty::Error(_) => todo!(),
1397+
| ty::FnPtr(_)
1398+
| ty::Infer(ty::FreshIntTy(_))
1399+
| ty::Infer(ty::FreshFloatTy(_)) => AsyncDropGlueMorphology::Noop,
1400+
1401+
ty::Tuple(tys) if tys.is_empty() => AsyncDropGlueMorphology::Noop,
1402+
ty::Adt(adt_def, _) if adt_def.is_manually_drop() => AsyncDropGlueMorphology::Noop,
1403+
1404+
// Foreign types can never have destructors.
1405+
ty::Foreign(_) => AsyncDropGlueMorphology::Noop,
1406+
1407+
// FIXME: implement dynamic types async drops
1408+
ty::Error(_) | ty::Dynamic(..) => AsyncDropGlueMorphology::DeferredDropInPlace,
1409+
1410+
ty::Tuple(_) | ty::Array(_, _) | ty::Slice(_) => {
1411+
// Assume worst-case scenario, because we can instantiate async
1412+
// destructors in different orders:
1413+
//
1414+
// 1. Instantiate [T; N] with T = String and N = 0
1415+
// 2. Instantiate <[String; 0] as AsyncDestruct>::Destructor
1416+
//
1417+
// And viceversa, thus we cannot rely on String not using async
1418+
// drop or array having zero (0) elements
1419+
AsyncDropGlueMorphology::Custom
1420+
}
1421+
ty::Pat(ty, _) => ty.async_drop_glue_morphology(tcx),
1422+
1423+
ty::Adt(adt_def, _) => tcx.async_drop_glue_morphology(adt_def.did()),
1424+
1425+
ty::Closure(did, _)
1426+
| ty::CoroutineClosure(did, _)
1427+
| ty::Coroutine(did, _)
1428+
| ty::CoroutineWitness(did, _) => tcx.async_drop_glue_morphology(*did),
1429+
1430+
ty::Alias(..) | ty::Param(_) | ty::Bound(..) | ty::Placeholder(..) | ty::Infer(_) => {
1431+
// No specifics, but would usually mean forwarding async drop glue
1432+
AsyncDropGlueMorphology::Custom
1433+
}
14081434
}
14091435
}
14101436

@@ -1451,7 +1477,11 @@ impl<'tcx> Ty<'tcx> {
14511477
/// (Note that this implies that if `ty` has an async destructor attached,
14521478
/// then `needs_async_drop` will definitely return `true` for `ty`.)
14531479
///
1454-
/// Note that this method is used to check eligible types in unions.
1480+
/// When constructing `AsyncDestruct::Destructor` type, use
1481+
/// [`Ty::async_drop_glue_morphology`] instead.
1482+
//
1483+
// FIXME(zetanumbers): Note that this method is used to check eligible types
1484+
// in unions.
14551485
#[inline]
14561486
pub fn needs_async_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
14571487
// Avoid querying in simple cases.
@@ -1647,10 +1677,13 @@ impl<'tcx> ExplicitSelf<'tcx> {
16471677
}
16481678
}
16491679

1650-
// FIXME(zetanumbers): make specifying asyncness explicit
16511680
/// Returns a list of types such that the given type needs drop if and only if
16521681
/// *any* of the returned types need drop. Returns `Err(AlwaysRequiresDrop)` if
16531682
/// this type always needs drop.
1683+
//
1684+
// FIXME(zetanumbers): consider replacing this with only
1685+
// `needs_drop_components_with_async`
1686+
#[inline]
16541687
pub fn needs_drop_components<'tcx>(
16551688
tcx: TyCtxt<'tcx>,
16561689
ty: Ty<'tcx>,

compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs

+31-6
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_middle::mir::{
1212
Terminator, TerminatorKind, UnwindAction, UnwindTerminateReason, RETURN_PLACE,
1313
};
1414
use rustc_middle::ty::adjustment::PointerCoercion;
15-
use rustc_middle::ty::util::Discr;
15+
use rustc_middle::ty::util::{AsyncDropGlueMorphology, Discr};
1616
use rustc_middle::ty::{self, Ty, TyCtxt};
1717
use rustc_span::source_map::respan;
1818
use rustc_span::{Span, Symbol};
@@ -115,15 +115,25 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> {
115115
}
116116

117117
fn build(self) -> Body<'tcx> {
118-
let (tcx, def_id, Some(self_ty)) = (self.tcx, self.def_id, self.self_ty) else {
118+
let (tcx, Some(self_ty)) = (self.tcx, self.self_ty) else {
119119
return self.build_zst_output();
120120
};
121+
match self_ty.async_drop_glue_morphology(tcx) {
122+
AsyncDropGlueMorphology::Noop => span_bug!(
123+
self.span,
124+
"async drop glue shim generator encountered type with noop async drop glue morphology"
125+
),
126+
AsyncDropGlueMorphology::DeferredDropInPlace => {
127+
return self.build_deferred_drop_in_place();
128+
}
129+
AsyncDropGlueMorphology::Custom => (),
130+
}
121131

122132
let surface_drop_kind = || {
123-
let param_env = tcx.param_env_reveal_all_normalized(def_id);
124-
if self_ty.has_surface_async_drop(tcx, param_env) {
133+
let adt_def = self_ty.ty_adt_def()?;
134+
if adt_def.async_destructor(tcx).is_some() {
125135
Some(SurfaceDropKind::Async)
126-
} else if self_ty.has_surface_drop(tcx, param_env) {
136+
} else if adt_def.destructor(tcx).is_some() {
127137
Some(SurfaceDropKind::Sync)
128138
} else {
129139
None
@@ -266,6 +276,13 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> {
266276
self.return_()
267277
}
268278

279+
fn build_deferred_drop_in_place(mut self) -> Body<'tcx> {
280+
self.put_self();
281+
let deferred = self.combine_deferred_drop_in_place();
282+
self.combine_fuse(deferred);
283+
self.return_()
284+
}
285+
269286
fn build_fused_async_surface(mut self) -> Body<'tcx> {
270287
self.put_self();
271288
let surface = self.combine_async_surface();
@@ -440,6 +457,14 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> {
440457
)
441458
}
442459

460+
fn combine_deferred_drop_in_place(&mut self) -> Ty<'tcx> {
461+
self.apply_combinator(
462+
1,
463+
LangItem::AsyncDropDeferredDropInPlace,
464+
&[self.self_ty.unwrap().into()],
465+
)
466+
}
467+
443468
fn combine_fuse(&mut self, inner_future_ty: Ty<'tcx>) -> Ty<'tcx> {
444469
self.apply_combinator(1, LangItem::AsyncDropFuse, &[inner_future_ty.into()])
445470
}
@@ -480,7 +505,7 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> {
480505
if let Some(ty) = self.self_ty {
481506
debug_assert_eq!(
482507
output.ty(&self.locals, self.tcx),
483-
ty.async_destructor_ty(self.tcx, self.param_env),
508+
ty.async_destructor_ty(self.tcx),
484509
"output async destructor types did not match for type: {ty:?}",
485510
);
486511
}

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,7 @@ symbols! {
430430
async_drop,
431431
async_drop_chain,
432432
async_drop_defer,
433+
async_drop_deferred_drop_in_place,
433434
async_drop_either,
434435
async_drop_fuse,
435436
async_drop_in_place,

compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -849,7 +849,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
849849
| ty::Str
850850
| ty::Slice(_)
851851
| ty::Tuple(_)
852-
| ty::Error(_) => self_ty.async_destructor_ty(ecx.tcx(), goal.param_env),
852+
| ty::Error(_) => self_ty.async_destructor_ty(ecx.tcx()),
853853

854854
// We do not call `Ty::async_destructor_ty` on alias, param, or placeholder
855855
// types, which return `<self_ty as AsyncDestruct>::AsyncDestructor`

compiler/rustc_trait_selection/src/traits/project.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1537,7 +1537,7 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
15371537
let destructor_def_id = tcx.associated_item_def_ids(trait_def_id)[0];
15381538
assert_eq!(destructor_def_id, item_def_id);
15391539

1540-
(self_ty.async_destructor_ty(tcx, obligation.param_env).into(), Vec::new())
1540+
(self_ty.async_destructor_ty(tcx).into(), Vec::new())
15411541
} else if lang_items.pointee_trait() == Some(trait_def_id) {
15421542
let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None);
15431543
assert_eq!(metadata_def_id, item_def_id);

0 commit comments

Comments
 (0)