Skip to content

Commit 7cdd95e

Browse files
committed
Optimize async drop glue for some old types
1 parent a47173c commit 7cdd95e

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
@@ -579,8 +579,8 @@ impl<'tcx> AdtDef<'tcx> {
579579
tcx.adt_destructor(self.did())
580580
}
581581

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

compiler/rustc_middle/src/ty/sty.rs

+24-24
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use std::assert_matches::debug_assert_matches;
2525
use std::borrow::Cow;
2626
use std::iter;
2727
use std::ops::{ControlFlow, Range};
28-
use ty::util::IntTypeExt;
28+
use ty::util::{AsyncDropGlueMorphology, IntTypeExt};
2929

3030
use rustc_type_ir::TyKind::*;
3131
use rustc_type_ir::{self as ir, BoundVar, CollectAndApply, DynKind};
@@ -1951,11 +1951,22 @@ impl<'tcx> Ty<'tcx> {
19511951
}
19521952

19531953
/// Returns the type of the async destructor of this type.
1954-
pub fn async_destructor_ty(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Ty<'tcx> {
1955-
if self.is_async_destructor_noop(tcx, param_env) || matches!(self.kind(), ty::Error(_)) {
1956-
return Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop)
1957-
.instantiate_identity();
1954+
pub fn async_destructor_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
1955+
match self.async_drop_glue_morphology(tcx) {
1956+
AsyncDropGlueMorphology::Noop => {
1957+
return Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop)
1958+
.instantiate_identity();
1959+
}
1960+
AsyncDropGlueMorphology::DeferredDropInPlace => {
1961+
let drop_in_place =
1962+
Ty::async_destructor_combinator(tcx, LangItem::AsyncDropDeferredDropInPlace)
1963+
.instantiate(tcx, &[self.into()]);
1964+
return Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse)
1965+
.instantiate(tcx, &[drop_in_place.into()]);
1966+
}
1967+
AsyncDropGlueMorphology::Custom => (),
19581968
}
1969+
19591970
match *self.kind() {
19601971
ty::Param(_) | ty::Alias(..) | ty::Infer(ty::TyVar(_)) => {
19611972
let assoc_items = tcx
@@ -1974,19 +1985,13 @@ impl<'tcx> Ty<'tcx> {
19741985
.adt_async_destructor_ty(
19751986
tcx,
19761987
adt_def.variants().iter().map(|v| v.fields.iter().map(|f| f.ty(tcx, args))),
1977-
param_env,
19781988
),
1979-
ty::Tuple(tys) => self.adt_async_destructor_ty(tcx, iter::once(tys), param_env),
1980-
ty::Closure(_, args) => self.adt_async_destructor_ty(
1981-
tcx,
1982-
iter::once(args.as_closure().upvar_tys()),
1983-
param_env,
1984-
),
1985-
ty::CoroutineClosure(_, args) => self.adt_async_destructor_ty(
1986-
tcx,
1987-
iter::once(args.as_coroutine_closure().upvar_tys()),
1988-
param_env,
1989-
),
1989+
ty::Tuple(tys) => self.adt_async_destructor_ty(tcx, iter::once(tys)),
1990+
ty::Closure(_, args) => {
1991+
self.adt_async_destructor_ty(tcx, iter::once(args.as_closure().upvar_tys()))
1992+
}
1993+
ty::CoroutineClosure(_, args) => self
1994+
.adt_async_destructor_ty(tcx, iter::once(args.as_coroutine_closure().upvar_tys())),
19901995

19911996
ty::Adt(adt_def, _) => {
19921997
assert!(adt_def.is_union());
@@ -2008,17 +2013,12 @@ impl<'tcx> Ty<'tcx> {
20082013
}
20092014
}
20102015

2011-
fn adt_async_destructor_ty<I>(
2012-
self,
2013-
tcx: TyCtxt<'tcx>,
2014-
variants: I,
2015-
param_env: ParamEnv<'tcx>,
2016-
) -> Ty<'tcx>
2016+
fn adt_async_destructor_ty<I>(self, tcx: TyCtxt<'tcx>, variants: I) -> Ty<'tcx>
20172017
where
20182018
I: Iterator + ExactSizeIterator,
20192019
I::Item: IntoIterator<Item = Ty<'tcx>>,
20202020
{
2021-
debug_assert!(!self.is_async_destructor_noop(tcx, param_env));
2021+
debug_assert_eq!(self.async_drop_glue_morphology(tcx), AsyncDropGlueMorphology::Custom);
20222022

20232023
let defer = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropDefer);
20242024
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
@@ -421,6 +421,25 @@ impl<'tcx> TyCtxt<'tcx> {
421421
Some(ty::AsyncDestructor { future, ctor })
422422
}
423423

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

1199+
/// Indicates the form of `AsyncDestruct::Destructor`. Used to simplify async
1200+
/// drop glue for types not using async drop.
1201+
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
1202+
pub enum AsyncDropGlueMorphology {
1203+
/// Async destructor simply does nothing
1204+
Noop,
1205+
/// Async destructor simply runs `drop_in_place`
1206+
DeferredDropInPlace,
1207+
/// Async destructor has custom logic
1208+
Custom,
1209+
}
1210+
11801211
impl<'tcx> Ty<'tcx> {
11811212
/// Returns the `Size` for primitive types (bool, uint, int, char, float).
11821213
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_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
@@ -839,7 +839,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
839839
| ty::Str
840840
| ty::Slice(_)
841841
| ty::Tuple(_)
842-
| ty::Error(_) => self_ty.async_destructor_ty(ecx.interner(), goal.param_env),
842+
| ty::Error(_) => self_ty.async_destructor_ty(ecx.interner()),
843843

844844
// We do not call `Ty::async_destructor_ty` on alias, param, or placeholder
845845
// 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)