1
Fork 0

Optimize async drop glue for some old types

This commit is contained in:
Daria Sukhonina 2024-05-03 15:47:23 +03:00
parent a47173c4f7
commit 7cdd95e1a6
11 changed files with 161 additions and 89 deletions

View file

@ -176,6 +176,7 @@ language_item_table! {
AsyncDropSlice, sym::async_drop_slice, async_drop_slice_fn, Target::Fn, GenericRequirement::Exact(1); AsyncDropSlice, sym::async_drop_slice, async_drop_slice_fn, Target::Fn, GenericRequirement::Exact(1);
AsyncDropChain, sym::async_drop_chain, async_drop_chain_fn, Target::Fn, GenericRequirement::Exact(2); AsyncDropChain, sym::async_drop_chain, async_drop_chain_fn, Target::Fn, GenericRequirement::Exact(2);
AsyncDropNoop, sym::async_drop_noop, async_drop_noop_fn, Target::Fn, GenericRequirement::Exact(0); AsyncDropNoop, sym::async_drop_noop, async_drop_noop_fn, Target::Fn, GenericRequirement::Exact(0);
AsyncDropDeferredDropInPlace, sym::async_drop_deferred_drop_in_place, async_drop_deferred_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1);
AsyncDropFuse, sym::async_drop_fuse, async_drop_fuse_fn, Target::Fn, GenericRequirement::Exact(1); AsyncDropFuse, sym::async_drop_fuse, async_drop_fuse_fn, Target::Fn, GenericRequirement::Exact(1);
AsyncDropDefer, sym::async_drop_defer, async_drop_defer_fn, Target::Fn, GenericRequirement::Exact(1); AsyncDropDefer, sym::async_drop_defer, async_drop_defer_fn, Target::Fn, GenericRequirement::Exact(1);
AsyncDropEither, sym::async_drop_either, async_drop_either_fn, Target::Fn, GenericRequirement::Exact(3); AsyncDropEither, sym::async_drop_either, async_drop_either_fn, Target::Fn, GenericRequirement::Exact(3);

View file

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

View file

@ -25,7 +25,7 @@ use std::assert_matches::debug_assert_matches;
use std::borrow::Cow; use std::borrow::Cow;
use std::iter; use std::iter;
use std::ops::{ControlFlow, Range}; use std::ops::{ControlFlow, Range};
use ty::util::IntTypeExt; use ty::util::{AsyncDropGlueMorphology, IntTypeExt};
use rustc_type_ir::TyKind::*; use rustc_type_ir::TyKind::*;
use rustc_type_ir::{self as ir, BoundVar, CollectAndApply, DynKind}; use rustc_type_ir::{self as ir, BoundVar, CollectAndApply, DynKind};
@ -1951,11 +1951,22 @@ impl<'tcx> Ty<'tcx> {
} }
/// Returns the type of the async destructor of this type. /// Returns the type of the async destructor of this type.
pub fn async_destructor_ty(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Ty<'tcx> { pub fn async_destructor_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
if self.is_async_destructor_noop(tcx, param_env) || matches!(self.kind(), ty::Error(_)) { match self.async_drop_glue_morphology(tcx) {
return Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop) AsyncDropGlueMorphology::Noop => {
.instantiate_identity(); return Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop)
.instantiate_identity();
}
AsyncDropGlueMorphology::DeferredDropInPlace => {
let drop_in_place =
Ty::async_destructor_combinator(tcx, LangItem::AsyncDropDeferredDropInPlace)
.instantiate(tcx, &[self.into()]);
return Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse)
.instantiate(tcx, &[drop_in_place.into()]);
}
AsyncDropGlueMorphology::Custom => (),
} }
match *self.kind() { match *self.kind() {
ty::Param(_) | ty::Alias(..) | ty::Infer(ty::TyVar(_)) => { ty::Param(_) | ty::Alias(..) | ty::Infer(ty::TyVar(_)) => {
let assoc_items = tcx let assoc_items = tcx
@ -1974,19 +1985,13 @@ impl<'tcx> Ty<'tcx> {
.adt_async_destructor_ty( .adt_async_destructor_ty(
tcx, tcx,
adt_def.variants().iter().map(|v| v.fields.iter().map(|f| f.ty(tcx, args))), adt_def.variants().iter().map(|v| v.fields.iter().map(|f| f.ty(tcx, args))),
param_env,
), ),
ty::Tuple(tys) => self.adt_async_destructor_ty(tcx, iter::once(tys), param_env), ty::Tuple(tys) => self.adt_async_destructor_ty(tcx, iter::once(tys)),
ty::Closure(_, args) => self.adt_async_destructor_ty( ty::Closure(_, args) => {
tcx, self.adt_async_destructor_ty(tcx, iter::once(args.as_closure().upvar_tys()))
iter::once(args.as_closure().upvar_tys()), }
param_env, ty::CoroutineClosure(_, args) => self
), .adt_async_destructor_ty(tcx, iter::once(args.as_coroutine_closure().upvar_tys())),
ty::CoroutineClosure(_, args) => self.adt_async_destructor_ty(
tcx,
iter::once(args.as_coroutine_closure().upvar_tys()),
param_env,
),
ty::Adt(adt_def, _) => { ty::Adt(adt_def, _) => {
assert!(adt_def.is_union()); assert!(adt_def.is_union());
@ -2008,17 +2013,12 @@ impl<'tcx> Ty<'tcx> {
} }
} }
fn adt_async_destructor_ty<I>( fn adt_async_destructor_ty<I>(self, tcx: TyCtxt<'tcx>, variants: I) -> Ty<'tcx>
self,
tcx: TyCtxt<'tcx>,
variants: I,
param_env: ParamEnv<'tcx>,
) -> Ty<'tcx>
where where
I: Iterator + ExactSizeIterator, I: Iterator + ExactSizeIterator,
I::Item: IntoIterator<Item = Ty<'tcx>>, I::Item: IntoIterator<Item = Ty<'tcx>>,
{ {
debug_assert!(!self.is_async_destructor_noop(tcx, param_env)); debug_assert_eq!(self.async_drop_glue_morphology(tcx), AsyncDropGlueMorphology::Custom);
let defer = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropDefer); let defer = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropDefer);
let chain = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropChain); let chain = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropChain);

View file

@ -421,6 +421,25 @@ impl<'tcx> TyCtxt<'tcx> {
Some(ty::AsyncDestructor { future, ctor }) Some(ty::AsyncDestructor { future, ctor })
} }
/// Returns async drop glue morphology for a definition. To get async drop
/// glue morphology for a type see [`Ty::async_drop_glue_morphology`].
//
// FIXME: consider making this a query
pub fn async_drop_glue_morphology(self, did: DefId) -> AsyncDropGlueMorphology {
let ty: Ty<'tcx> = self.type_of(did).instantiate_identity();
// Async drop glue morphology is an internal detail, so reveal_all probably
// should be fine
let param_env = ty::ParamEnv::reveal_all();
if ty.needs_async_drop(self, param_env) {
AsyncDropGlueMorphology::Custom
} else if ty.needs_drop(self, param_env) {
AsyncDropGlueMorphology::DeferredDropInPlace
} else {
AsyncDropGlueMorphology::Noop
}
}
/// Returns the set of types that are required to be alive in /// Returns the set of types that are required to be alive in
/// order to run the destructor of `def` (see RFCs 769 and /// order to run the destructor of `def` (see RFCs 769 and
/// 1238). /// 1238).
@ -1177,6 +1196,18 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for WeakAliasTypeExpander<'tcx> {
} }
} }
/// Indicates the form of `AsyncDestruct::Destructor`. Used to simplify async
/// drop glue for types not using async drop.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum AsyncDropGlueMorphology {
/// Async destructor simply does nothing
Noop,
/// Async destructor simply runs `drop_in_place`
DeferredDropInPlace,
/// Async destructor has custom logic
Custom,
}
impl<'tcx> Ty<'tcx> { impl<'tcx> Ty<'tcx> {
/// Returns the `Size` for primitive types (bool, uint, int, char, float). /// Returns the `Size` for primitive types (bool, uint, int, char, float).
pub fn primitive_size(self, tcx: TyCtxt<'tcx>) -> Size { pub fn primitive_size(self, tcx: TyCtxt<'tcx>) -> Size {
@ -1342,27 +1373,16 @@ impl<'tcx> Ty<'tcx> {
} }
} }
/// Checks whether values of this type `T` implement has noop async destructor. /// Get morphology of the async drop glue, needed for types which do not
/// use async drop. To get async drop glue morphology for a definition see
/// [`TyCtxt::async_drop_glue_morphology`]. Used for `AsyncDestruct::Destructor`
/// type construction.
// //
// FIXME: implement optimization to make ADTs, which do not need drop, // FIXME: implement optimization to not instantiate a certain morphology of
// to skip fields or to have noop async destructor, use `needs_(async_)drop` // async drop glue too soon to allow per type optimizations, see array case
pub fn is_async_destructor_noop( // for more info. Perhaps then remove this method and use `needs_(async_)drop`
self, // instead.
tcx: TyCtxt<'tcx>, pub fn async_drop_glue_morphology(self, tcx: TyCtxt<'tcx>) -> AsyncDropGlueMorphology {
param_env: ty::ParamEnv<'tcx>,
) -> bool {
// TODO: check on the most generic version of your type
self.is_async_destructor_trivially_noop()
|| self.needs_async_drop(tcx, param_env)
|| self.needs_drop(tcx, param_env)
}
/// Fast path helper for testing if a type has noop async destructor.
///
/// Returning `true` means the type is known to have noop async destructor
/// implementation. Returning `true` means nothing -- could be
/// `Drop`, might not be.
fn is_async_destructor_trivially_noop(self) -> bool {
match self.kind() { match self.kind() {
ty::Int(_) ty::Int(_)
| ty::Uint(_) | ty::Uint(_)
@ -1374,37 +1394,43 @@ impl<'tcx> Ty<'tcx> {
| ty::Ref(..) | ty::Ref(..)
| ty::RawPtr(..) | ty::RawPtr(..)
| ty::FnDef(..) | ty::FnDef(..)
| ty::FnPtr(_) => true, | ty::FnPtr(_)
ty::Tuple(tys) => tys.is_empty(), | ty::Infer(ty::FreshIntTy(_))
ty::Adt(adt_def, _) => adt_def.is_manually_drop(), | ty::Infer(ty::FreshFloatTy(_)) => AsyncDropGlueMorphology::Noop,
ty::Bool => todo!(),
ty::Char => todo!(), ty::Tuple(tys) if tys.is_empty() => AsyncDropGlueMorphology::Noop,
ty::Int(_) => todo!(), ty::Adt(adt_def, _) if adt_def.is_manually_drop() => AsyncDropGlueMorphology::Noop,
ty::Uint(_) => todo!(),
ty::Float(_) => todo!(), // Foreign types can never have destructors.
ty::Adt(_, _) => todo!(), ty::Foreign(_) => AsyncDropGlueMorphology::Noop,
ty::Foreign(_) => todo!(),
ty::Str => todo!(), // FIXME: implement dynamic types async drops
ty::Array(_, _) => todo!(), ty::Error(_) | ty::Dynamic(..) => AsyncDropGlueMorphology::DeferredDropInPlace,
ty::Pat(_, _) => todo!(),
ty::Slice(_) => todo!(), ty::Tuple(_) | ty::Array(_, _) | ty::Slice(_) => {
ty::RawPtr(_, _) => todo!(), // Assume worst-case scenario, because we can instantiate async
ty::Ref(_, _, _) => todo!(), // destructors in different orders:
ty::FnDef(_, _) => todo!(), //
ty::FnPtr(_) => todo!(), // 1. Instantiate [T; N] with T = String and N = 0
ty::Dynamic(_, _, _) => todo!(), // 2. Instantiate <[String; 0] as AsyncDestruct>::Destructor
ty::Closure(_, _) => todo!(), //
ty::CoroutineClosure(_, _) => todo!(), // And viceversa, thus we cannot rely on String not using async
ty::Coroutine(_, _) => todo!(), // drop or array having zero (0) elements
ty::CoroutineWitness(_, _) => todo!(), AsyncDropGlueMorphology::Custom
ty::Never => todo!(), }
ty::Tuple(_) => todo!(), ty::Pat(ty, _) => ty.async_drop_glue_morphology(tcx),
ty::Alias(_, _) => todo!(),
ty::Param(_) => todo!(), ty::Adt(adt_def, _) => tcx.async_drop_glue_morphology(adt_def.did()),
ty::Bound(_, _) => todo!(),
ty::Placeholder(_) => todo!(), ty::Closure(did, _)
ty::Infer(_) => todo!(), | ty::CoroutineClosure(did, _)
ty::Error(_) => todo!(), | ty::Coroutine(did, _)
| ty::CoroutineWitness(did, _) => tcx.async_drop_glue_morphology(*did),
ty::Alias(..) | ty::Param(_) | ty::Bound(..) | ty::Placeholder(..) | ty::Infer(_) => {
// No specifics, but would usually mean forwarding async drop glue
AsyncDropGlueMorphology::Custom
}
} }
} }
@ -1451,7 +1477,11 @@ impl<'tcx> Ty<'tcx> {
/// (Note that this implies that if `ty` has an async destructor attached, /// (Note that this implies that if `ty` has an async destructor attached,
/// then `needs_async_drop` will definitely return `true` for `ty`.) /// then `needs_async_drop` will definitely return `true` for `ty`.)
/// ///
/// Note that this method is used to check eligible types in unions. /// When constructing `AsyncDestruct::Destructor` type, use
/// [`Ty::async_drop_glue_morphology`] instead.
//
// FIXME(zetanumbers): Note that this method is used to check eligible types
// in unions.
#[inline] #[inline]
pub fn needs_async_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { pub fn needs_async_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
// Avoid querying in simple cases. // Avoid querying in simple cases.
@ -1647,10 +1677,13 @@ impl<'tcx> ExplicitSelf<'tcx> {
} }
} }
// FIXME(zetanumbers): make specifying asyncness explicit
/// Returns a list of types such that the given type needs drop if and only if /// Returns a list of types such that the given type needs drop if and only if
/// *any* of the returned types need drop. Returns `Err(AlwaysRequiresDrop)` if /// *any* of the returned types need drop. Returns `Err(AlwaysRequiresDrop)` if
/// this type always needs drop. /// this type always needs drop.
//
// FIXME(zetanumbers): consider replacing this with only
// `needs_drop_components_with_async`
#[inline]
pub fn needs_drop_components<'tcx>( pub fn needs_drop_components<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>, ty: Ty<'tcx>,

View file

@ -12,7 +12,7 @@ use rustc_middle::mir::{
Terminator, TerminatorKind, UnwindAction, UnwindTerminateReason, RETURN_PLACE, Terminator, TerminatorKind, UnwindAction, UnwindTerminateReason, RETURN_PLACE,
}; };
use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::util::Discr; use rustc_middle::ty::util::{AsyncDropGlueMorphology, Discr};
use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::{bug, span_bug}; use rustc_middle::{bug, span_bug};
use rustc_span::source_map::respan; use rustc_span::source_map::respan;
@ -116,15 +116,25 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> {
} }
fn build(self) -> Body<'tcx> { fn build(self) -> Body<'tcx> {
let (tcx, def_id, Some(self_ty)) = (self.tcx, self.def_id, self.self_ty) else { let (tcx, Some(self_ty)) = (self.tcx, self.self_ty) else {
return self.build_zst_output(); return self.build_zst_output();
}; };
match self_ty.async_drop_glue_morphology(tcx) {
AsyncDropGlueMorphology::Noop => span_bug!(
self.span,
"async drop glue shim generator encountered type with noop async drop glue morphology"
),
AsyncDropGlueMorphology::DeferredDropInPlace => {
return self.build_deferred_drop_in_place();
}
AsyncDropGlueMorphology::Custom => (),
}
let surface_drop_kind = || { let surface_drop_kind = || {
let param_env = tcx.param_env_reveal_all_normalized(def_id); let adt_def = self_ty.ty_adt_def()?;
if self_ty.has_surface_async_drop(tcx, param_env) { if adt_def.async_destructor(tcx).is_some() {
Some(SurfaceDropKind::Async) Some(SurfaceDropKind::Async)
} else if self_ty.has_surface_drop(tcx, param_env) { } else if adt_def.destructor(tcx).is_some() {
Some(SurfaceDropKind::Sync) Some(SurfaceDropKind::Sync)
} else { } else {
None None
@ -267,6 +277,13 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> {
self.return_() self.return_()
} }
fn build_deferred_drop_in_place(mut self) -> Body<'tcx> {
self.put_self();
let deferred = self.combine_deferred_drop_in_place();
self.combine_fuse(deferred);
self.return_()
}
fn build_fused_async_surface(mut self) -> Body<'tcx> { fn build_fused_async_surface(mut self) -> Body<'tcx> {
self.put_self(); self.put_self();
let surface = self.combine_async_surface(); let surface = self.combine_async_surface();
@ -441,6 +458,14 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> {
) )
} }
fn combine_deferred_drop_in_place(&mut self) -> Ty<'tcx> {
self.apply_combinator(
1,
LangItem::AsyncDropDeferredDropInPlace,
&[self.self_ty.unwrap().into()],
)
}
fn combine_fuse(&mut self, inner_future_ty: Ty<'tcx>) -> Ty<'tcx> { fn combine_fuse(&mut self, inner_future_ty: Ty<'tcx>) -> Ty<'tcx> {
self.apply_combinator(1, LangItem::AsyncDropFuse, &[inner_future_ty.into()]) self.apply_combinator(1, LangItem::AsyncDropFuse, &[inner_future_ty.into()])
} }
@ -481,7 +506,7 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> {
if let Some(ty) = self.self_ty { if let Some(ty) = self.self_ty {
debug_assert_eq!( debug_assert_eq!(
output.ty(&self.locals, self.tcx), output.ty(&self.locals, self.tcx),
ty.async_destructor_ty(self.tcx, self.param_env), ty.async_destructor_ty(self.tcx),
"output async destructor types did not match for type: {ty:?}", "output async destructor types did not match for type: {ty:?}",
); );
} }

View file

@ -430,6 +430,7 @@ symbols! {
async_drop, async_drop,
async_drop_chain, async_drop_chain,
async_drop_defer, async_drop_defer,
async_drop_deferred_drop_in_place,
async_drop_either, async_drop_either,
async_drop_fuse, async_drop_fuse,
async_drop_in_place, async_drop_in_place,

View file

@ -839,7 +839,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
| ty::Str | ty::Str
| ty::Slice(_) | ty::Slice(_)
| ty::Tuple(_) | ty::Tuple(_)
| ty::Error(_) => self_ty.async_destructor_ty(ecx.interner(), goal.param_env), | ty::Error(_) => self_ty.async_destructor_ty(ecx.interner()),
// We do not call `Ty::async_destructor_ty` on alias, param, or placeholder // We do not call `Ty::async_destructor_ty` on alias, param, or placeholder
// types, which return `<self_ty as AsyncDestruct>::AsyncDestructor` // types, which return `<self_ty as AsyncDestruct>::AsyncDestructor`

View file

@ -1560,7 +1560,7 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
let destructor_def_id = tcx.associated_item_def_ids(trait_def_id)[0]; let destructor_def_id = tcx.associated_item_def_ids(trait_def_id)[0];
assert_eq!(destructor_def_id, item_def_id); assert_eq!(destructor_def_id, item_def_id);
(self_ty.async_destructor_ty(tcx, obligation.param_env).into(), Vec::new()) (self_ty.async_destructor_ty(tcx).into(), Vec::new())
} else if lang_items.pointee_trait() == Some(trait_def_id) { } else if lang_items.pointee_trait() == Some(trait_def_id) {
let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None); let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None);
assert_eq!(metadata_def_id, item_def_id); assert_eq!(metadata_def_id, item_def_id);

View file

@ -4,6 +4,7 @@ use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::bug; use rustc_middle::bug;
use rustc_middle::query::Providers; use rustc_middle::query::Providers;
use rustc_middle::traits::{BuiltinImplSource, CodegenObligationError}; use rustc_middle::traits::{BuiltinImplSource, CodegenObligationError};
use rustc_middle::ty::util::AsyncDropGlueMorphology;
use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::GenericArgsRef;
use rustc_middle::ty::{self, Instance, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{self, Instance, TyCtxt, TypeVisitableExt};
use rustc_span::sym; use rustc_span::sym;
@ -59,7 +60,7 @@ fn resolve_instance<'tcx>(
} else if Some(def_id) == tcx.lang_items().async_drop_in_place_fn() { } else if Some(def_id) == tcx.lang_items().async_drop_in_place_fn() {
let ty = args.type_at(0); let ty = args.type_at(0);
if !ty.is_async_destructor_noop(tcx, param_env) { if ty.async_drop_glue_morphology(tcx) != AsyncDropGlueMorphology::Noop {
match *ty.kind() { match *ty.kind() {
ty::Closure(..) ty::Closure(..)
| ty::CoroutineClosure(..) | ty::CoroutineClosure(..)

View file

@ -36,7 +36,7 @@ fn needs_async_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty
// it needs async drop. // it needs async drop.
let adt_has_async_dtor = let adt_has_async_dtor =
|adt_def: ty::AdtDef<'tcx>| adt_def.async_destructor(tcx).map(|_| DtorType::Significant); |adt_def: ty::AdtDef<'tcx>| adt_def.async_destructor(tcx).map(|_| DtorType::Significant);
let res = drop_tys_helper(tcx, query.value, query.param_env, adt_has_dtor, false) let res = drop_tys_helper(tcx, query.value, query.param_env, adt_has_async_dtor, false)
.filter(filter_array_elements(tcx, query.param_env)) .filter(filter_array_elements(tcx, query.param_env))
.next() .next()
.is_some(); .is_some();

View file

@ -161,6 +161,11 @@ async unsafe fn surface_drop_in_place<T: Drop + ?Sized>(ptr: *mut T) {
/// wrapped future completes by returning `Poll::Ready(())` on poll. This /// wrapped future completes by returning `Poll::Ready(())` on poll. This
/// is useful for constructing async destructors to guarantee this /// is useful for constructing async destructors to guarantee this
/// "fuse" property /// "fuse" property
//
// FIXME: Consider optimizing combinators to not have to use fuse in majority
// of cases, perhaps by adding `#[(rustc_)idempotent(_future)]` attribute for
// async functions and blocks with the unit return type. However current layout
// optimizations currently encode `None` case into the async block's discriminant.
struct Fuse<T> { struct Fuse<T> {
inner: Option<T>, inner: Option<T>,
} }
@ -251,6 +256,12 @@ async unsafe fn either<O: IntoFuture<Output = ()>, M: IntoFuture<Output = ()>, T
} }
} }
#[cfg(not(bootstrap))]
#[lang = "async_drop_deferred_drop_in_place"]
async unsafe fn deferred_drop_in_place<T>(to_drop: *mut T) {
unsafe { crate::ptr::drop_in_place(to_drop) }
}
/// Used for noop async destructors. We don't use [`core::future::Ready`] /// Used for noop async destructors. We don't use [`core::future::Ready`]
/// because it panics after its second poll, which could be potentially /// because it panics after its second poll, which could be potentially
/// bad if that would happen during the cleanup. /// bad if that would happen during the cleanup.