Skip to content

Commit d1e3191

Browse files
committed
Optimize async drop glue for some old types
1 parent 48af6f8 commit d1e3191

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
@@ -176,6 +176,7 @@ language_item_table! {
176176
AsyncDropSlice, sym::async_drop_slice, async_drop_slice_fn, Target::Fn, GenericRequirement::Exact(1);
177177
AsyncDropChain, sym::async_drop_chain, async_drop_chain_fn, Target::Fn, GenericRequirement::Exact(2);
178178
AsyncDropNoop, sym::async_drop_noop, async_drop_noop_fn, Target::Fn, GenericRequirement::Exact(0);
179+
AsyncDropDeferredDropInPlace, sym::async_drop_deferred_drop_in_place, async_drop_deferred_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1);
179180
AsyncDropFuse, sym::async_drop_fuse, async_drop_fuse_fn, Target::Fn, GenericRequirement::Exact(1);
180181
AsyncDropDefer, sym::async_drop_defer, async_drop_defer_fn, Target::Fn, GenericRequirement::Exact(1);
181182
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::TyKind::*;
3232
use rustc_type_ir::{self as ir, BoundVar, CollectAndApply, DynKind};
@@ -2122,11 +2122,22 @@ impl<'tcx> Ty<'tcx> {
21222122
}
21232123

21242124
/// Returns the type of the async destructor of this type.
2125-
pub fn async_destructor_ty(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Ty<'tcx> {
2126-
if self.is_async_destructor_noop(tcx, param_env) || matches!(self.kind(), ty::Error(_)) {
2127-
return Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop)
2128-
.instantiate_identity();
2125+
pub fn async_destructor_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
2126+
match self.async_drop_glue_morphology(tcx) {
2127+
AsyncDropGlueMorphology::Noop => {
2128+
return Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop)
2129+
.instantiate_identity();
2130+
}
2131+
AsyncDropGlueMorphology::DeferredDropInPlace => {
2132+
let drop_in_place =
2133+
Ty::async_destructor_combinator(tcx, LangItem::AsyncDropDeferredDropInPlace)
2134+
.instantiate(tcx, &[self.into()]);
2135+
return Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse)
2136+
.instantiate(tcx, &[drop_in_place.into()]);
2137+
}
2138+
AsyncDropGlueMorphology::Custom => (),
21292139
}
2140+
21302141
match *self.kind() {
21312142
ty::Param(_) | ty::Alias(..) | ty::Infer(ty::TyVar(_)) => {
21322143
let assoc_items = tcx
@@ -2145,19 +2156,13 @@ impl<'tcx> Ty<'tcx> {
21452156
.adt_async_destructor_ty(
21462157
tcx,
21472158
adt_def.variants().iter().map(|v| v.fields.iter().map(|f| f.ty(tcx, args))),
2148-
param_env,
21492159
),
2150-
ty::Tuple(tys) => self.adt_async_destructor_ty(tcx, iter::once(tys), param_env),
2151-
ty::Closure(_, args) => self.adt_async_destructor_ty(
2152-
tcx,
2153-
iter::once(args.as_closure().upvar_tys()),
2154-
param_env,
2155-
),
2156-
ty::CoroutineClosure(_, args) => self.adt_async_destructor_ty(
2157-
tcx,
2158-
iter::once(args.as_coroutine_closure().upvar_tys()),
2159-
param_env,
2160-
),
2160+
ty::Tuple(tys) => self.adt_async_destructor_ty(tcx, iter::once(tys)),
2161+
ty::Closure(_, args) => {
2162+
self.adt_async_destructor_ty(tcx, iter::once(args.as_closure().upvar_tys()))
2163+
}
2164+
ty::CoroutineClosure(_, args) => self
2165+
.adt_async_destructor_ty(tcx, iter::once(args.as_coroutine_closure().upvar_tys())),
21612166

21622167
ty::Adt(adt_def, _) => {
21632168
assert!(adt_def.is_union());
@@ -2179,17 +2184,12 @@ impl<'tcx> Ty<'tcx> {
21792184
}
21802185
}
21812186

2182-
fn adt_async_destructor_ty<I>(
2183-
self,
2184-
tcx: TyCtxt<'tcx>,
2185-
variants: I,
2186-
param_env: ParamEnv<'tcx>,
2187-
) -> Ty<'tcx>
2187+
fn adt_async_destructor_ty<I>(self, tcx: TyCtxt<'tcx>, variants: I) -> Ty<'tcx>
21882188
where
21892189
I: Iterator + ExactSizeIterator,
21902190
I::Item: IntoIterator<Item = Ty<'tcx>>,
21912191
{
2192-
debug_assert!(!self.is_async_destructor_noop(tcx, param_env));
2192+
debug_assert_eq!(self.async_drop_glue_morphology(tcx), AsyncDropGlueMorphology::Custom);
21932193

21942194
let defer = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropDefer);
21952195
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 {
@@ -1341,27 +1372,16 @@ impl<'tcx> Ty<'tcx> {
13411372
}
13421373
}
13431374

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

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

1649-
// FIXME(zetanumbers): make specifying asyncness explicit
16501679
/// Returns a list of types such that the given type needs drop if and only if
16511680
/// *any* of the returned types need drop. Returns `Err(AlwaysRequiresDrop)` if
16521681
/// this type always needs drop.
1682+
//
1683+
// FIXME(zetanumbers): consider replacing this with only
1684+
// `needs_drop_components_with_async`
1685+
#[inline]
16531686
pub fn needs_drop_components<'tcx>(
16541687
tcx: TyCtxt<'tcx>,
16551688
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_middle::{bug, span_bug};
1818
use rustc_span::source_map::respan;
@@ -116,15 +116,25 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> {
116116
}
117117

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

123133
let surface_drop_kind = || {
124-
let param_env = tcx.param_env_reveal_all_normalized(def_id);
125-
if self_ty.has_surface_async_drop(tcx, param_env) {
134+
let adt_def = self_ty.ty_adt_def()?;
135+
if adt_def.async_destructor(tcx).is_some() {
126136
Some(SurfaceDropKind::Async)
127-
} else if self_ty.has_surface_drop(tcx, param_env) {
137+
} else if adt_def.destructor(tcx).is_some() {
128138
Some(SurfaceDropKind::Sync)
129139
} else {
130140
None
@@ -267,6 +277,13 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> {
267277
self.return_()
268278
}
269279

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

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

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
@@ -830,7 +830,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
830830
| ty::Str
831831
| ty::Slice(_)
832832
| ty::Tuple(_)
833-
| ty::Error(_) => self_ty.async_destructor_ty(ecx.tcx(), goal.param_env),
833+
| ty::Error(_) => self_ty.async_destructor_ty(ecx.tcx()),
834834

835835
// We do not call `Ty::async_destructor_ty` on alias, param, or placeholder
836836
// 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
@@ -1560,7 +1560,7 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
15601560
let destructor_def_id = tcx.associated_item_def_ids(trait_def_id)[0];
15611561
assert_eq!(destructor_def_id, item_def_id);
15621562

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

0 commit comments

Comments
 (0)