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
40 changed files with 1921 additions and 20 deletions
|
@ -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
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue