mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 06:44:35 +00:00
Add simple async drop glue generation
Explainer: https://zetanumbers.github.io/book/async-drop-design.html https://github.com/rust-lang/rust/pull/121801
This commit is contained in:
parent
1dea922ea6
commit
24a24ec6ba
@ -363,6 +363,24 @@ fn exported_symbols_provider_local(
|
||||
},
|
||||
));
|
||||
}
|
||||
MonoItem::Fn(Instance {
|
||||
def: InstanceDef::AsyncDropGlueCtorShim(def_id, ty),
|
||||
args,
|
||||
}) => {
|
||||
// A little sanity-check
|
||||
debug_assert_eq!(
|
||||
args.non_erasable_generics(tcx, def_id).skip(1).next(),
|
||||
Some(GenericArgKind::Type(ty))
|
||||
);
|
||||
symbols.push((
|
||||
ExportedSymbol::AsyncDropGlueCtorShim(ty),
|
||||
SymbolExportInfo {
|
||||
level: SymbolExportLevel::Rust,
|
||||
kind: SymbolExportKind::Text,
|
||||
used: false,
|
||||
},
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
// Any other symbols don't qualify for sharing
|
||||
}
|
||||
@ -385,6 +403,7 @@ fn upstream_monomorphizations_provider(
|
||||
let mut instances: DefIdMap<UnordMap<_, _>> = Default::default();
|
||||
|
||||
let drop_in_place_fn_def_id = tcx.lang_items().drop_in_place_fn();
|
||||
let async_drop_in_place_fn_def_id = tcx.lang_items().async_drop_in_place_fn();
|
||||
|
||||
for &cnum in cnums.iter() {
|
||||
for (exported_symbol, _) in tcx.exported_symbols(cnum).iter() {
|
||||
@ -399,6 +418,18 @@ fn upstream_monomorphizations_provider(
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ExportedSymbol::AsyncDropGlueCtorShim(ty) => {
|
||||
if let Some(async_drop_in_place_fn_def_id) = async_drop_in_place_fn_def_id {
|
||||
(
|
||||
async_drop_in_place_fn_def_id,
|
||||
tcx.mk_args(&[tcx.lifetimes.re_erased.into(), ty.into()]),
|
||||
)
|
||||
} else {
|
||||
// `drop_in_place` in place does not exist, don't try
|
||||
// to use it.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ExportedSymbol::NonGeneric(..)
|
||||
| ExportedSymbol::ThreadLocalShim(..)
|
||||
| ExportedSymbol::NoDefId(..) => {
|
||||
@ -534,6 +565,13 @@ pub fn symbol_name_for_instance_in_crate<'tcx>(
|
||||
Instance::resolve_drop_in_place(tcx, ty),
|
||||
instantiating_crate,
|
||||
),
|
||||
ExportedSymbol::AsyncDropGlueCtorShim(ty) => {
|
||||
rustc_symbol_mangling::symbol_name_for_instance_in_crate(
|
||||
tcx,
|
||||
Instance::resolve_async_drop_in_place(tcx, ty),
|
||||
instantiating_crate,
|
||||
)
|
||||
}
|
||||
ExportedSymbol::NoDefId(symbol_name) => symbol_name.to_string(),
|
||||
}
|
||||
}
|
||||
@ -582,6 +620,9 @@ pub fn linking_symbol_name_for_instance_in_crate<'tcx>(
|
||||
// DropGlue always use the Rust calling convention and thus follow the target's default
|
||||
// symbol decoration scheme.
|
||||
ExportedSymbol::DropGlue(..) => None,
|
||||
// AsyncDropGlueCtorShim always use the Rust calling convention and thus follow the
|
||||
// target's default symbol decoration scheme.
|
||||
ExportedSymbol::AsyncDropGlueCtorShim(..) => None,
|
||||
// NoDefId always follow the target's default symbol decoration scheme.
|
||||
ExportedSymbol::NoDefId(..) => None,
|
||||
// ThreadLocalShim always follow the target's default symbol decoration scheme.
|
||||
|
@ -558,6 +558,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
| ty::InstanceDef::CloneShim(..)
|
||||
| ty::InstanceDef::FnPtrAddrShim(..)
|
||||
| ty::InstanceDef::ThreadLocalShim(..)
|
||||
| ty::InstanceDef::AsyncDropGlueCtorShim(..)
|
||||
| ty::InstanceDef::Item(_) => {
|
||||
// We need MIR for this fn
|
||||
let Some((body, instance)) = M::find_mir_or_eval_fn(
|
||||
|
@ -162,6 +162,18 @@ language_item_table! {
|
||||
Drop, sym::drop, drop_trait, Target::Trait, GenericRequirement::None;
|
||||
Destruct, sym::destruct, destruct_trait, Target::Trait, GenericRequirement::None;
|
||||
|
||||
AsyncDrop, sym::async_drop, async_drop_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
AsyncDestruct, sym::async_destruct, async_destruct_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
AsyncDropInPlace, sym::async_drop_in_place, async_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1);
|
||||
SurfaceAsyncDropInPlace, sym::surface_async_drop_in_place, surface_async_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1);
|
||||
AsyncDropSurfaceDropInPlace, sym::async_drop_surface_drop_in_place, async_drop_surface_drop_in_place_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);
|
||||
AsyncDropNoop, sym::async_drop_noop, async_drop_noop_fn, Target::Fn, GenericRequirement::Exact(0);
|
||||
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);
|
||||
AsyncDropEither, sym::async_drop_either, async_drop_either_fn, Target::Fn, GenericRequirement::Exact(3);
|
||||
|
||||
CoerceUnsized, sym::coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1);
|
||||
DispatchFromDyn, sym::dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1);
|
||||
|
||||
@ -281,6 +293,7 @@ language_item_table! {
|
||||
|
||||
ExchangeMalloc, sym::exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None;
|
||||
DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1);
|
||||
FallbackSurfaceDrop, sym::fallback_surface_drop, fallback_surface_drop_fn, Target::Fn, GenericRequirement::None;
|
||||
AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None;
|
||||
|
||||
Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1);
|
||||
|
@ -38,8 +38,11 @@ pub fn check_legal_trait_for_method_call(
|
||||
receiver: Option<Span>,
|
||||
expr_span: Span,
|
||||
trait_id: DefId,
|
||||
body_id: DefId,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
if tcx.lang_items().drop_trait() == Some(trait_id) {
|
||||
if tcx.lang_items().drop_trait() == Some(trait_id)
|
||||
&& tcx.lang_items().fallback_surface_drop_fn() != Some(body_id)
|
||||
{
|
||||
let sugg = if let Some(receiver) = receiver.filter(|s| !s.is_empty()) {
|
||||
errors::ExplicitDestructorCallSugg::Snippet {
|
||||
lo: expr_span.shrink_to_lo(),
|
||||
|
@ -1118,6 +1118,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
None,
|
||||
span,
|
||||
container_id,
|
||||
self.body_id.to_def_id(),
|
||||
) {
|
||||
self.set_tainted_by_errors(e);
|
||||
}
|
||||
|
@ -628,6 +628,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
|
||||
Some(self.self_expr.span),
|
||||
self.call_expr.span,
|
||||
trait_def_id,
|
||||
self.body_id.to_def_id(),
|
||||
) {
|
||||
self.set_tainted_by_errors(e);
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ pub enum ExportedSymbol<'tcx> {
|
||||
NonGeneric(DefId),
|
||||
Generic(DefId, GenericArgsRef<'tcx>),
|
||||
DropGlue(Ty<'tcx>),
|
||||
AsyncDropGlueCtorShim(Ty<'tcx>),
|
||||
ThreadLocalShim(DefId),
|
||||
NoDefId(ty::SymbolName<'tcx>),
|
||||
}
|
||||
@ -59,6 +60,9 @@ impl<'tcx> ExportedSymbol<'tcx> {
|
||||
ExportedSymbol::DropGlue(ty) => {
|
||||
tcx.symbol_name(ty::Instance::resolve_drop_in_place(tcx, ty))
|
||||
}
|
||||
ExportedSymbol::AsyncDropGlueCtorShim(ty) => {
|
||||
tcx.symbol_name(ty::Instance::resolve_async_drop_in_place(tcx, ty))
|
||||
}
|
||||
ExportedSymbol::ThreadLocalShim(def_id) => tcx.symbol_name(ty::Instance {
|
||||
def: ty::InstanceDef::ThreadLocalShim(def_id),
|
||||
args: ty::GenericArgs::empty(),
|
||||
|
@ -406,7 +406,8 @@ impl<'tcx> CodegenUnit<'tcx> {
|
||||
| InstanceDef::DropGlue(..)
|
||||
| InstanceDef::CloneShim(..)
|
||||
| InstanceDef::ThreadLocalShim(..)
|
||||
| InstanceDef::FnPtrAddrShim(..) => None,
|
||||
| InstanceDef::FnPtrAddrShim(..)
|
||||
| InstanceDef::AsyncDropGlueCtorShim(..) => None,
|
||||
}
|
||||
}
|
||||
MonoItem::Static(def_id) => def_id.as_local().map(Idx::index),
|
||||
|
@ -187,6 +187,17 @@ fn dump_path<'tcx>(
|
||||
}));
|
||||
s
|
||||
}
|
||||
ty::InstanceDef::AsyncDropGlueCtorShim(_, ty) => {
|
||||
// Unfortunately, pretty-printed typed are not very filename-friendly.
|
||||
// We dome some filtering.
|
||||
let mut s = ".".to_owned();
|
||||
s.extend(ty.to_string().chars().filter_map(|c| match c {
|
||||
' ' => None,
|
||||
':' | '<' | '>' => Some('_'),
|
||||
c => Some(c),
|
||||
}));
|
||||
s
|
||||
}
|
||||
_ => String::new(),
|
||||
};
|
||||
|
||||
|
@ -355,7 +355,8 @@ macro_rules! make_mir_visitor {
|
||||
ty::InstanceDef::FnPtrShim(_def_id, ty) |
|
||||
ty::InstanceDef::DropGlue(_def_id, Some(ty)) |
|
||||
ty::InstanceDef::CloneShim(_def_id, ty) |
|
||||
ty::InstanceDef::FnPtrAddrShim(_def_id, ty) => {
|
||||
ty::InstanceDef::FnPtrAddrShim(_def_id, ty) |
|
||||
ty::InstanceDef::AsyncDropGlueCtorShim(_def_id, ty) => {
|
||||
// FIXME(eddyb) use a better `TyContext` here.
|
||||
self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
|
||||
}
|
||||
|
@ -1344,6 +1344,14 @@ rustc_queries! {
|
||||
query is_unpin_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
|
||||
desc { "computing whether `{}` is `Unpin`", env.value }
|
||||
}
|
||||
/// Query backing `Ty::has_surface_async_drop`.
|
||||
query has_surface_async_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
|
||||
desc { "computing whether `{}` has `AsyncDrop` implementation", env.value }
|
||||
}
|
||||
/// Query backing `Ty::has_surface_drop`.
|
||||
query has_surface_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
|
||||
desc { "computing whether `{}` has `Drop` implementation", env.value }
|
||||
}
|
||||
/// Query backing `Ty::needs_drop`.
|
||||
query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
|
||||
desc { "computing whether `{}` needs drop", env.value }
|
||||
|
@ -168,6 +168,12 @@ pub enum InstanceDef<'tcx> {
|
||||
///
|
||||
/// The `DefId` is for `FnPtr::addr`, the `Ty` is the type `T`.
|
||||
FnPtrAddrShim(DefId, Ty<'tcx>),
|
||||
|
||||
/// `core::future::async_drop::async_drop_in_place::<'_, T>`.
|
||||
///
|
||||
/// The `DefId` is for `core::future::async_drop::async_drop_in_place`, the `Ty`
|
||||
/// is the type `T`.
|
||||
AsyncDropGlueCtorShim(DefId, Ty<'tcx>),
|
||||
}
|
||||
|
||||
impl<'tcx> Instance<'tcx> {
|
||||
@ -210,7 +216,9 @@ impl<'tcx> Instance<'tcx> {
|
||||
InstanceDef::Item(def) => tcx
|
||||
.upstream_monomorphizations_for(def)
|
||||
.and_then(|monos| monos.get(&self.args).cloned()),
|
||||
InstanceDef::DropGlue(_, Some(_)) => tcx.upstream_drop_glue_for(self.args),
|
||||
InstanceDef::DropGlue(_, Some(_)) | InstanceDef::AsyncDropGlueCtorShim(_, _) => {
|
||||
tcx.upstream_drop_glue_for(self.args)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -235,7 +243,8 @@ impl<'tcx> InstanceDef<'tcx> {
|
||||
| ty::InstanceDef::CoroutineKindShim { coroutine_def_id: def_id }
|
||||
| InstanceDef::DropGlue(def_id, _)
|
||||
| InstanceDef::CloneShim(def_id, _)
|
||||
| InstanceDef::FnPtrAddrShim(def_id, _) => def_id,
|
||||
| InstanceDef::FnPtrAddrShim(def_id, _)
|
||||
| InstanceDef::AsyncDropGlueCtorShim(def_id, _) => def_id,
|
||||
}
|
||||
}
|
||||
|
||||
@ -243,9 +252,9 @@ impl<'tcx> InstanceDef<'tcx> {
|
||||
pub fn def_id_if_not_guaranteed_local_codegen(self) -> Option<DefId> {
|
||||
match self {
|
||||
ty::InstanceDef::Item(def) => Some(def),
|
||||
ty::InstanceDef::DropGlue(def_id, Some(_)) | InstanceDef::ThreadLocalShim(def_id) => {
|
||||
Some(def_id)
|
||||
}
|
||||
ty::InstanceDef::DropGlue(def_id, Some(_))
|
||||
| InstanceDef::AsyncDropGlueCtorShim(def_id, _)
|
||||
| InstanceDef::ThreadLocalShim(def_id) => Some(def_id),
|
||||
InstanceDef::VTableShim(..)
|
||||
| InstanceDef::ReifyShim(..)
|
||||
| InstanceDef::FnPtrShim(..)
|
||||
@ -347,7 +356,8 @@ impl<'tcx> InstanceDef<'tcx> {
|
||||
| InstanceDef::ThreadLocalShim(..)
|
||||
| InstanceDef::FnPtrAddrShim(..)
|
||||
| InstanceDef::FnPtrShim(..)
|
||||
| InstanceDef::DropGlue(_, Some(_)) => false,
|
||||
| InstanceDef::DropGlue(_, Some(_))
|
||||
| InstanceDef::AsyncDropGlueCtorShim(..) => false,
|
||||
InstanceDef::ClosureOnceShim { .. }
|
||||
| InstanceDef::ConstructCoroutineInClosureShim { .. }
|
||||
| InstanceDef::CoroutineKindShim { .. }
|
||||
@ -396,6 +406,7 @@ fn fmt_instance(
|
||||
InstanceDef::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({ty}))"),
|
||||
InstanceDef::CloneShim(_, ty) => write!(f, " - shim({ty})"),
|
||||
InstanceDef::FnPtrAddrShim(_, ty) => write!(f, " - shim({ty})"),
|
||||
InstanceDef::AsyncDropGlueCtorShim(_, ty) => write!(f, " - shim({ty})"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -638,6 +649,12 @@ impl<'tcx> Instance<'tcx> {
|
||||
Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args)
|
||||
}
|
||||
|
||||
pub fn resolve_async_drop_in_place(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ty::Instance<'tcx> {
|
||||
let def_id = tcx.require_lang_item(LangItem::AsyncDropInPlace, None);
|
||||
let args = tcx.mk_args(&[ty.into()]);
|
||||
Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(tcx), ret)]
|
||||
pub fn fn_once_adapter_instance(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
|
@ -1797,7 +1797,8 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
| ty::InstanceDef::DropGlue(..)
|
||||
| ty::InstanceDef::CloneShim(..)
|
||||
| ty::InstanceDef::ThreadLocalShim(..)
|
||||
| ty::InstanceDef::FnPtrAddrShim(..) => self.mir_shims(instance),
|
||||
| ty::InstanceDef::FnPtrAddrShim(..)
|
||||
| ty::InstanceDef::AsyncDropGlueCtorShim(..) => self.mir_shims(instance),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@ use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT};
|
||||
use rustc_target::spec::abi::{self, Abi};
|
||||
use std::assert_matches::debug_assert_matches;
|
||||
use std::borrow::Cow;
|
||||
use std::iter;
|
||||
use std::ops::{ControlFlow, Deref, Range};
|
||||
use ty::util::IntTypeExt;
|
||||
|
||||
@ -2316,6 +2317,152 @@ impl<'tcx> Ty<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
match *self.kind() {
|
||||
ty::Param(_) | ty::Alias(..) | ty::Infer(ty::TyVar(_)) => {
|
||||
let assoc_items = tcx
|
||||
.associated_item_def_ids(tcx.require_lang_item(LangItem::AsyncDestruct, None));
|
||||
Ty::new_projection(tcx, assoc_items[0], [self])
|
||||
}
|
||||
|
||||
ty::Array(elem_ty, _) | ty::Slice(elem_ty) => {
|
||||
let dtor = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropSlice)
|
||||
.instantiate(tcx, &[elem_ty.into()]);
|
||||
Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse)
|
||||
.instantiate(tcx, &[dtor.into()])
|
||||
}
|
||||
|
||||
ty::Adt(adt_def, _) if adt_def.is_manually_drop() => {
|
||||
Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop).instantiate_identity()
|
||||
}
|
||||
ty::Adt(adt_def, args) if adt_def.is_enum() || adt_def.is_struct() => self
|
||||
.adt_async_destructor_ty(
|
||||
tcx,
|
||||
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::Closure(_, args) => self.adt_async_destructor_ty(
|
||||
tcx,
|
||||
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()),
|
||||
param_env,
|
||||
),
|
||||
|
||||
ty::Adt(adt_def, _) => {
|
||||
assert!(adt_def.is_union());
|
||||
|
||||
match self.surface_async_dropper_ty(tcx, param_env) {
|
||||
None => Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop)
|
||||
.instantiate_identity(),
|
||||
Some(surface_drop) => {
|
||||
Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse)
|
||||
.instantiate(tcx, &[surface_drop.into()])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ty::Never
|
||||
| ty::Bool
|
||||
| ty::Char
|
||||
| ty::Int(_)
|
||||
| ty::Uint(_)
|
||||
| ty::Float(_)
|
||||
| ty::Str
|
||||
| ty::RawPtr(_, _)
|
||||
| ty::Ref(..)
|
||||
| ty::FnDef(..)
|
||||
| ty::FnPtr(..)
|
||||
| ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
|
||||
| ty::Error(_) => {
|
||||
Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop).instantiate_identity()
|
||||
}
|
||||
|
||||
ty::Dynamic(..) | ty::CoroutineWitness(..) | ty::Coroutine(..) | ty::Pat(..) => {
|
||||
bug!("`async_destructor_ty` is not yet implemented for type: {self:?}")
|
||||
}
|
||||
|
||||
ty::Bound(..)
|
||||
| ty::Foreign(_)
|
||||
| ty::Placeholder(_)
|
||||
| ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
|
||||
bug!("`async_destructor_ty` applied to unexpected type: {self:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn adt_async_destructor_ty<I>(
|
||||
self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
variants: I,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
) -> Ty<'tcx>
|
||||
where
|
||||
I: Iterator + ExactSizeIterator,
|
||||
I::Item: IntoIterator<Item = Ty<'tcx>>,
|
||||
{
|
||||
let defer = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropDefer);
|
||||
let chain = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropChain);
|
||||
|
||||
let noop =
|
||||
Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop).instantiate_identity();
|
||||
let either = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropEither);
|
||||
|
||||
let variants_dtor = variants
|
||||
.into_iter()
|
||||
.map(|variant| {
|
||||
variant
|
||||
.into_iter()
|
||||
.map(|ty| defer.instantiate(tcx, &[ty.into()]))
|
||||
.reduce(|acc, next| chain.instantiate(tcx, &[acc.into(), next.into()]))
|
||||
.unwrap_or(noop)
|
||||
})
|
||||
.reduce(|other, matched| {
|
||||
either.instantiate(tcx, &[other.into(), matched.into(), self.into()])
|
||||
})
|
||||
.unwrap_or(noop);
|
||||
|
||||
let dtor = if let Some(dropper_ty) = self.surface_async_dropper_ty(tcx, param_env) {
|
||||
Ty::async_destructor_combinator(tcx, LangItem::AsyncDropChain)
|
||||
.instantiate(tcx, &[dropper_ty.into(), variants_dtor.into()])
|
||||
} else {
|
||||
variants_dtor
|
||||
};
|
||||
|
||||
Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse)
|
||||
.instantiate(tcx, &[dtor.into()])
|
||||
}
|
||||
|
||||
fn surface_async_dropper_ty(
|
||||
self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
) -> Option<Ty<'tcx>> {
|
||||
if self.has_surface_async_drop(tcx, param_env) {
|
||||
Some(LangItem::SurfaceAsyncDropInPlace)
|
||||
} else if self.has_surface_drop(tcx, param_env) {
|
||||
Some(LangItem::AsyncDropSurfaceDropInPlace)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
.map(|dropper| {
|
||||
Ty::async_destructor_combinator(tcx, dropper).instantiate(tcx, &[self.into()])
|
||||
})
|
||||
}
|
||||
|
||||
fn async_destructor_combinator(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
lang_item: LangItem,
|
||||
) -> ty::EarlyBinder<Ty<'tcx>> {
|
||||
tcx.fn_sig(tcx.require_lang_item(lang_item, None))
|
||||
.map_bound(|fn_sig| fn_sig.output().no_bound_vars().unwrap())
|
||||
}
|
||||
|
||||
/// Returns the type of metadata for (potentially fat) pointers to this type,
|
||||
/// or the struct tail if the metadata type cannot be determined.
|
||||
pub fn ptr_metadata_ty_or_tail(
|
||||
|
@ -1303,6 +1303,97 @@ impl<'tcx> Ty<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether values of this type `T` implements the `AsyncDrop`
|
||||
/// trait.
|
||||
pub fn has_surface_async_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
|
||||
self.trivially_has_surface_async_drop()
|
||||
&& tcx.has_surface_async_drop_raw(param_env.and(self))
|
||||
}
|
||||
|
||||
/// Fast path helper for testing if a type has `AsyncDrop`
|
||||
/// implementation.
|
||||
///
|
||||
/// Returning `false` means the type is known to not have `AsyncDrop`
|
||||
/// implementation. Returning `true` means nothing -- could be
|
||||
/// `AsyncDrop`, might not be.
|
||||
fn trivially_has_surface_async_drop(self) -> bool {
|
||||
match self.kind() {
|
||||
ty::Int(_)
|
||||
| ty::Uint(_)
|
||||
| ty::Float(_)
|
||||
| ty::Bool
|
||||
| ty::Char
|
||||
| ty::Str
|
||||
| ty::Never
|
||||
| ty::Ref(..)
|
||||
| ty::RawPtr(_, _)
|
||||
| ty::FnDef(..)
|
||||
| ty::FnPtr(_)
|
||||
| ty::Error(_)
|
||||
| ty::Tuple(_)
|
||||
| ty::Slice(_)
|
||||
| ty::Array(_, _)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(..)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Pat(..) => false,
|
||||
ty::Adt(..)
|
||||
| ty::Bound(..)
|
||||
| ty::Dynamic(..)
|
||||
| ty::Foreign(_)
|
||||
| ty::Infer(_)
|
||||
| ty::Alias(..)
|
||||
| ty::Param(_)
|
||||
| ty::Placeholder(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether values of this type `T` implements the `AsyncDrop`
|
||||
/// trait.
|
||||
pub fn has_surface_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
|
||||
self.trivially_has_surface_drop() && tcx.has_surface_drop_raw(param_env.and(self))
|
||||
}
|
||||
|
||||
/// Fast path helper for testing if a type has `AsyncDrop`
|
||||
/// implementation.
|
||||
///
|
||||
/// Returning `false` means the type is known to not have `AsyncDrop`
|
||||
/// implementation. Returning `true` means nothing -- could be
|
||||
/// `AsyncDrop`, might not be.
|
||||
fn trivially_has_surface_drop(self) -> bool {
|
||||
match self.kind() {
|
||||
ty::Int(_)
|
||||
| ty::Uint(_)
|
||||
| ty::Float(_)
|
||||
| ty::Bool
|
||||
| ty::Char
|
||||
| ty::Str
|
||||
| ty::Never
|
||||
| ty::Ref(..)
|
||||
| ty::RawPtr(_, _)
|
||||
| ty::FnDef(..)
|
||||
| ty::FnPtr(_)
|
||||
| ty::Error(_)
|
||||
| ty::Tuple(_)
|
||||
| ty::Slice(_)
|
||||
| ty::Array(_, _)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(..)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Pat(..) => false,
|
||||
ty::Adt(..)
|
||||
| ty::Bound(..)
|
||||
| ty::Dynamic(..)
|
||||
| ty::Foreign(_)
|
||||
| ty::Infer(_)
|
||||
| ty::Alias(..)
|
||||
| ty::Param(_)
|
||||
| ty::Placeholder(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// If `ty.needs_drop(...)` returns `true`, then `ty` is definitely
|
||||
/// non-copy and *might* have a destructor attached; if it returns
|
||||
/// `false`, then `ty` definitely has no destructor (i.e., no drop glue).
|
||||
|
@ -332,7 +332,8 @@ impl<'tcx> Inliner<'tcx> {
|
||||
| InstanceDef::DropGlue(..)
|
||||
| InstanceDef::CloneShim(..)
|
||||
| InstanceDef::ThreadLocalShim(..)
|
||||
| InstanceDef::FnPtrAddrShim(..) => return Ok(()),
|
||||
| InstanceDef::FnPtrAddrShim(..)
|
||||
| InstanceDef::AsyncDropGlueCtorShim(..) => return Ok(()),
|
||||
}
|
||||
|
||||
if self.tcx.is_constructor(callee_def_id) {
|
||||
@ -1071,7 +1072,8 @@ fn try_instance_mir<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
instance: InstanceDef<'tcx>,
|
||||
) -> Result<&'tcx Body<'tcx>, &'static str> {
|
||||
if let ty::InstanceDef::DropGlue(_, Some(ty)) = instance
|
||||
if let ty::InstanceDef::DropGlue(_, Some(ty)) | ty::InstanceDef::AsyncDropGlueCtorShim(_, ty) =
|
||||
instance
|
||||
&& let ty::Adt(def, args) = ty.kind()
|
||||
{
|
||||
let fields = def.all_fields();
|
||||
|
@ -94,8 +94,10 @@ pub(crate) fn mir_callgraph_reachable<'tcx>(
|
||||
| InstanceDef::CloneShim(..) => {}
|
||||
|
||||
// This shim does not call any other functions, thus there can be no recursion.
|
||||
InstanceDef::FnPtrAddrShim(..) => continue,
|
||||
InstanceDef::DropGlue(..) => {
|
||||
InstanceDef::FnPtrAddrShim(..) => {
|
||||
continue;
|
||||
}
|
||||
InstanceDef::DropGlue(..) | InstanceDef::AsyncDropGlueCtorShim(..) => {
|
||||
// FIXME: A not fully instantiated drop shim can cause ICEs if one attempts to
|
||||
// have its MIR built. Likely oli-obk just screwed up the `ParamEnv`s, so this
|
||||
// needs some more analysis.
|
||||
|
@ -22,6 +22,8 @@ use crate::{
|
||||
use rustc_middle::mir::patch::MirPatch;
|
||||
use rustc_mir_dataflow::elaborate_drops::{self, DropElaborator, DropFlagMode, DropStyle};
|
||||
|
||||
mod async_destructor_ctor;
|
||||
|
||||
pub fn provide(providers: &mut Providers) {
|
||||
providers.mir_shims = make_shim;
|
||||
}
|
||||
@ -127,6 +129,9 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
|
||||
ty::InstanceDef::ThreadLocalShim(..) => build_thread_local_shim(tcx, instance),
|
||||
ty::InstanceDef::CloneShim(def_id, ty) => build_clone_shim(tcx, def_id, ty),
|
||||
ty::InstanceDef::FnPtrAddrShim(def_id, ty) => build_fn_ptr_addr_shim(tcx, def_id, ty),
|
||||
ty::InstanceDef::AsyncDropGlueCtorShim(def_id, ty) => {
|
||||
async_destructor_ctor::build_async_destructor_ctor_shim(tcx, def_id, ty)
|
||||
}
|
||||
ty::InstanceDef::Virtual(..) => {
|
||||
bug!("InstanceDef::Virtual ({:?}) is for direct calls only", instance)
|
||||
}
|
||||
|
612
compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs
Normal file
612
compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs
Normal file
@ -0,0 +1,612 @@
|
||||
use std::iter;
|
||||
|
||||
use itertools::Itertools;
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_const_eval::interpret;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_index::{Idx, IndexVec};
|
||||
use rustc_middle::mir::{
|
||||
BasicBlock, BasicBlockData, Body, CallSource, CastKind, Const, ConstOperand, ConstValue, Local,
|
||||
LocalDecl, MirSource, Operand, Place, PlaceElem, Rvalue, SourceInfo, Statement, StatementKind,
|
||||
Terminator, TerminatorKind, UnwindAction, UnwindTerminateReason,
|
||||
};
|
||||
use rustc_middle::ty::adjustment::PointerCoercion;
|
||||
use rustc_middle::ty::util::Discr;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_span::source_map::respan;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use rustc_target::abi::{FieldIdx, VariantIdx};
|
||||
use rustc_target::spec::PanicStrategy;
|
||||
|
||||
use super::{local_decls_for_sig, new_body};
|
||||
|
||||
pub fn build_async_destructor_ctor_shim<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: DefId,
|
||||
self_ty: Ty<'tcx>,
|
||||
) -> Body<'tcx> {
|
||||
AsyncDestructorCtorShimBuilder::new(tcx, def_id, self_ty).build()
|
||||
}
|
||||
|
||||
/// Builder for async_drop_in_place shim. Functions as a stack machine
|
||||
/// to build up an expression using combinators. Stack contains pairs
|
||||
/// of locals and types. Combinator is a not yet instantiated pair of a
|
||||
/// function and a type, is considered to be an operator which consumes
|
||||
/// operands from the stack by instantiating its function and its type
|
||||
/// with operand types and moving locals into the function call. Top
|
||||
/// pair is considered to be the last operand.
|
||||
// FIXME: add mir-opt tests
|
||||
struct AsyncDestructorCtorShimBuilder<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: DefId,
|
||||
self_ty: Ty<'tcx>,
|
||||
span: Span,
|
||||
source_info: SourceInfo,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
|
||||
stack: Vec<Operand<'tcx>>,
|
||||
last_bb: BasicBlock,
|
||||
top_cleanup_bb: Option<BasicBlock>,
|
||||
|
||||
locals: IndexVec<Local, LocalDecl<'tcx>>,
|
||||
bbs: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum SurfaceDropKind {
|
||||
Async,
|
||||
Sync,
|
||||
}
|
||||
|
||||
impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> {
|
||||
const SELF_PTR: Local = Local::from_u32(1);
|
||||
const INPUT_COUNT: usize = 1;
|
||||
const MAX_STACK_LEN: usize = 2;
|
||||
|
||||
fn new(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -> Self {
|
||||
let span = tcx.def_span(def_id);
|
||||
let Some(sig) = tcx.fn_sig(def_id).instantiate(tcx, &[self_ty.into()]).no_bound_vars()
|
||||
else {
|
||||
span_bug!(span, "async_drop_in_place_raw with bound vars for `{self_ty}`");
|
||||
};
|
||||
|
||||
let source_info = SourceInfo::outermost(span);
|
||||
|
||||
debug_assert_eq!(sig.inputs().len(), Self::INPUT_COUNT);
|
||||
let locals = local_decls_for_sig(&sig, span);
|
||||
|
||||
// Usual case: noop() + unwind resume + return
|
||||
let mut bbs = IndexVec::with_capacity(3);
|
||||
let param_env = tcx.param_env_reveal_all_normalized(def_id);
|
||||
AsyncDestructorCtorShimBuilder {
|
||||
tcx,
|
||||
def_id,
|
||||
self_ty,
|
||||
span,
|
||||
source_info,
|
||||
param_env,
|
||||
|
||||
stack: Vec::with_capacity(Self::MAX_STACK_LEN),
|
||||
last_bb: bbs.push(BasicBlockData::new(None)),
|
||||
top_cleanup_bb: match tcx.sess.panic_strategy() {
|
||||
PanicStrategy::Unwind => {
|
||||
// Don't drop input arg because it's just a pointer
|
||||
Some(bbs.push(BasicBlockData {
|
||||
statements: Vec::new(),
|
||||
terminator: Some(Terminator {
|
||||
source_info,
|
||||
kind: TerminatorKind::UnwindResume,
|
||||
}),
|
||||
is_cleanup: true,
|
||||
}))
|
||||
}
|
||||
PanicStrategy::Abort => None,
|
||||
},
|
||||
|
||||
locals,
|
||||
bbs,
|
||||
}
|
||||
}
|
||||
|
||||
fn build(self) -> Body<'tcx> {
|
||||
let (tcx, def_id, self_ty) = (self.tcx, self.def_id, self.self_ty);
|
||||
|
||||
let surface_drop_kind = || {
|
||||
let param_env = tcx.param_env_reveal_all_normalized(def_id);
|
||||
if self_ty.has_surface_async_drop(tcx, param_env) {
|
||||
Some(SurfaceDropKind::Async)
|
||||
} else if self_ty.has_surface_drop(tcx, param_env) {
|
||||
Some(SurfaceDropKind::Sync)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
match self_ty.kind() {
|
||||
ty::Array(elem_ty, _) => self.build_slice(true, *elem_ty),
|
||||
ty::Slice(elem_ty) => self.build_slice(false, *elem_ty),
|
||||
|
||||
ty::Adt(adt_def, _) if adt_def.is_manually_drop() => self.build_noop(),
|
||||
|
||||
ty::Tuple(elem_tys) => self.build_chain(None, elem_tys.iter()),
|
||||
ty::Adt(adt_def, args) if adt_def.is_struct() => {
|
||||
let field_tys = adt_def.non_enum_variant().fields.iter().map(|f| f.ty(tcx, args));
|
||||
self.build_chain(surface_drop_kind(), field_tys)
|
||||
}
|
||||
ty::Closure(_, args) => self.build_chain(None, args.as_closure().upvar_tys().iter()),
|
||||
ty::CoroutineClosure(_, args) => {
|
||||
self.build_chain(None, args.as_coroutine_closure().upvar_tys().iter())
|
||||
}
|
||||
|
||||
ty::Adt(adt_def, args) if adt_def.is_enum() => {
|
||||
self.build_enum(*adt_def, *args, surface_drop_kind())
|
||||
}
|
||||
|
||||
ty::Never
|
||||
| ty::Bool
|
||||
| ty::Char
|
||||
| ty::Int(_)
|
||||
| ty::Uint(_)
|
||||
| ty::Float(_)
|
||||
| ty::Str
|
||||
| ty::RawPtr(_, _)
|
||||
| ty::Ref(_, _, _)
|
||||
| ty::FnDef(_, _)
|
||||
| ty::FnPtr(_)
|
||||
| ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
|
||||
| ty::Error(_) => self.build_noop(),
|
||||
|
||||
ty::Adt(adt_def, _) => {
|
||||
assert!(adt_def.is_union());
|
||||
match surface_drop_kind() {
|
||||
Some(SurfaceDropKind::Async) => self.build_fused_async_surface(),
|
||||
Some(SurfaceDropKind::Sync) => self.build_fused_sync_surface(),
|
||||
None => self.build_noop(),
|
||||
}
|
||||
}
|
||||
|
||||
ty::Dynamic(..) | ty::CoroutineWitness(..) | ty::Coroutine(..) | ty::Pat(..) => {
|
||||
bug!(
|
||||
"Building async destructor constructor shim is not yet implemented for type: {self_ty:?}"
|
||||
)
|
||||
}
|
||||
|
||||
ty::Bound(..)
|
||||
| ty::Foreign(_)
|
||||
| ty::Placeholder(_)
|
||||
| ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) | ty::TyVar(_))
|
||||
| ty::Param(_)
|
||||
| ty::Alias(..) => {
|
||||
bug!("Building async destructor for unexpected type: {self_ty:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_enum(
|
||||
mut self,
|
||||
adt_def: ty::AdtDef<'tcx>,
|
||||
args: ty::GenericArgsRef<'tcx>,
|
||||
surface_drop: Option<SurfaceDropKind>,
|
||||
) -> Body<'tcx> {
|
||||
let tcx = self.tcx;
|
||||
|
||||
let surface = match surface_drop {
|
||||
None => None,
|
||||
Some(kind) => {
|
||||
self.put_self();
|
||||
Some(match kind {
|
||||
SurfaceDropKind::Async => self.combine_async_surface(),
|
||||
SurfaceDropKind::Sync => self.combine_sync_surface(),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
let mut other = None;
|
||||
for (variant_idx, discr) in adt_def.discriminants(tcx) {
|
||||
let variant = adt_def.variant(variant_idx);
|
||||
|
||||
let mut chain = None;
|
||||
for (field_idx, field) in variant.fields.iter_enumerated() {
|
||||
let field_ty = field.ty(tcx, args);
|
||||
self.put_variant_field(variant.name, variant_idx, field_idx, field_ty);
|
||||
let defer = self.combine_defer(field_ty);
|
||||
chain = Some(match chain {
|
||||
None => defer,
|
||||
Some(chain) => self.combine_chain(chain, defer),
|
||||
})
|
||||
}
|
||||
let variant_dtor = chain.unwrap_or_else(|| self.put_noop());
|
||||
|
||||
other = Some(match other {
|
||||
None => variant_dtor,
|
||||
Some(other) => {
|
||||
self.put_self();
|
||||
self.put_discr(discr);
|
||||
self.combine_either(other, variant_dtor)
|
||||
}
|
||||
});
|
||||
}
|
||||
let variants_dtor = other.unwrap_or_else(|| self.put_noop());
|
||||
|
||||
let dtor = match surface {
|
||||
None => variants_dtor,
|
||||
Some(surface) => self.combine_chain(surface, variants_dtor),
|
||||
};
|
||||
self.combine_fuse(dtor);
|
||||
self.return_()
|
||||
}
|
||||
|
||||
fn build_chain<I>(mut self, surface_drop: Option<SurfaceDropKind>, elem_tys: I) -> Body<'tcx>
|
||||
where
|
||||
I: Iterator<Item = Ty<'tcx>> + ExactSizeIterator,
|
||||
{
|
||||
let surface = match surface_drop {
|
||||
None => None,
|
||||
Some(kind) => {
|
||||
self.put_self();
|
||||
Some(match kind {
|
||||
SurfaceDropKind::Async => self.combine_async_surface(),
|
||||
SurfaceDropKind::Sync => self.combine_sync_surface(),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
let mut chain = None;
|
||||
for (field_idx, field_ty) in elem_tys.enumerate().map(|(i, ty)| (FieldIdx::new(i), ty)) {
|
||||
self.put_field(field_idx, field_ty);
|
||||
let defer = self.combine_defer(field_ty);
|
||||
chain = Some(match chain {
|
||||
None => defer,
|
||||
Some(chain) => self.combine_chain(chain, defer),
|
||||
})
|
||||
}
|
||||
let chain = chain.unwrap_or_else(|| self.put_noop());
|
||||
|
||||
let dtor = match surface {
|
||||
None => chain,
|
||||
Some(surface) => self.combine_chain(surface, chain),
|
||||
};
|
||||
self.combine_fuse(dtor);
|
||||
self.return_()
|
||||
}
|
||||
|
||||
fn build_noop(mut self) -> Body<'tcx> {
|
||||
self.put_noop();
|
||||
self.return_()
|
||||
}
|
||||
|
||||
fn build_fused_async_surface(mut self) -> Body<'tcx> {
|
||||
self.put_self();
|
||||
let surface = self.combine_async_surface();
|
||||
self.combine_fuse(surface);
|
||||
self.return_()
|
||||
}
|
||||
|
||||
fn build_fused_sync_surface(mut self) -> Body<'tcx> {
|
||||
self.put_self();
|
||||
let surface = self.combine_sync_surface();
|
||||
self.combine_fuse(surface);
|
||||
self.return_()
|
||||
}
|
||||
|
||||
fn build_slice(mut self, is_array: bool, elem_ty: Ty<'tcx>) -> Body<'tcx> {
|
||||
if is_array {
|
||||
self.put_array_as_slice(elem_ty)
|
||||
} else {
|
||||
self.put_self()
|
||||
}
|
||||
let dtor = self.combine_slice(elem_ty);
|
||||
self.combine_fuse(dtor);
|
||||
self.return_()
|
||||
}
|
||||
|
||||
/// Puts `to_drop: *mut Self` on top of the stack.
|
||||
fn put_self(&mut self) {
|
||||
self.put_operand(Operand::Copy(Self::SELF_PTR.into()))
|
||||
}
|
||||
|
||||
/// Given that `Self is [ElemTy; N]` puts `to_drop: *mut [ElemTy]`
|
||||
/// on top of the stack.
|
||||
fn put_array_as_slice(&mut self, elem_ty: Ty<'tcx>) {
|
||||
let slice_ptr_ty = Ty::new_mut_ptr(self.tcx, Ty::new_slice(self.tcx, elem_ty));
|
||||
self.put_temp_rvalue(Rvalue::Cast(
|
||||
CastKind::PointerCoercion(PointerCoercion::Unsize),
|
||||
Operand::Copy(Self::SELF_PTR.into()),
|
||||
slice_ptr_ty,
|
||||
))
|
||||
}
|
||||
|
||||
/// If given Self is a struct puts `to_drop: *mut FieldTy` on top
|
||||
/// of the stack.
|
||||
fn put_field(&mut self, field: FieldIdx, field_ty: Ty<'tcx>) {
|
||||
let place = Place {
|
||||
local: Self::SELF_PTR,
|
||||
projection: self
|
||||
.tcx
|
||||
.mk_place_elems(&[PlaceElem::Deref, PlaceElem::Field(field, field_ty)]),
|
||||
};
|
||||
self.put_temp_rvalue(Rvalue::AddressOf(Mutability::Mut, place))
|
||||
}
|
||||
|
||||
/// If given Self is an enum puts `to_drop: *mut FieldTy` on top of
|
||||
/// the stack.
|
||||
fn put_variant_field(
|
||||
&mut self,
|
||||
variant_sym: Symbol,
|
||||
variant: VariantIdx,
|
||||
field: FieldIdx,
|
||||
field_ty: Ty<'tcx>,
|
||||
) {
|
||||
let place = Place {
|
||||
local: Self::SELF_PTR,
|
||||
projection: self.tcx.mk_place_elems(&[
|
||||
PlaceElem::Deref,
|
||||
PlaceElem::Downcast(Some(variant_sym), variant),
|
||||
PlaceElem::Field(field, field_ty),
|
||||
]),
|
||||
};
|
||||
self.put_temp_rvalue(Rvalue::AddressOf(Mutability::Mut, place))
|
||||
}
|
||||
|
||||
/// If given Self is an enum puts `to_drop: *mut FieldTy` on top of
|
||||
/// the stack.
|
||||
fn put_discr(&mut self, discr: Discr<'tcx>) {
|
||||
let (size, _) = discr.ty.int_size_and_signed(self.tcx);
|
||||
self.put_operand(Operand::const_from_scalar(
|
||||
self.tcx,
|
||||
discr.ty,
|
||||
interpret::Scalar::from_uint(discr.val, size),
|
||||
self.span,
|
||||
));
|
||||
}
|
||||
|
||||
/// Puts `x: RvalueType` on top of the stack.
|
||||
fn put_temp_rvalue(&mut self, rvalue: Rvalue<'tcx>) {
|
||||
let last_bb = &mut self.bbs[self.last_bb];
|
||||
debug_assert!(last_bb.terminator.is_none());
|
||||
let source_info = self.source_info;
|
||||
|
||||
let local_ty = rvalue.ty(&self.locals, self.tcx);
|
||||
// We need to create a new local to be able to "consume" it with
|
||||
// a combinator
|
||||
let local = self.locals.push(LocalDecl::with_source_info(local_ty, source_info));
|
||||
last_bb.statements.extend_from_slice(&[
|
||||
Statement { source_info, kind: StatementKind::StorageLive(local) },
|
||||
Statement {
|
||||
source_info,
|
||||
kind: StatementKind::Assign(Box::new((local.into(), rvalue))),
|
||||
},
|
||||
]);
|
||||
|
||||
self.put_operand(Operand::Move(local.into()));
|
||||
}
|
||||
|
||||
/// Puts operand on top of the stack.
|
||||
fn put_operand(&mut self, operand: Operand<'tcx>) {
|
||||
if let Some(top_cleanup_bb) = &mut self.top_cleanup_bb {
|
||||
let source_info = self.source_info;
|
||||
match &operand {
|
||||
Operand::Copy(_) | Operand::Constant(_) => {
|
||||
*top_cleanup_bb = self.bbs.push(BasicBlockData {
|
||||
statements: Vec::new(),
|
||||
terminator: Some(Terminator {
|
||||
source_info,
|
||||
kind: TerminatorKind::Goto { target: *top_cleanup_bb },
|
||||
}),
|
||||
is_cleanup: true,
|
||||
});
|
||||
}
|
||||
Operand::Move(place) => {
|
||||
let local = place.as_local().unwrap();
|
||||
*top_cleanup_bb = self.bbs.push(BasicBlockData {
|
||||
statements: Vec::new(),
|
||||
terminator: Some(Terminator {
|
||||
source_info,
|
||||
kind: if self.locals[local].ty.needs_drop(self.tcx, self.param_env) {
|
||||
TerminatorKind::Drop {
|
||||
place: local.into(),
|
||||
target: *top_cleanup_bb,
|
||||
unwind: UnwindAction::Terminate(
|
||||
UnwindTerminateReason::InCleanup,
|
||||
),
|
||||
replace: false,
|
||||
}
|
||||
} else {
|
||||
TerminatorKind::Goto { target: *top_cleanup_bb }
|
||||
},
|
||||
}),
|
||||
is_cleanup: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
self.stack.push(operand);
|
||||
}
|
||||
|
||||
/// Puts `noop: async_drop::Noop` on top of the stack
|
||||
fn put_noop(&mut self) -> Ty<'tcx> {
|
||||
self.apply_combinator(0, LangItem::AsyncDropNoop, &[])
|
||||
}
|
||||
|
||||
fn combine_async_surface(&mut self) -> Ty<'tcx> {
|
||||
self.apply_combinator(1, LangItem::SurfaceAsyncDropInPlace, &[self.self_ty.into()])
|
||||
}
|
||||
|
||||
fn combine_sync_surface(&mut self) -> Ty<'tcx> {
|
||||
self.apply_combinator(1, LangItem::AsyncDropSurfaceDropInPlace, &[self.self_ty.into()])
|
||||
}
|
||||
|
||||
fn combine_fuse(&mut self, inner_future_ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
self.apply_combinator(1, LangItem::AsyncDropFuse, &[inner_future_ty.into()])
|
||||
}
|
||||
|
||||
fn combine_slice(&mut self, elem_ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
self.apply_combinator(1, LangItem::AsyncDropSlice, &[elem_ty.into()])
|
||||
}
|
||||
|
||||
fn combine_defer(&mut self, to_drop_ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
self.apply_combinator(1, LangItem::AsyncDropDefer, &[to_drop_ty.into()])
|
||||
}
|
||||
|
||||
fn combine_chain(&mut self, first: Ty<'tcx>, second: Ty<'tcx>) -> Ty<'tcx> {
|
||||
self.apply_combinator(2, LangItem::AsyncDropChain, &[first.into(), second.into()])
|
||||
}
|
||||
|
||||
fn combine_either(&mut self, other: Ty<'tcx>, matched: Ty<'tcx>) -> Ty<'tcx> {
|
||||
self.apply_combinator(
|
||||
4,
|
||||
LangItem::AsyncDropEither,
|
||||
&[other.into(), matched.into(), self.self_ty.into()],
|
||||
)
|
||||
}
|
||||
|
||||
fn return_(mut self) -> Body<'tcx> {
|
||||
let last_bb = &mut self.bbs[self.last_bb];
|
||||
debug_assert!(last_bb.terminator.is_none());
|
||||
let source_info = self.source_info;
|
||||
|
||||
let (1, Some(output)) = (self.stack.len(), self.stack.pop()) else {
|
||||
span_bug!(
|
||||
self.span,
|
||||
"async destructor ctor shim builder finished with invalid number of stack items: expected 1 found {}",
|
||||
self.stack.len(),
|
||||
)
|
||||
};
|
||||
const RETURN_LOCAL: Local = Local::from_u32(0);
|
||||
|
||||
debug_assert_eq!(
|
||||
output.ty(&self.locals, self.tcx),
|
||||
self.self_ty.async_destructor_ty(self.tcx, self.param_env),
|
||||
);
|
||||
let dead_storage = match &output {
|
||||
Operand::Move(place) => Some(Statement {
|
||||
source_info,
|
||||
kind: StatementKind::StorageDead(place.as_local().unwrap()),
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
last_bb.statements.extend(
|
||||
iter::once(Statement {
|
||||
source_info,
|
||||
kind: StatementKind::Assign(Box::new((RETURN_LOCAL.into(), Rvalue::Use(output)))),
|
||||
})
|
||||
.chain(dead_storage),
|
||||
);
|
||||
|
||||
last_bb.terminator = Some(Terminator { source_info, kind: TerminatorKind::Return });
|
||||
|
||||
let source = MirSource::from_instance(ty::InstanceDef::AsyncDropGlueCtorShim(
|
||||
self.def_id,
|
||||
self.self_ty,
|
||||
));
|
||||
new_body(source, self.bbs, self.locals, Self::INPUT_COUNT, self.span)
|
||||
}
|
||||
|
||||
fn apply_combinator(
|
||||
&mut self,
|
||||
arity: usize,
|
||||
function: LangItem,
|
||||
args: &[ty::GenericArg<'tcx>],
|
||||
) -> Ty<'tcx> {
|
||||
let function = self.tcx.require_lang_item(function, Some(self.span));
|
||||
let operands_split = self
|
||||
.stack
|
||||
.len()
|
||||
.checked_sub(arity)
|
||||
.expect("async destructor ctor shim combinator tried to consume too many items");
|
||||
let operands = &self.stack[operands_split..];
|
||||
|
||||
let func_ty = Ty::new_fn_def(self.tcx, function, args.iter().copied());
|
||||
let func_sig = func_ty.fn_sig(self.tcx).no_bound_vars().unwrap();
|
||||
#[cfg(debug_assertions)]
|
||||
operands.iter().zip(func_sig.inputs()).for_each(|(operand, expected_ty)| {
|
||||
let operand_ty = operand.ty(&self.locals, self.tcx);
|
||||
if operand_ty == *expected_ty {
|
||||
return;
|
||||
}
|
||||
|
||||
// If projection of Discriminant then compare with `Ty::discriminant_ty`
|
||||
if let ty::Alias(ty::AliasKind::Projection, ty::AliasTy { args, def_id, .. }) =
|
||||
expected_ty.kind()
|
||||
&& Some(*def_id) == self.tcx.lang_items().discriminant_type()
|
||||
&& args.first().unwrap().as_type().unwrap().discriminant_ty(self.tcx) == operand_ty
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
span_bug!(
|
||||
self.span,
|
||||
"Operand type and combinator argument type are not equal.
|
||||
operand_ty: {:?}
|
||||
argument_ty: {:?}
|
||||
",
|
||||
operand_ty,
|
||||
expected_ty
|
||||
);
|
||||
});
|
||||
|
||||
let target = self.bbs.push(BasicBlockData {
|
||||
statements: operands
|
||||
.iter()
|
||||
.rev()
|
||||
.filter_map(|o| {
|
||||
if let Operand::Move(Place { local, projection }) = o {
|
||||
assert!(projection.is_empty());
|
||||
Some(Statement {
|
||||
source_info: self.source_info,
|
||||
kind: StatementKind::StorageDead(*local),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
terminator: None,
|
||||
is_cleanup: false,
|
||||
});
|
||||
|
||||
let dest_ty = func_sig.output();
|
||||
let dest =
|
||||
self.locals.push(LocalDecl::with_source_info(dest_ty, self.source_info).immutable());
|
||||
|
||||
let unwind = if let Some(top_cleanup_bb) = &mut self.top_cleanup_bb {
|
||||
for _ in 0..arity {
|
||||
*top_cleanup_bb =
|
||||
self.bbs[*top_cleanup_bb].terminator().successors().exactly_one().ok().unwrap();
|
||||
}
|
||||
UnwindAction::Cleanup(*top_cleanup_bb)
|
||||
} else {
|
||||
UnwindAction::Unreachable
|
||||
};
|
||||
|
||||
let last_bb = &mut self.bbs[self.last_bb];
|
||||
debug_assert!(last_bb.terminator.is_none());
|
||||
last_bb.statements.push(Statement {
|
||||
source_info: self.source_info,
|
||||
kind: StatementKind::StorageLive(dest),
|
||||
});
|
||||
last_bb.terminator = Some(Terminator {
|
||||
source_info: self.source_info,
|
||||
kind: TerminatorKind::Call {
|
||||
func: Operand::Constant(Box::new(ConstOperand {
|
||||
span: self.span,
|
||||
user_ty: None,
|
||||
const_: Const::Val(ConstValue::ZeroSized, func_ty),
|
||||
})),
|
||||
destination: dest.into(),
|
||||
target: Some(target),
|
||||
unwind,
|
||||
call_source: CallSource::Misc,
|
||||
fn_span: self.span,
|
||||
args: self.stack.drain(operands_split..).map(|o| respan(self.span, o)).collect(),
|
||||
},
|
||||
});
|
||||
|
||||
self.put_operand(Operand::Move(dest.into()));
|
||||
self.last_bb = target;
|
||||
|
||||
dest_ty
|
||||
}
|
||||
}
|
@ -973,6 +973,7 @@ fn visit_instance_use<'tcx>(
|
||||
}
|
||||
}
|
||||
ty::InstanceDef::DropGlue(_, Some(_))
|
||||
| ty::InstanceDef::AsyncDropGlueCtorShim(..)
|
||||
| ty::InstanceDef::VTableShim(..)
|
||||
| ty::InstanceDef::ReifyShim(..)
|
||||
| ty::InstanceDef::ClosureOnceShim { .. }
|
||||
|
@ -625,7 +625,8 @@ fn characteristic_def_id_of_mono_item<'tcx>(
|
||||
| ty::InstanceDef::Virtual(..)
|
||||
| ty::InstanceDef::CloneShim(..)
|
||||
| ty::InstanceDef::ThreadLocalShim(..)
|
||||
| ty::InstanceDef::FnPtrAddrShim(..) => return None,
|
||||
| ty::InstanceDef::FnPtrAddrShim(..)
|
||||
| ty::InstanceDef::AsyncDropGlueCtorShim(..) => return None,
|
||||
};
|
||||
|
||||
// If this is a method, we want to put it into the same module as
|
||||
@ -769,7 +770,9 @@ fn mono_item_visibility<'tcx>(
|
||||
};
|
||||
|
||||
let def_id = match instance.def {
|
||||
InstanceDef::Item(def_id) | InstanceDef::DropGlue(def_id, Some(_)) => def_id,
|
||||
InstanceDef::Item(def_id)
|
||||
| InstanceDef::DropGlue(def_id, Some(_))
|
||||
| InstanceDef::AsyncDropGlueCtorShim(def_id, _) => def_id,
|
||||
|
||||
// We match the visibility of statics here
|
||||
InstanceDef::ThreadLocalShim(def_id) => {
|
||||
|
@ -807,7 +807,10 @@ impl<'tcx> Stable<'tcx> for ty::Instance<'tcx> {
|
||||
| ty::InstanceDef::ThreadLocalShim(..)
|
||||
| ty::InstanceDef::DropGlue(..)
|
||||
| ty::InstanceDef::CloneShim(..)
|
||||
| ty::InstanceDef::FnPtrShim(..) => stable_mir::mir::mono::InstanceKind::Shim,
|
||||
| ty::InstanceDef::FnPtrShim(..)
|
||||
| ty::InstanceDef::AsyncDropGlueCtorShim(..) => {
|
||||
stable_mir::mir::mono::InstanceKind::Shim
|
||||
}
|
||||
};
|
||||
stable_mir::mir::mono::Instance { def, kind }
|
||||
}
|
||||
|
@ -424,6 +424,16 @@ symbols! {
|
||||
async_call_mut,
|
||||
async_call_once,
|
||||
async_closure,
|
||||
async_destruct,
|
||||
async_drop,
|
||||
async_drop_chain,
|
||||
async_drop_defer,
|
||||
async_drop_either,
|
||||
async_drop_fuse,
|
||||
async_drop_in_place,
|
||||
async_drop_noop,
|
||||
async_drop_slice,
|
||||
async_drop_surface_drop_in_place,
|
||||
async_fn,
|
||||
async_fn_in_trait,
|
||||
async_fn_kind_helper,
|
||||
@ -825,6 +835,7 @@ symbols! {
|
||||
fadd_fast,
|
||||
fake_variadic,
|
||||
fallback,
|
||||
fallback_surface_drop,
|
||||
fdiv_algebraic,
|
||||
fdiv_fast,
|
||||
feature,
|
||||
@ -1786,6 +1797,7 @@ symbols! {
|
||||
sub_assign,
|
||||
sub_with_overflow,
|
||||
suggestion,
|
||||
surface_async_drop_in_place,
|
||||
sym,
|
||||
sync,
|
||||
synthetic,
|
||||
|
@ -55,7 +55,9 @@ pub(super) fn mangle<'tcx>(
|
||||
printer
|
||||
.print_def_path(
|
||||
def_id,
|
||||
if let ty::InstanceDef::DropGlue(_, _) = instance.def {
|
||||
if let ty::InstanceDef::DropGlue(_, _) | ty::InstanceDef::AsyncDropGlueCtorShim(_, _) =
|
||||
instance.def
|
||||
{
|
||||
// Add the name of the dropped type to the symbol name
|
||||
&*instance.args
|
||||
} else {
|
||||
|
@ -240,6 +240,11 @@ pub(super) trait GoalKind<'tcx>:
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx>;
|
||||
|
||||
fn consider_builtin_async_destruct_candidate(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx>;
|
||||
|
||||
fn consider_builtin_destruct_candidate(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
@ -520,6 +525,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
G::consider_builtin_coroutine_candidate(self, goal)
|
||||
} else if lang_items.discriminant_kind_trait() == Some(trait_def_id) {
|
||||
G::consider_builtin_discriminant_kind_candidate(self, goal)
|
||||
} else if lang_items.async_destruct_trait() == Some(trait_def_id) {
|
||||
G::consider_builtin_async_destruct_candidate(self, goal)
|
||||
} else if lang_items.destruct_trait() == Some(trait_def_id) {
|
||||
G::consider_builtin_destruct_candidate(self, goal)
|
||||
} else if lang_items.transmute_trait() == Some(trait_def_id) {
|
||||
|
@ -814,6 +814,59 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
|
||||
})
|
||||
}
|
||||
|
||||
fn consider_builtin_async_destruct_candidate(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx> {
|
||||
let self_ty = goal.predicate.self_ty();
|
||||
let discriminant_ty = match *self_ty.kind() {
|
||||
ty::Bool
|
||||
| ty::Char
|
||||
| ty::Int(..)
|
||||
| ty::Uint(..)
|
||||
| ty::Float(..)
|
||||
| ty::Array(..)
|
||||
| ty::RawPtr(..)
|
||||
| ty::Ref(..)
|
||||
| ty::FnDef(..)
|
||||
| ty::FnPtr(..)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Infer(ty::IntVar(..) | ty::FloatVar(..))
|
||||
| ty::Coroutine(..)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Pat(..)
|
||||
| ty::Never
|
||||
| ty::Adt(_, _)
|
||||
| ty::Str
|
||||
| ty::Slice(_)
|
||||
| ty::Dynamic(_, _, _)
|
||||
| ty::Tuple(_)
|
||||
| ty::Error(_) => self_ty.async_destructor_ty(ecx.tcx(), goal.param_env),
|
||||
|
||||
// We do not call `Ty::discriminant_ty` on alias, param, or placeholder
|
||||
// types, which return `<self_ty as DiscriminantKind>::Discriminant`
|
||||
// (or ICE in the case of placeholders). Projecting a type to itself
|
||||
// is never really productive.
|
||||
ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
|
||||
| ty::Foreign(..)
|
||||
| ty::Bound(..) => bug!(
|
||||
"unexpected self ty `{:?}` when normalizing `<T as AsyncDestruct>::AsyncDestructor`",
|
||||
goal.predicate.self_ty()
|
||||
),
|
||||
};
|
||||
|
||||
ecx.probe_misc_candidate("builtin async destruct").enter(|ecx| {
|
||||
ecx.eq(goal.param_env, goal.predicate.term, discriminant_ty.into())
|
||||
.expect("expected goal term to be fully unconstrained");
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
})
|
||||
}
|
||||
|
||||
fn consider_builtin_destruct_candidate(
|
||||
_ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
|
@ -544,6 +544,18 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}
|
||||
|
||||
fn consider_builtin_async_destruct_candidate(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx> {
|
||||
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
// `AsyncDestruct` is automatically implemented for every type.
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}
|
||||
|
||||
fn consider_builtin_destruct_candidate(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
|
@ -1074,6 +1074,42 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
|
||||
| ty::Infer(..)
|
||||
| ty::Error(_) => false,
|
||||
}
|
||||
} else if lang_items.async_destruct_trait() == Some(trait_ref.def_id) {
|
||||
match self_ty.kind() {
|
||||
ty::Bool
|
||||
| ty::Char
|
||||
| ty::Int(_)
|
||||
| ty::Uint(_)
|
||||
| ty::Float(_)
|
||||
| ty::Adt(..)
|
||||
| ty::Str
|
||||
| ty::Array(..)
|
||||
| ty::Slice(_)
|
||||
| ty::RawPtr(..)
|
||||
| ty::Ref(..)
|
||||
| ty::FnDef(..)
|
||||
| ty::FnPtr(..)
|
||||
| ty::Dynamic(..)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(..)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Pat(..)
|
||||
| ty::Never
|
||||
| ty::Tuple(..)
|
||||
| ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true,
|
||||
|
||||
// type parameters, opaques, and unnormalized projections don't have
|
||||
// a known discriminant and may need to be normalized further or rely
|
||||
// on param env for async destructor projections
|
||||
ty::Param(_)
|
||||
| ty::Foreign(_)
|
||||
| ty::Alias(..)
|
||||
| ty::Bound(..)
|
||||
| ty::Placeholder(..)
|
||||
| ty::Infer(..)
|
||||
| ty::Error(_) => false,
|
||||
}
|
||||
} else if lang_items.pointee_trait() == Some(trait_ref.def_id) {
|
||||
let tail = selcx.tcx().struct_tail_with_normalize(
|
||||
self_ty,
|
||||
@ -1488,15 +1524,20 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
|
||||
) -> Progress<'tcx> {
|
||||
let tcx = selcx.tcx();
|
||||
let self_ty = obligation.predicate.self_ty();
|
||||
let args = tcx.mk_args(&[self_ty.into()]);
|
||||
let lang_items = tcx.lang_items();
|
||||
let item_def_id = obligation.predicate.def_id;
|
||||
let trait_def_id = tcx.trait_of_item(item_def_id).unwrap();
|
||||
let args = tcx.mk_args(&[self_ty.into()]);
|
||||
let (term, obligations) = if lang_items.discriminant_kind_trait() == Some(trait_def_id) {
|
||||
let discriminant_def_id = tcx.require_lang_item(LangItem::Discriminant, None);
|
||||
assert_eq!(discriminant_def_id, item_def_id);
|
||||
|
||||
(self_ty.discriminant_ty(tcx).into(), Vec::new())
|
||||
} else if lang_items.async_destruct_trait() == Some(trait_def_id) {
|
||||
let destructor_def_id = tcx.associated_item_def_ids(trait_def_id)[0];
|
||||
assert_eq!(destructor_def_id, item_def_id);
|
||||
|
||||
(self_ty.async_destructor_ty(tcx, obligation.param_env).into(), Vec::new())
|
||||
} else if lang_items.pointee_trait() == Some(trait_def_id) {
|
||||
let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None);
|
||||
assert_eq!(metadata_def_id, item_def_id);
|
||||
|
@ -81,6 +81,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
} else if lang_items.discriminant_kind_trait() == Some(def_id) {
|
||||
// `DiscriminantKind` is automatically implemented for every type.
|
||||
candidates.vec.push(BuiltinCandidate { has_nested: false });
|
||||
} else if lang_items.async_destruct_trait() == Some(def_id) {
|
||||
// `AsyncDestruct` is automatically implemented for every type.
|
||||
candidates.vec.push(BuiltinCandidate { has_nested: false });
|
||||
} else if lang_items.pointee_trait() == Some(def_id) {
|
||||
// `Pointee` is automatically implemented for every type.
|
||||
candidates.vec.push(BuiltinCandidate { has_nested: false });
|
||||
|
@ -22,6 +22,17 @@ fn is_unpin_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>)
|
||||
is_item_raw(tcx, query, LangItem::Unpin)
|
||||
}
|
||||
|
||||
fn has_surface_async_drop_raw<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
|
||||
) -> bool {
|
||||
is_item_raw(tcx, query, LangItem::AsyncDrop)
|
||||
}
|
||||
|
||||
fn has_surface_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
|
||||
is_item_raw(tcx, query, LangItem::Drop)
|
||||
}
|
||||
|
||||
fn is_item_raw<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
|
||||
@ -34,5 +45,13 @@ fn is_item_raw<'tcx>(
|
||||
}
|
||||
|
||||
pub(crate) fn provide(providers: &mut Providers) {
|
||||
*providers = Providers { is_copy_raw, is_sized_raw, is_freeze_raw, is_unpin_raw, ..*providers };
|
||||
*providers = Providers {
|
||||
is_copy_raw,
|
||||
is_sized_raw,
|
||||
is_freeze_raw,
|
||||
is_unpin_raw,
|
||||
has_surface_async_drop_raw,
|
||||
has_surface_drop_raw,
|
||||
..*providers
|
||||
};
|
||||
}
|
||||
|
@ -54,6 +54,47 @@ fn resolve_instance<'tcx>(
|
||||
debug!(" => trivial drop glue");
|
||||
ty::InstanceDef::DropGlue(def_id, None)
|
||||
}
|
||||
} else if Some(def_id) == tcx.lang_items().async_drop_in_place_fn() {
|
||||
let ty = args.type_at(0);
|
||||
|
||||
match *ty.kind() {
|
||||
ty::Array(..)
|
||||
| ty::Slice(_)
|
||||
| ty::Tuple(_)
|
||||
| ty::Bool
|
||||
| ty::Char
|
||||
| ty::Int(_)
|
||||
| ty::Uint(_)
|
||||
| ty::Float(_)
|
||||
| ty::Str
|
||||
| ty::RawPtr(_, _)
|
||||
| ty::Ref(..)
|
||||
| ty::FnDef(..)
|
||||
| ty::FnPtr(..)
|
||||
| ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
|
||||
| ty::Adt(..)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Pat(..)
|
||||
| ty::Never
|
||||
| ty::Coroutine(..) => {}
|
||||
|
||||
ty::Param(_)
|
||||
| ty::Error(_)
|
||||
| ty::Dynamic(..)
|
||||
| ty::Alias(..)
|
||||
| ty::Infer(ty::TyVar(_))
|
||||
| ty::Bound(..)
|
||||
| ty::Foreign(_)
|
||||
| ty::Placeholder(_)
|
||||
| ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
|
||||
debug!(" => async drop glue ctor");
|
||||
ty::InstanceDef::AsyncDropGlueCtorShim(def_id, ty)
|
||||
} else {
|
||||
debug!(" => free item");
|
||||
// FIXME(effects): we may want to erase the effect param if that is present on this item.
|
||||
|
271
library/core/src/future/async_drop.rs
Normal file
271
library/core/src/future/async_drop.rs
Normal file
@ -0,0 +1,271 @@
|
||||
#![unstable(feature = "async_drop", issue = "none")]
|
||||
|
||||
use crate::fmt;
|
||||
use crate::future::{Future, IntoFuture};
|
||||
use crate::intrinsics::discriminant_value;
|
||||
use crate::marker::{DiscriminantKind, PhantomPinned};
|
||||
use crate::mem::MaybeUninit;
|
||||
use crate::pin::Pin;
|
||||
use crate::task::{ready, Context, Poll};
|
||||
|
||||
/// Asynchronously drops a value by running `AsyncDrop::async_drop`
|
||||
/// on a value and its fields recursively.
|
||||
#[unstable(feature = "async_drop", issue = "none")]
|
||||
pub fn async_drop<T>(value: T) -> AsyncDropOwning<T> {
|
||||
AsyncDropOwning { value: MaybeUninit::new(value), dtor: None, _pinned: PhantomPinned }
|
||||
}
|
||||
|
||||
/// A future returned by the [`async_drop`].
|
||||
#[unstable(feature = "async_drop", issue = "none")]
|
||||
pub struct AsyncDropOwning<T> {
|
||||
value: MaybeUninit<T>,
|
||||
dtor: Option<AsyncDropInPlace<T>>,
|
||||
_pinned: PhantomPinned,
|
||||
}
|
||||
|
||||
#[unstable(feature = "async_drop", issue = "none")]
|
||||
impl<T> fmt::Debug for AsyncDropOwning<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("AsyncDropOwning").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "async_drop", issue = "none")]
|
||||
impl<T> Future for AsyncDropOwning<T> {
|
||||
type Output = ();
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
// SAFETY: Self is pinned thus it is ok to store references to self
|
||||
unsafe {
|
||||
let this = self.get_unchecked_mut();
|
||||
let dtor = Pin::new_unchecked(
|
||||
this.dtor.get_or_insert_with(|| async_drop_in_place(this.value.as_mut_ptr())),
|
||||
);
|
||||
// AsyncDestuctors are idempotent so Self gets idempotency as well
|
||||
dtor.poll(cx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[lang = "async_drop_in_place"]
|
||||
#[allow(unconditional_recursion)]
|
||||
// FIXME: Consider if `#[rustc_diagnostic_item = "ptr_drop_in_place"]` is needed?
|
||||
unsafe fn async_drop_in_place_raw<T: ?Sized>(
|
||||
to_drop: *mut T,
|
||||
) -> <T as AsyncDestruct>::AsyncDestructor {
|
||||
// Code here does not matter - this is replaced by the
|
||||
// real async drop glue constructor by the compiler.
|
||||
|
||||
// SAFETY: see comment above
|
||||
unsafe { async_drop_in_place_raw(to_drop) }
|
||||
}
|
||||
|
||||
/// Creates the asynchronous destructor of the pointed-to value.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Behavior is undefined if any of the following conditions are violated:
|
||||
///
|
||||
/// * `to_drop` must be [valid](crate::ptr#safety) for both reads and writes.
|
||||
///
|
||||
/// * `to_drop` must be properly aligned, even if `T` has size 0.
|
||||
///
|
||||
/// * `to_drop` must be nonnull, even if `T` has size 0.
|
||||
///
|
||||
/// * The value `to_drop` points to must be valid for async dropping,
|
||||
/// which may mean it must uphold additional invariants. These
|
||||
/// invariants depend on the type of the value being dropped. For
|
||||
/// instance, when dropping a Box, the box's pointer to the heap must
|
||||
/// be valid.
|
||||
///
|
||||
/// * While `async_drop_in_place` is executing or the returned async
|
||||
/// destructor is alive, the only way to access parts of `to_drop`
|
||||
/// is through the `self: Pin<&mut Self>` references supplied to
|
||||
/// the `AsyncDrop::async_drop` methods that `async_drop_in_place`
|
||||
/// or `AsyncDropInPlace<T>::poll` invokes. This usually means the
|
||||
/// returned future stores the `to_drop` pointer and user is required
|
||||
/// to guarantee that dropped value doesn't move.
|
||||
///
|
||||
#[unstable(feature = "async_drop", issue = "none")]
|
||||
pub unsafe fn async_drop_in_place<T: ?Sized>(to_drop: *mut T) -> AsyncDropInPlace<T> {
|
||||
// SAFETY: `async_drop_in_place_raw` has the same safety requirements
|
||||
unsafe { AsyncDropInPlace(async_drop_in_place_raw(to_drop)) }
|
||||
}
|
||||
|
||||
/// A future returned by the [`async_drop_in_place`].
|
||||
#[unstable(feature = "async_drop", issue = "none")]
|
||||
pub struct AsyncDropInPlace<T: ?Sized>(<T as AsyncDestruct>::AsyncDestructor);
|
||||
|
||||
#[unstable(feature = "async_drop", issue = "none")]
|
||||
impl<T: ?Sized> fmt::Debug for AsyncDropInPlace<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("AsyncDropInPlace").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "async_drop", issue = "none")]
|
||||
impl<T: ?Sized> Future for AsyncDropInPlace<T> {
|
||||
type Output = ();
|
||||
|
||||
#[inline(always)]
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
// SAFETY: This code simply forwards poll call to the inner future
|
||||
unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().0) }.poll(cx)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(zetanumbers): Add same restrictions on AsyncDrop impls as
|
||||
// with Drop impls
|
||||
/// Custom code within the asynchronous destructor.
|
||||
#[unstable(feature = "async_drop", issue = "none")]
|
||||
#[lang = "async_drop"]
|
||||
pub trait AsyncDrop {
|
||||
/// A future returned by the [`AsyncDrop::async_drop`] to be part
|
||||
/// of the async destructor.
|
||||
#[unstable(feature = "async_drop", issue = "none")]
|
||||
type Dropper<'a>: Future<Output = ()>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
/// Constructs the asynchronous destructor for this type.
|
||||
#[unstable(feature = "async_drop", issue = "none")]
|
||||
fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_>;
|
||||
}
|
||||
|
||||
#[lang = "async_destruct"]
|
||||
#[rustc_deny_explicit_impl(implement_via_object = false)]
|
||||
trait AsyncDestruct {
|
||||
type AsyncDestructor: Future<Output = ()>;
|
||||
}
|
||||
|
||||
/// Basically calls `AsyncDrop::async_drop` with pointer. Used to simplify
|
||||
/// generation of the code for `async_drop_in_place_raw`
|
||||
#[lang = "surface_async_drop_in_place"]
|
||||
async unsafe fn surface_async_drop_in_place<T: AsyncDrop + ?Sized>(ptr: *mut T) {
|
||||
// SAFETY: We call this from async drop `async_drop_in_place_raw`
|
||||
// which has the same safety requirements
|
||||
unsafe { <T as AsyncDrop>::async_drop(Pin::new_unchecked(&mut *ptr)).await }
|
||||
}
|
||||
|
||||
/// Basically calls `Drop::drop` with pointer. Used to simplify generation
|
||||
/// of the code for `async_drop_in_place_raw`
|
||||
#[allow(drop_bounds)]
|
||||
#[lang = "async_drop_surface_drop_in_place"]
|
||||
async unsafe fn surface_drop_in_place<T: Drop + ?Sized>(ptr: *mut T) {
|
||||
// SAFETY: We call this from async drop `async_drop_in_place_raw`
|
||||
// which has the same safety requirements
|
||||
unsafe { crate::ops::fallback_surface_drop(&mut *ptr) }
|
||||
}
|
||||
|
||||
/// Wraps a future to continue outputing `Poll::Ready(())` once after
|
||||
/// wrapped future completes by returning `Poll::Ready(())` on poll. This
|
||||
/// is useful for constructing async destructors to guarantee this
|
||||
/// "fuse" property
|
||||
struct Fuse<T> {
|
||||
inner: Option<T>,
|
||||
}
|
||||
|
||||
#[lang = "async_drop_fuse"]
|
||||
fn fuse<T>(inner: T) -> Fuse<T> {
|
||||
Fuse { inner: Some(inner) }
|
||||
}
|
||||
|
||||
impl<T> Future for Fuse<T>
|
||||
where
|
||||
T: Future<Output = ()>,
|
||||
{
|
||||
type Output = ();
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
// SAFETY: pin projection into `self.inner`
|
||||
unsafe {
|
||||
let this = self.get_unchecked_mut();
|
||||
if let Some(inner) = &mut this.inner {
|
||||
ready!(Pin::new_unchecked(inner).poll(cx));
|
||||
this.inner = None;
|
||||
}
|
||||
}
|
||||
Poll::Ready(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Async destructor for arrays and slices.
|
||||
#[lang = "async_drop_slice"]
|
||||
async unsafe fn slice<T>(s: *mut [T]) {
|
||||
let len = s.len();
|
||||
let ptr = s.as_mut_ptr();
|
||||
for i in 0..len {
|
||||
// SAFETY: we iterate over elements of `s` slice
|
||||
unsafe { async_drop_in_place_raw(ptr.add(i)).await }
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a chain of two futures, which awaits them sequentially as
|
||||
/// a future.
|
||||
#[lang = "async_drop_chain"]
|
||||
async fn chain<F, G>(first: F, last: G)
|
||||
where
|
||||
F: IntoFuture<Output = ()>,
|
||||
G: IntoFuture<Output = ()>,
|
||||
{
|
||||
first.await;
|
||||
last.await;
|
||||
}
|
||||
|
||||
/// Basically a lazy version of `async_drop_in_place`. Returns a future
|
||||
/// that would call `AsyncDrop::async_drop` on a first poll.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Same as `async_drop_in_place` except is lazy to avoid creating
|
||||
/// multiple mutable refernces.
|
||||
#[lang = "async_drop_defer"]
|
||||
async unsafe fn defer<T: ?Sized>(to_drop: *mut T) {
|
||||
// SAFETY: same safety requirements as `async_drop_in_place`
|
||||
unsafe { async_drop_in_place(to_drop) }.await
|
||||
}
|
||||
|
||||
/// If `T`'s discriminant is equal to the stored one then awaits `M`
|
||||
/// otherwise awaits the `O`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// User should carefully manage returned future, since it would
|
||||
/// try creating an immutable referece from `this` and get pointee's
|
||||
/// discriminant.
|
||||
// FIXME(zetanumbers): Send and Sync impls
|
||||
#[lang = "async_drop_either"]
|
||||
async unsafe fn either<O: IntoFuture<Output = ()>, M: IntoFuture<Output = ()>, T>(
|
||||
other: O,
|
||||
matched: M,
|
||||
this: *mut T,
|
||||
discr: <T as DiscriminantKind>::Discriminant,
|
||||
) {
|
||||
// SAFETY: Guaranteed by the safety section of this funtion's documentation
|
||||
if unsafe { discriminant_value(&*this) } == discr {
|
||||
drop(other);
|
||||
matched.await
|
||||
} else {
|
||||
drop(matched);
|
||||
other.await
|
||||
}
|
||||
}
|
||||
|
||||
/// Used for noop async destructors. We don't use [`core::future::Ready`]
|
||||
/// because it panics after its second poll, which could be potentially
|
||||
/// bad if that would happen during the cleanup.
|
||||
#[derive(Clone, Copy)]
|
||||
struct Noop;
|
||||
|
||||
#[lang = "async_drop_noop"]
|
||||
fn noop() -> Noop {
|
||||
Noop
|
||||
}
|
||||
|
||||
impl Future for Noop {
|
||||
type Output = ();
|
||||
|
||||
fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
Poll::Ready(())
|
||||
}
|
||||
}
|
@ -12,6 +12,8 @@
|
||||
use crate::ptr::NonNull;
|
||||
use crate::task::Context;
|
||||
|
||||
#[cfg(not(bootstrap))]
|
||||
mod async_drop;
|
||||
mod future;
|
||||
mod into_future;
|
||||
mod join;
|
||||
@ -36,6 +38,10 @@ pub use ready::{ready, Ready};
|
||||
#[stable(feature = "future_poll_fn", since = "1.64.0")]
|
||||
pub use poll_fn::{poll_fn, PollFn};
|
||||
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "async_drop", issue = "none")]
|
||||
pub use async_drop::{async_drop, async_drop_in_place, AsyncDrop, AsyncDropInPlace};
|
||||
|
||||
/// This type is needed because:
|
||||
///
|
||||
/// a) Coroutines cannot implement `for<'a, 'b> Coroutine<&'a mut Context<'b>>`, so we need to pass
|
||||
|
@ -238,3 +238,11 @@ pub trait Drop {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
fn drop(&mut self);
|
||||
}
|
||||
|
||||
/// Fallback function to call surface level `Drop::drop` function
|
||||
#[cfg(not(bootstrap))]
|
||||
#[allow(drop_bounds)]
|
||||
#[lang = "fallback_surface_drop"]
|
||||
pub(crate) fn fallback_surface_drop<T: Drop + ?Sized>(x: &mut T) {
|
||||
<T as Drop>::drop(x)
|
||||
}
|
||||
|
@ -174,6 +174,9 @@ pub use self::deref::Receiver;
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::drop::Drop;
|
||||
|
||||
#[cfg(not(bootstrap))]
|
||||
pub(crate) use self::drop::fallback_surface_drop;
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::function::{Fn, FnMut, FnOnce};
|
||||
|
||||
|
191
src/tools/miri/tests/pass/async-drop.rs
Normal file
191
src/tools/miri/tests/pass/async-drop.rs
Normal file
@ -0,0 +1,191 @@
|
||||
//@revisions: stack tree
|
||||
//@compile-flags: -Zmiri-strict-provenance
|
||||
//@[tree]compile-flags: -Zmiri-tree-borrows
|
||||
#![feature(async_drop, impl_trait_in_assoc_type, noop_waker, async_closure)]
|
||||
#![allow(incomplete_features, dead_code)]
|
||||
|
||||
// FIXME(zetanumbers): consider AsyncDestruct::async_drop cleanup tests
|
||||
use core::future::{async_drop_in_place, AsyncDrop, Future};
|
||||
use core::hint::black_box;
|
||||
use core::mem::{self, ManuallyDrop};
|
||||
use core::pin::{pin, Pin};
|
||||
use core::task::{Context, Poll, Waker};
|
||||
|
||||
async fn test_async_drop<T>(x: T) {
|
||||
let mut x = mem::MaybeUninit::new(x);
|
||||
let dtor = pin!(unsafe { async_drop_in_place(x.as_mut_ptr()) });
|
||||
test_idempotency(dtor).await;
|
||||
}
|
||||
|
||||
fn test_idempotency<T>(mut x: Pin<&mut T>) -> impl Future<Output = ()> + '_
|
||||
where
|
||||
T: Future<Output = ()>,
|
||||
{
|
||||
core::future::poll_fn(move |cx| {
|
||||
assert_eq!(x.as_mut().poll(cx), Poll::Ready(()));
|
||||
assert_eq!(x.as_mut().poll(cx), Poll::Ready(()));
|
||||
Poll::Ready(())
|
||||
})
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let waker = Waker::noop();
|
||||
let mut cx = Context::from_waker(&waker);
|
||||
|
||||
let i = 13;
|
||||
let fut = pin!(async {
|
||||
test_async_drop(Int(0)).await;
|
||||
test_async_drop(AsyncInt(0)).await;
|
||||
test_async_drop([AsyncInt(1), AsyncInt(2)]).await;
|
||||
test_async_drop((AsyncInt(3), AsyncInt(4))).await;
|
||||
test_async_drop(5).await;
|
||||
let j = 42;
|
||||
test_async_drop(&i).await;
|
||||
test_async_drop(&j).await;
|
||||
test_async_drop(AsyncStruct { b: AsyncInt(8), a: AsyncInt(7), i: 6 }).await;
|
||||
test_async_drop(ManuallyDrop::new(AsyncInt(9))).await;
|
||||
|
||||
let foo = AsyncInt(10);
|
||||
test_async_drop(AsyncReference { foo: &foo }).await;
|
||||
|
||||
let foo = AsyncInt(11);
|
||||
test_async_drop(|| {
|
||||
black_box(foo);
|
||||
let foo = AsyncInt(10);
|
||||
foo
|
||||
})
|
||||
.await;
|
||||
|
||||
test_async_drop(AsyncEnum::A(AsyncInt(12))).await;
|
||||
test_async_drop(AsyncEnum::B(SyncInt(13))).await;
|
||||
|
||||
test_async_drop(SyncInt(14)).await;
|
||||
test_async_drop(SyncThenAsync { i: 15, a: AsyncInt(16), b: SyncInt(17), c: AsyncInt(18) })
|
||||
.await;
|
||||
|
||||
let async_drop_fut = pin!(core::future::async_drop(AsyncInt(19)));
|
||||
test_idempotency(async_drop_fut).await;
|
||||
|
||||
let foo = AsyncInt(20);
|
||||
test_async_drop(async || {
|
||||
black_box(foo);
|
||||
let foo = AsyncInt(19);
|
||||
// Await point there, but this is async closure so it's fine
|
||||
black_box(core::future::ready(())).await;
|
||||
foo
|
||||
})
|
||||
.await;
|
||||
|
||||
test_async_drop(AsyncUnion { signed: 21 }).await;
|
||||
});
|
||||
let res = fut.poll(&mut cx);
|
||||
assert_eq!(res, Poll::Ready(()));
|
||||
}
|
||||
|
||||
struct AsyncInt(i32);
|
||||
|
||||
impl AsyncDrop for AsyncInt {
|
||||
type Dropper<'a> = impl Future<Output = ()>;
|
||||
|
||||
fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {
|
||||
async move {
|
||||
println!("AsyncInt::Dropper::poll: {}", self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SyncInt(i32);
|
||||
|
||||
impl Drop for SyncInt {
|
||||
fn drop(&mut self) {
|
||||
println!("SyncInt::drop: {}", self.0);
|
||||
}
|
||||
}
|
||||
|
||||
struct SyncThenAsync {
|
||||
i: i32,
|
||||
a: AsyncInt,
|
||||
b: SyncInt,
|
||||
c: AsyncInt,
|
||||
}
|
||||
|
||||
impl Drop for SyncThenAsync {
|
||||
fn drop(&mut self) {
|
||||
println!("SyncThenAsync::drop: {}", self.i);
|
||||
}
|
||||
}
|
||||
|
||||
struct AsyncReference<'a> {
|
||||
foo: &'a AsyncInt,
|
||||
}
|
||||
|
||||
impl AsyncDrop for AsyncReference<'_> {
|
||||
type Dropper<'a> = impl Future<Output = ()> where Self: 'a;
|
||||
|
||||
fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {
|
||||
async move {
|
||||
println!("AsyncReference::Dropper::poll: {}", self.foo.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Int(i32);
|
||||
|
||||
struct AsyncStruct {
|
||||
i: i32,
|
||||
a: AsyncInt,
|
||||
b: AsyncInt,
|
||||
}
|
||||
|
||||
impl AsyncDrop for AsyncStruct {
|
||||
type Dropper<'a> = impl Future<Output = ()>;
|
||||
|
||||
fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {
|
||||
async move {
|
||||
println!("AsyncStruct::Dropper::poll: {}", self.i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum AsyncEnum {
|
||||
A(AsyncInt),
|
||||
B(SyncInt),
|
||||
}
|
||||
|
||||
impl AsyncDrop for AsyncEnum {
|
||||
type Dropper<'a> = impl Future<Output = ()>;
|
||||
|
||||
fn async_drop(mut self: Pin<&mut Self>) -> Self::Dropper<'_> {
|
||||
async move {
|
||||
let new_self = match &*self {
|
||||
AsyncEnum::A(foo) => {
|
||||
println!("AsyncEnum(A)::Dropper::poll: {}", foo.0);
|
||||
AsyncEnum::B(SyncInt(foo.0))
|
||||
}
|
||||
AsyncEnum::B(foo) => {
|
||||
println!("AsyncEnum(B)::Dropper::poll: {}", foo.0);
|
||||
AsyncEnum::A(AsyncInt(foo.0))
|
||||
}
|
||||
};
|
||||
mem::forget(mem::replace(&mut *self, new_self));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(zetanumbers): Disallow types with `AsyncDrop` in unions
|
||||
union AsyncUnion {
|
||||
signed: i32,
|
||||
unsigned: u32,
|
||||
}
|
||||
|
||||
impl AsyncDrop for AsyncUnion {
|
||||
type Dropper<'a> = impl Future<Output = ()>;
|
||||
|
||||
fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {
|
||||
async move {
|
||||
println!("AsyncUnion::Dropper::poll: {}, {}", unsafe { self.signed }, unsafe {
|
||||
self.unsigned
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
22
src/tools/miri/tests/pass/async-drop.stack.stdout
Normal file
22
src/tools/miri/tests/pass/async-drop.stack.stdout
Normal file
@ -0,0 +1,22 @@
|
||||
AsyncInt::Dropper::poll: 0
|
||||
AsyncInt::Dropper::poll: 1
|
||||
AsyncInt::Dropper::poll: 2
|
||||
AsyncInt::Dropper::poll: 3
|
||||
AsyncInt::Dropper::poll: 4
|
||||
AsyncStruct::Dropper::poll: 6
|
||||
AsyncInt::Dropper::poll: 7
|
||||
AsyncInt::Dropper::poll: 8
|
||||
AsyncReference::Dropper::poll: 10
|
||||
AsyncInt::Dropper::poll: 11
|
||||
AsyncEnum(A)::Dropper::poll: 12
|
||||
SyncInt::drop: 12
|
||||
AsyncEnum(B)::Dropper::poll: 13
|
||||
AsyncInt::Dropper::poll: 13
|
||||
SyncInt::drop: 14
|
||||
SyncThenAsync::drop: 15
|
||||
AsyncInt::Dropper::poll: 16
|
||||
SyncInt::drop: 17
|
||||
AsyncInt::Dropper::poll: 18
|
||||
AsyncInt::Dropper::poll: 19
|
||||
AsyncInt::Dropper::poll: 20
|
||||
AsyncUnion::Dropper::poll: 21, 21
|
22
src/tools/miri/tests/pass/async-drop.tree.stdout
Normal file
22
src/tools/miri/tests/pass/async-drop.tree.stdout
Normal file
@ -0,0 +1,22 @@
|
||||
AsyncInt::Dropper::poll: 0
|
||||
AsyncInt::Dropper::poll: 1
|
||||
AsyncInt::Dropper::poll: 2
|
||||
AsyncInt::Dropper::poll: 3
|
||||
AsyncInt::Dropper::poll: 4
|
||||
AsyncStruct::Dropper::poll: 6
|
||||
AsyncInt::Dropper::poll: 7
|
||||
AsyncInt::Dropper::poll: 8
|
||||
AsyncReference::Dropper::poll: 10
|
||||
AsyncInt::Dropper::poll: 11
|
||||
AsyncEnum(A)::Dropper::poll: 12
|
||||
SyncInt::drop: 12
|
||||
AsyncEnum(B)::Dropper::poll: 13
|
||||
AsyncInt::Dropper::poll: 13
|
||||
SyncInt::drop: 14
|
||||
SyncThenAsync::drop: 15
|
||||
AsyncInt::Dropper::poll: 16
|
||||
SyncInt::drop: 17
|
||||
AsyncInt::Dropper::poll: 18
|
||||
AsyncInt::Dropper::poll: 19
|
||||
AsyncInt::Dropper::poll: 20
|
||||
AsyncUnion::Dropper::poll: 21, 21
|
197
tests/ui/async-await/async-drop.rs
Normal file
197
tests/ui/async-await/async-drop.rs
Normal file
@ -0,0 +1,197 @@
|
||||
//@ run-pass
|
||||
//@ check-run-results
|
||||
|
||||
#![feature(async_drop, impl_trait_in_assoc_type, noop_waker, async_closure)]
|
||||
#![allow(incomplete_features, dead_code)]
|
||||
|
||||
//@ edition: 2021
|
||||
|
||||
// FIXME(zetanumbers): consider AsyncDestruct::async_drop cleanup tests
|
||||
use core::future::{async_drop_in_place, AsyncDrop, Future};
|
||||
use core::hint::black_box;
|
||||
use core::mem::{self, ManuallyDrop};
|
||||
use core::pin::{pin, Pin};
|
||||
use core::task::{Context, Poll, Waker};
|
||||
|
||||
async fn test_async_drop<T>(x: T) {
|
||||
let mut x = mem::MaybeUninit::new(x);
|
||||
let dtor = pin!(unsafe { async_drop_in_place(x.as_mut_ptr()) });
|
||||
test_idempotency(dtor).await;
|
||||
}
|
||||
|
||||
fn test_idempotency<T>(mut x: Pin<&mut T>) -> impl Future<Output = ()> + '_
|
||||
where
|
||||
T: Future<Output = ()>,
|
||||
{
|
||||
core::future::poll_fn(move |cx| {
|
||||
assert_eq!(x.as_mut().poll(cx), Poll::Ready(()));
|
||||
assert_eq!(x.as_mut().poll(cx), Poll::Ready(()));
|
||||
Poll::Ready(())
|
||||
})
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let waker = Waker::noop();
|
||||
let mut cx = Context::from_waker(&waker);
|
||||
|
||||
let i = 13;
|
||||
let fut = pin!(async {
|
||||
test_async_drop(Int(0)).await;
|
||||
test_async_drop(AsyncInt(0)).await;
|
||||
test_async_drop([AsyncInt(1), AsyncInt(2)]).await;
|
||||
test_async_drop((AsyncInt(3), AsyncInt(4))).await;
|
||||
test_async_drop(5).await;
|
||||
let j = 42;
|
||||
test_async_drop(&i).await;
|
||||
test_async_drop(&j).await;
|
||||
test_async_drop(AsyncStruct { b: AsyncInt(8), a: AsyncInt(7), i: 6 }).await;
|
||||
test_async_drop(ManuallyDrop::new(AsyncInt(9))).await;
|
||||
|
||||
let foo = AsyncInt(10);
|
||||
test_async_drop(AsyncReference { foo: &foo }).await;
|
||||
|
||||
let foo = AsyncInt(11);
|
||||
test_async_drop(|| {
|
||||
black_box(foo);
|
||||
let foo = AsyncInt(10);
|
||||
foo
|
||||
}).await;
|
||||
|
||||
test_async_drop(AsyncEnum::A(AsyncInt(12))).await;
|
||||
test_async_drop(AsyncEnum::B(SyncInt(13))).await;
|
||||
|
||||
test_async_drop(SyncInt(14)).await;
|
||||
test_async_drop(SyncThenAsync {
|
||||
i: 15,
|
||||
a: AsyncInt(16),
|
||||
b: SyncInt(17),
|
||||
c: AsyncInt(18),
|
||||
}).await;
|
||||
|
||||
let async_drop_fut = pin!(core::future::async_drop(AsyncInt(19)));
|
||||
test_idempotency(async_drop_fut).await;
|
||||
|
||||
let foo = AsyncInt(20);
|
||||
test_async_drop(async || {
|
||||
black_box(foo);
|
||||
let foo = AsyncInt(19);
|
||||
// Await point there, but this is async closure so it's fine
|
||||
black_box(core::future::ready(())).await;
|
||||
foo
|
||||
}).await;
|
||||
|
||||
test_async_drop(AsyncUnion { signed: 21 }).await;
|
||||
});
|
||||
let res = fut.poll(&mut cx);
|
||||
assert_eq!(res, Poll::Ready(()));
|
||||
}
|
||||
|
||||
struct AsyncInt(i32);
|
||||
|
||||
impl AsyncDrop for AsyncInt {
|
||||
type Dropper<'a> = impl Future<Output = ()>;
|
||||
|
||||
fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {
|
||||
async move {
|
||||
println!("AsyncInt::Dropper::poll: {}", self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SyncInt(i32);
|
||||
|
||||
impl Drop for SyncInt {
|
||||
fn drop(&mut self) {
|
||||
println!("SyncInt::drop: {}", self.0);
|
||||
}
|
||||
}
|
||||
|
||||
struct SyncThenAsync {
|
||||
i: i32,
|
||||
a: AsyncInt,
|
||||
b: SyncInt,
|
||||
c: AsyncInt,
|
||||
}
|
||||
|
||||
impl Drop for SyncThenAsync {
|
||||
fn drop(&mut self) {
|
||||
println!("SyncThenAsync::drop: {}", self.i);
|
||||
}
|
||||
}
|
||||
|
||||
struct AsyncReference<'a> {
|
||||
foo: &'a AsyncInt,
|
||||
}
|
||||
|
||||
impl AsyncDrop for AsyncReference<'_> {
|
||||
type Dropper<'a> = impl Future<Output = ()> where Self: 'a;
|
||||
|
||||
fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {
|
||||
async move {
|
||||
println!("AsyncReference::Dropper::poll: {}", self.foo.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Int(i32);
|
||||
|
||||
struct AsyncStruct {
|
||||
i: i32,
|
||||
a: AsyncInt,
|
||||
b: AsyncInt,
|
||||
}
|
||||
|
||||
impl AsyncDrop for AsyncStruct {
|
||||
type Dropper<'a> = impl Future<Output = ()>;
|
||||
|
||||
fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {
|
||||
async move {
|
||||
println!("AsyncStruct::Dropper::poll: {}", self.i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum AsyncEnum {
|
||||
A(AsyncInt),
|
||||
B(SyncInt),
|
||||
}
|
||||
|
||||
impl AsyncDrop for AsyncEnum {
|
||||
type Dropper<'a> = impl Future<Output = ()>;
|
||||
|
||||
fn async_drop(mut self: Pin<&mut Self>) -> Self::Dropper<'_> {
|
||||
async move {
|
||||
let new_self = match &*self {
|
||||
AsyncEnum::A(foo) => {
|
||||
println!("AsyncEnum(A)::Dropper::poll: {}", foo.0);
|
||||
AsyncEnum::B(SyncInt(foo.0))
|
||||
}
|
||||
AsyncEnum::B(foo) => {
|
||||
println!("AsyncEnum(B)::Dropper::poll: {}", foo.0);
|
||||
AsyncEnum::A(AsyncInt(foo.0))
|
||||
}
|
||||
};
|
||||
mem::forget(mem::replace(&mut *self, new_self));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(zetanumbers): Disallow types with `AsyncDrop` in unions
|
||||
union AsyncUnion {
|
||||
signed: i32,
|
||||
unsigned: u32,
|
||||
}
|
||||
|
||||
impl AsyncDrop for AsyncUnion {
|
||||
type Dropper<'a> = impl Future<Output = ()>;
|
||||
|
||||
fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {
|
||||
async move {
|
||||
println!(
|
||||
"AsyncUnion::Dropper::poll: {}, {}",
|
||||
unsafe { self.signed },
|
||||
unsafe { self.unsigned },
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
22
tests/ui/async-await/async-drop.run.stdout
Normal file
22
tests/ui/async-await/async-drop.run.stdout
Normal file
@ -0,0 +1,22 @@
|
||||
AsyncInt::Dropper::poll: 0
|
||||
AsyncInt::Dropper::poll: 1
|
||||
AsyncInt::Dropper::poll: 2
|
||||
AsyncInt::Dropper::poll: 3
|
||||
AsyncInt::Dropper::poll: 4
|
||||
AsyncStruct::Dropper::poll: 6
|
||||
AsyncInt::Dropper::poll: 7
|
||||
AsyncInt::Dropper::poll: 8
|
||||
AsyncReference::Dropper::poll: 10
|
||||
AsyncInt::Dropper::poll: 11
|
||||
AsyncEnum(A)::Dropper::poll: 12
|
||||
SyncInt::drop: 12
|
||||
AsyncEnum(B)::Dropper::poll: 13
|
||||
AsyncInt::Dropper::poll: 13
|
||||
SyncInt::drop: 14
|
||||
SyncThenAsync::drop: 15
|
||||
AsyncInt::Dropper::poll: 16
|
||||
SyncInt::drop: 17
|
||||
AsyncInt::Dropper::poll: 18
|
||||
AsyncInt::Dropper::poll: 19
|
||||
AsyncInt::Dropper::poll: 20
|
||||
AsyncUnion::Dropper::poll: 21, 21
|
Loading…
Reference in New Issue
Block a user