Auto merge of #121801 - zetanumbers:async_drop_glue, r=oli-obk
Add simple async drop glue generation This is a prototype of the async drop glue generation for some simple types. Async drop glue is intended to behave very similar to the regular drop glue except for being asynchronous. Currently it does not execute synchronous drops but only calls user implementations of `AsyncDrop::async_drop` associative function and awaits the returned future. It is not complete as it only recurses into arrays, slices, tuples, and structs and does not have same sensible restrictions as the old `Drop` trait implementation like having the same bounds as the type definition, while code assumes their existence (requires a future work). This current design uses a workaround as it does not create any custom async destructor state machine types for ADTs, but instead uses types defined in the std library called future combinators (deferred_async_drop, chain, ready_unit). Also I recommend reading my [explainer](https://zetanumbers.github.io/book/async-drop-design.html). This is a part of the [MCP: Low level components for async drop](https://github.com/rust-lang/compiler-team/issues/727) work. Feature completeness: - [x] `AsyncDrop` trait - [ ] `async_drop_in_place_raw`/async drop glue generation support for - [x] Trivially destructible types (integers, bools, floats, string slices, pointers, references, etc.) - [x] Arrays and slices (array pointer is unsized into slice pointer) - [x] ADTs (enums, structs, unions) - [x] tuple-like types (tuples, closures) - [ ] Dynamic types (`dyn Trait`, see explainer's [proposed design](https://github.com/zetanumbers/posts/blob/main/async-drop-design.md#async-drop-glue-for-dyn-trait)) - [ ] coroutines (https://github.com/rust-lang/rust/pull/123948) - [x] Async drop glue includes sync drop glue code - [x] Cleanup branch generation for `async_drop_in_place_raw` - [ ] Union rejects non-trivially async destructible fields - [ ] `AsyncDrop` implementation requires same bounds as type definition - [ ] Skip trivially destructible fields (optimization) - [ ] New [`TyKind::AdtAsyncDestructor`](https://github.com/zetanumbers/posts/blob/main/async-drop-design.md#adt-async-destructor-types) and get rid of combinators - [ ] [Synchronously undroppable types](https://github.com/zetanumbers/posts/blob/main/async-drop-design.md#exclusively-async-drop) - [ ] Automatic async drop at the end of the scope in async context
This commit is contained in:
commit
aca749eefc
44 changed files with 1916 additions and 24 deletions
|
@ -363,6 +363,24 @@ fn exported_symbols_provider_local(
|
|||
},
|
||||
));
|
||||
}
|
||||
MonoItem::Fn(Instance {
|
||||
def: InstanceDef::AsyncDropGlueCtorShim(def_id, Some(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.
|
||||
|
|
|
@ -835,7 +835,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
|
||||
let def = instance.map(|i| i.def);
|
||||
|
||||
if let Some(ty::InstanceDef::DropGlue(_, None)) = def {
|
||||
if let Some(
|
||||
ty::InstanceDef::DropGlue(_, None) | ty::InstanceDef::AsyncDropGlueCtorShim(_, None),
|
||||
) = def
|
||||
{
|
||||
// Empty drop glue; a no-op.
|
||||
let target = target.unwrap();
|
||||
return helper.funclet_br(self, bx, target, mergeable_succ);
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -1131,6 +1131,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(),
|
||||
|
|
|
@ -65,7 +65,9 @@ impl<'tcx> MonoItem<'tcx> {
|
|||
match instance.def {
|
||||
// "Normal" functions size estimate: the number of
|
||||
// statements, plus one for the terminator.
|
||||
InstanceDef::Item(..) | InstanceDef::DropGlue(..) => {
|
||||
InstanceDef::Item(..)
|
||||
| InstanceDef::DropGlue(..)
|
||||
| InstanceDef::AsyncDropGlueCtorShim(..) => {
|
||||
let mir = tcx.instance_mir(instance.def);
|
||||
mir.basic_blocks.iter().map(|bb| bb.statements.len() + 1).sum()
|
||||
}
|
||||
|
@ -406,7 +408,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(_, Some(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(),
|
||||
};
|
||||
|
||||
|
|
|
@ -350,12 +350,14 @@ macro_rules! make_mir_visitor {
|
|||
receiver_by_ref: _,
|
||||
} |
|
||||
ty::InstanceDef::CoroutineKindShim { coroutine_def_id: _def_id } |
|
||||
ty::InstanceDef::AsyncDropGlueCtorShim(_def_id, None) |
|
||||
ty::InstanceDef::DropGlue(_def_id, None) => {}
|
||||
|
||||
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, Some(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, Option<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(..)
|
||||
|
@ -279,6 +288,7 @@ impl<'tcx> InstanceDef<'tcx> {
|
|||
let def_id = match *self {
|
||||
ty::InstanceDef::Item(def) => def,
|
||||
ty::InstanceDef::DropGlue(_, Some(_)) => return false,
|
||||
ty::InstanceDef::AsyncDropGlueCtorShim(_, Some(_)) => return false,
|
||||
ty::InstanceDef::ThreadLocalShim(_) => return false,
|
||||
_ => return true,
|
||||
};
|
||||
|
@ -347,11 +357,13 @@ impl<'tcx> InstanceDef<'tcx> {
|
|||
| InstanceDef::ThreadLocalShim(..)
|
||||
| InstanceDef::FnPtrAddrShim(..)
|
||||
| InstanceDef::FnPtrShim(..)
|
||||
| InstanceDef::DropGlue(_, Some(_)) => false,
|
||||
| InstanceDef::DropGlue(_, Some(_))
|
||||
| InstanceDef::AsyncDropGlueCtorShim(_, Some(_)) => false,
|
||||
InstanceDef::ClosureOnceShim { .. }
|
||||
| InstanceDef::ConstructCoroutineInClosureShim { .. }
|
||||
| InstanceDef::CoroutineKindShim { .. }
|
||||
| InstanceDef::DropGlue(..)
|
||||
| InstanceDef::AsyncDropGlueCtorShim(..)
|
||||
| InstanceDef::Item(_)
|
||||
| InstanceDef::Intrinsic(..)
|
||||
| InstanceDef::ReifyShim(..)
|
||||
|
@ -396,6 +408,8 @@ 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(_, None) => write!(f, " - shim(None)"),
|
||||
InstanceDef::AsyncDropGlueCtorShim(_, Some(ty)) => write!(f, " - shim(Some({ty}))"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -638,6 +652,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,133 @@ 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> {
|
||||
if self.is_async_destructor_noop(tcx, param_env) || matches!(self.kind(), ty::Error(_)) {
|
||||
return Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop)
|
||||
.instantiate_identity();
|
||||
}
|
||||
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, 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());
|
||||
|
||||
let surface_drop = self.surface_async_dropper_ty(tcx, param_env).unwrap();
|
||||
|
||||
Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse)
|
||||
.instantiate(tcx, &[surface_drop.into()])
|
||||
}
|
||||
|
||||
ty::Bound(..)
|
||||
| ty::Foreign(_)
|
||||
| ty::Placeholder(_)
|
||||
| ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
|
||||
bug!("`async_destructor_ty` applied to unexpected type: {self:?}")
|
||||
}
|
||||
|
||||
_ => bug!("`async_destructor_ty` is not yet implemented for 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>>,
|
||||
{
|
||||
debug_assert!(!self.is_async_destructor_noop(tcx, param_env));
|
||||
|
||||
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();
|
||||
|
||||
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,98 @@ 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.could_have_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 could_have_surface_async_drop(self) -> bool {
|
||||
!self.is_async_destructor_trivially_noop()
|
||||
&& !matches!(
|
||||
self.kind(),
|
||||
ty::Tuple(_)
|
||||
| ty::Slice(_)
|
||||
| ty::Array(_, _)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(..)
|
||||
)
|
||||
}
|
||||
|
||||
/// Checks whether values of this type `T` implements the `Drop`
|
||||
/// trait.
|
||||
pub fn has_surface_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
|
||||
self.could_have_surface_drop() && tcx.has_surface_drop_raw(param_env.and(self))
|
||||
}
|
||||
|
||||
/// Fast path helper for testing if a type has `Drop` implementation.
|
||||
///
|
||||
/// Returning `false` means the type is known to not have `Drop`
|
||||
/// implementation. Returning `true` means nothing -- could be
|
||||
/// `Drop`, might not be.
|
||||
fn could_have_surface_drop(self) -> bool {
|
||||
!self.is_async_destructor_trivially_noop()
|
||||
&& !matches!(
|
||||
self.kind(),
|
||||
ty::Tuple(_)
|
||||
| ty::Slice(_)
|
||||
| ty::Array(_, _)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(..)
|
||||
)
|
||||
}
|
||||
|
||||
/// Checks whether values of this type `T` implement has noop async destructor.
|
||||
//
|
||||
// FIXME: implement optimization to make ADTs, which do not need drop,
|
||||
// to skip fields or to have noop async destructor.
|
||||
pub fn is_async_destructor_noop(
|
||||
self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> bool {
|
||||
self.is_async_destructor_trivially_noop()
|
||||
|| if let ty::Adt(adt_def, _) = self.kind() {
|
||||
(adt_def.is_union() || adt_def.is_payloadfree())
|
||||
&& !self.has_surface_async_drop(tcx, param_env)
|
||||
&& !self.has_surface_drop(tcx, param_env)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// 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() {
|
||||
ty::Int(_)
|
||||
| ty::Uint(_)
|
||||
| ty::Float(_)
|
||||
| ty::Bool
|
||||
| ty::Char
|
||||
| ty::Str
|
||||
| ty::Never
|
||||
| ty::Ref(..)
|
||||
| ty::RawPtr(..)
|
||||
| ty::FnDef(..)
|
||||
| ty::FnPtr(_) => true,
|
||||
ty::Tuple(tys) => tys.is_empty(),
|
||||
ty::Adt(adt_def, _) => adt_def.is_manually_drop(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
|
@ -1083,7 +1084,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(_, Some(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)
|
||||
}
|
||||
|
|
618
compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs
Normal file
618
compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs
Normal file
|
@ -0,0 +1,618 @@
|
|||
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, RETURN_PLACE,
|
||||
};
|
||||
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,
|
||||
ty: Option<Ty<'tcx>>,
|
||||
) -> Body<'tcx> {
|
||||
debug!("build_drop_shim(def_id={:?}, ty={:?})", def_id, ty);
|
||||
|
||||
AsyncDestructorCtorShimBuilder::new(tcx, def_id, 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: Option<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: Option<Ty<'tcx>>) -> Self {
|
||||
let args = if let Some(ty) = self_ty {
|
||||
tcx.mk_args(&[ty.into()])
|
||||
} else {
|
||||
ty::GenericArgs::identity_for_item(tcx, def_id)
|
||||
};
|
||||
let sig = tcx.fn_sig(def_id).instantiate(tcx, args);
|
||||
let sig = tcx.instantiate_bound_regions_with_erased(sig);
|
||||
let span = tcx.def_span(def_id);
|
||||
|
||||
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, Some(self_ty)) = (self.tcx, self.def_id, self.self_ty) else {
|
||||
return self.build_zst_output();
|
||||
};
|
||||
|
||||
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::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::Adt(adt_def, _) => {
|
||||
assert!(adt_def.is_union());
|
||||
match surface_drop_kind().unwrap() {
|
||||
SurfaceDropKind::Async => self.build_fused_async_surface(),
|
||||
SurfaceDropKind::Sync => self.build_fused_sync_surface(),
|
||||
}
|
||||
}
|
||||
|
||||
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:?}")
|
||||
}
|
||||
|
||||
_ => {
|
||||
bug!(
|
||||
"Building async destructor constructor shim is not yet implemented for 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_zst_output(mut self) -> Body<'tcx> {
|
||||
self.put_zst_output();
|
||||
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_()
|
||||
}
|
||||
|
||||
fn put_zst_output(&mut self) {
|
||||
let return_ty = self.locals[RETURN_PLACE].ty;
|
||||
self.put_operand(Operand::Constant(Box::new(ConstOperand {
|
||||
span: self.span,
|
||||
user_ty: None,
|
||||
const_: Const::zero_sized(return_ty),
|
||||
})));
|
||||
}
|
||||
|
||||
/// 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.unwrap().into()])
|
||||
}
|
||||
|
||||
fn combine_sync_surface(&mut self) -> Ty<'tcx> {
|
||||
self.apply_combinator(
|
||||
1,
|
||||
LangItem::AsyncDropSurfaceDropInPlace,
|
||||
&[self.self_ty.unwrap().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.unwrap().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(),
|
||||
)
|
||||
};
|
||||
#[cfg(debug_assertions)]
|
||||
if let Some(ty) = self.self_ty {
|
||||
debug_assert_eq!(
|
||||
output.ty(&self.locals, self.tcx),
|
||||
ty.async_destructor_ty(self.tcx, self.param_env),
|
||||
"output async destructor types did not match for type: {ty:?}",
|
||||
);
|
||||
}
|
||||
|
||||
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_PLACE.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
|
||||
}
|
||||
}
|
|
@ -966,13 +966,14 @@ fn visit_instance_use<'tcx>(
|
|||
ty::InstanceDef::ThreadLocalShim(..) => {
|
||||
bug!("{:?} being reified", instance);
|
||||
}
|
||||
ty::InstanceDef::DropGlue(_, None) => {
|
||||
ty::InstanceDef::DropGlue(_, None) | ty::InstanceDef::AsyncDropGlueCtorShim(_, None) => {
|
||||
// Don't need to emit noop drop glue if we are calling directly.
|
||||
if !is_direct_call {
|
||||
output.push(create_fn_mono_item(tcx, instance, source));
|
||||
}
|
||||
}
|
||||
ty::InstanceDef::DropGlue(_, Some(_))
|
||||
| ty::InstanceDef::AsyncDropGlueCtorShim(_, Some(_))
|
||||
| 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, Some(_)) => def_id,
|
||||
|
||||
// We match the visibility of statics here
|
||||
InstanceDef::ThreadLocalShim(def_id) => {
|
||||
|
@ -786,6 +789,7 @@ fn mono_item_visibility<'tcx>(
|
|||
| InstanceDef::ConstructCoroutineInClosureShim { .. }
|
||||
| InstanceDef::CoroutineKindShim { .. }
|
||||
| InstanceDef::DropGlue(..)
|
||||
| InstanceDef::AsyncDropGlueCtorShim(..)
|
||||
| InstanceDef::CloneShim(..)
|
||||
| InstanceDef::FnPtrAddrShim(..) => return Visibility::Hidden,
|
||||
};
|
||||
|
|
|
@ -500,6 +500,12 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
|
|||
matches!(instance.def, ty::InstanceDef::DropGlue(_, None))
|
||||
}
|
||||
|
||||
fn is_empty_async_drop_ctor_shim(&self, def: InstanceDef) -> bool {
|
||||
let tables = self.0.borrow_mut();
|
||||
let instance = tables.instances[def];
|
||||
matches!(instance.def, ty::InstanceDef::AsyncDropGlueCtorShim(_, None))
|
||||
}
|
||||
|
||||
fn mono_instance(&self, def_id: stable_mir::DefId) -> stable_mir::mir::mono::Instance {
|
||||
let mut tables = self.0.borrow_mut();
|
||||
let def_id = tables[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 }
|
||||
}
|
||||
|
|
|
@ -425,6 +425,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,
|
||||
|
@ -826,6 +836,7 @@ symbols! {
|
|||
fadd_fast,
|
||||
fake_variadic,
|
||||
fallback,
|
||||
fallback_surface_drop,
|
||||
fdiv_algebraic,
|
||||
fdiv_fast,
|
||||
feature,
|
||||
|
@ -1789,6 +1800,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 async_destructor_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::Never
|
||||
| ty::Adt(_, _)
|
||||
| ty::Str
|
||||
| ty::Slice(_)
|
||||
| ty::Tuple(_)
|
||||
| ty::Error(_) => self_ty.async_destructor_ty(ecx.tcx(), goal.param_env),
|
||||
|
||||
// We do not call `Ty::async_destructor_ty` on alias, param, or placeholder
|
||||
// types, which return `<self_ty as AsyncDestruct>::AsyncDestructor`
|
||||
// (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()
|
||||
),
|
||||
|
||||
ty::Pat(..) | ty::Dynamic(..) | ty::Coroutine(..) | ty::CoroutineWitness(..) => bug!(
|
||||
"`consider_builtin_async_destruct_candidate` is not yet implemented for type: {self_ty:?}"
|
||||
),
|
||||
};
|
||||
|
||||
ecx.probe_misc_candidate("builtin async destruct").enter(|ecx| {
|
||||
ecx.eq(goal.param_env, goal.predicate.term, async_destructor_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 async destructor 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,28 @@ 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);
|
||||
|
||||
if !ty.is_async_destructor_noop(tcx, param_env) {
|
||||
match *ty.kind() {
|
||||
ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(..)
|
||||
| ty::Tuple(..)
|
||||
| ty::Adt(..)
|
||||
| ty::Dynamic(..)
|
||||
| ty::Array(..)
|
||||
| ty::Slice(..) => {}
|
||||
// Async destructor ctor shims can only be built from ADTs.
|
||||
_ => return Ok(None),
|
||||
}
|
||||
debug!(" => nontrivial async drop glue ctor");
|
||||
ty::InstanceDef::AsyncDropGlueCtorShim(def_id, Some(ty))
|
||||
} else {
|
||||
debug!(" => trivial async drop glue ctor");
|
||||
ty::InstanceDef::AsyncDropGlueCtorShim(def_id, None)
|
||||
}
|
||||
} else {
|
||||
debug!(" => free item");
|
||||
// FIXME(effects): we may want to erase the effect param if that is present on this item.
|
||||
|
|
|
@ -158,6 +158,9 @@ pub trait Context {
|
|||
/// Check if this is an empty DropGlue shim.
|
||||
fn is_empty_drop_shim(&self, def: InstanceDef) -> bool;
|
||||
|
||||
/// Check if this is an empty AsyncDropGlueCtor shim.
|
||||
fn is_empty_async_drop_ctor_shim(&self, def: InstanceDef) -> bool;
|
||||
|
||||
/// Convert a non-generic crate item into an instance.
|
||||
/// This function will panic if the item is generic.
|
||||
fn mono_instance(&self, def_id: DefId) -> Instance;
|
||||
|
|
|
@ -157,7 +157,10 @@ impl Instance {
|
|||
/// When generating code for a Drop terminator, users can ignore an empty drop glue.
|
||||
/// These shims are only needed to generate a valid Drop call done via VTable.
|
||||
pub fn is_empty_shim(&self) -> bool {
|
||||
self.kind == InstanceKind::Shim && with(|cx| cx.is_empty_drop_shim(self.def))
|
||||
self.kind == InstanceKind::Shim
|
||||
&& with(|cx| {
|
||||
cx.is_empty_drop_shim(self.def) || cx.is_empty_async_drop_ctor_shim(self.def)
|
||||
})
|
||||
}
|
||||
|
||||
/// Try to constant evaluate the instance into a constant with the given type.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue