Skip to content

Commit 54fef57

Browse files
committed
Async drop support for dyn traits
1 parent 674999c commit 54fef57

15 files changed

Lines changed: 680 additions & 186 deletions

File tree

compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1471,6 +1471,9 @@ fn build_vtable_type_di_node<'ll, 'tcx>(
14711471
ty::VtblEntry::MetadataDropInPlace => {
14721472
("drop_in_place".to_string(), void_pointer_type_di_node)
14731473
}
1474+
ty::VtblEntry::MetadataAsyncDropInPlace => {
1475+
("async_drop_in_place".to_string(), void_pointer_type_di_node)
1476+
}
14741477
ty::VtblEntry::Method(_) => {
14751478
// Note: This code does not try to give a proper name to each method
14761479
// because their might be multiple methods with the same name

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 125 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
610610
// \-------/
611611
//
612612
let virtual_drop = Instance {
613-
def: ty::InstanceKind::Virtual(drop_fn.def_id(), 0), // idx 0: the drop function
613+
def: ty::InstanceKind::Virtual(
614+
drop_fn.def_id(),
615+
ty::COMMON_VTABLE_ENTRIES_DROPINPLACE,
616+
),
614617
args: drop_fn.args,
615618
};
616619
debug!("ty = {:?}", ty);
@@ -652,7 +655,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
652655
//
653656
// SO THEN WE CAN USE THE ABOVE CODE.
654657
let virtual_drop = Instance {
655-
def: ty::InstanceKind::Virtual(drop_fn.def_id(), 0), // idx 0: the drop function
658+
def: ty::InstanceKind::Virtual(
659+
drop_fn.def_id(),
660+
ty::COMMON_VTABLE_ENTRIES_DROPINPLACE,
661+
),
656662
args: drop_fn.args,
657663
};
658664
debug!("ty = {:?}", ty);
@@ -707,6 +713,108 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
707713
)
708714
}
709715

716+
#[tracing::instrument(level = "trace", skip(self, helper, bx))]
717+
fn codegen_async_drop_dyn(
718+
&mut self,
719+
helper: TerminatorCodegenHelper<'tcx>,
720+
bx: &mut Bx,
721+
source_info: &mir::SourceInfo,
722+
dropee: mir::Place<'tcx>,
723+
destination: mir::Place<'tcx>,
724+
target: mir::BasicBlock,
725+
unwind: mir::UnwindAction,
726+
mergeable_succ: bool,
727+
) -> MergingSucc {
728+
let ty = dropee.ty(self.mir, bx.tcx()).ty;
729+
let ty = self.monomorphize(ty);
730+
let drop_fn = Instance::resolve_async_drop_in_place_dyn(bx.tcx(), ty).unwrap();
731+
let place = self.codegen_place(bx, dropee.as_ref());
732+
733+
let (args1, args2);
734+
let mut args = if let Some(llextra) = place.val.llextra {
735+
args2 = [place.val.llval, llextra];
736+
&args2[..]
737+
} else {
738+
args1 = [place.val.llval];
739+
&args1[..]
740+
};
741+
742+
let (drop_fn, fn_abi, drop_instance) = match ty.kind() {
743+
ty::Dynamic(_, _, ty::Dyn) => {
744+
let virtual_drop = Instance {
745+
def: ty::InstanceKind::Virtual(
746+
drop_fn.def_id(),
747+
ty::COMMON_VTABLE_ENTRIES_ASYNCDROPINPLACE,
748+
),
749+
args: drop_fn.args,
750+
};
751+
let fn_abi = bx.fn_abi_of_instance(virtual_drop, ty::List::empty());
752+
let vtable = args[1];
753+
// Truncate vtable off of args list
754+
args = &args[..1];
755+
(
756+
meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_ASYNCDROPINPLACE)
757+
.get_optional_fn(bx, vtable, ty, fn_abi),
758+
fn_abi,
759+
virtual_drop,
760+
)
761+
}
762+
ty::Dynamic(_, _, ty::DynStar) => {
763+
let virtual_drop = Instance {
764+
def: ty::InstanceKind::Virtual(
765+
drop_fn.def_id(),
766+
ty::COMMON_VTABLE_ENTRIES_ASYNCDROPINPLACE,
767+
),
768+
args: drop_fn.args,
769+
};
770+
let fn_abi = bx.fn_abi_of_instance(virtual_drop, ty::List::empty());
771+
let meta_ptr = place.project_field(bx, 1);
772+
let meta = bx.load_operand(meta_ptr);
773+
// Truncate vtable off of args list
774+
assert!(args.len() == 1);
775+
args = &args[..1];
776+
debug!("args' = {:?}", args);
777+
(
778+
meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_ASYNCDROPINPLACE)
779+
.get_optional_fn(bx, meta.immediate(), ty, fn_abi),
780+
fn_abi,
781+
virtual_drop,
782+
)
783+
}
784+
_ => bug!("Non-virtual call for async drop terminator (ty is not dyn or dyn*)"),
785+
};
786+
// We generate a null check for the drop_fn. This saves a bunch of relocations being
787+
// generated for no-op drops.
788+
// FIXME: do we need it for dyn async drop?
789+
{
790+
let is_not_null = bx.append_sibling_block("is_not_null");
791+
let llty = bx.fn_ptr_backend_type(fn_abi);
792+
let null = bx.const_null(llty);
793+
let non_null =
794+
bx.icmp(base::bin_op_to_icmp_predicate(mir::BinOp::Ne, false), drop_fn, null);
795+
bx.cond_br(non_null, is_not_null, helper.llbb_with_cleanup(self, target));
796+
bx.switch_to_block(is_not_null);
797+
self.set_debug_loc(bx, *source_info);
798+
}
799+
assert!(!fn_abi.ret.is_indirect());
800+
let mut llargs = Vec::new();
801+
let return_dest = self.make_return_dest(bx, destination, &fn_abi.ret, &mut llargs);
802+
assert!(llargs.is_empty());
803+
804+
helper.do_call(
805+
self,
806+
bx,
807+
fn_abi,
808+
drop_fn,
809+
args,
810+
Some((return_dest, target)),
811+
unwind,
812+
&[],
813+
Some(drop_instance),
814+
false,
815+
)
816+
}
817+
710818
fn codegen_assert_terminator(
711819
&mut self,
712820
helper: TerminatorCodegenHelper<'tcx>,
@@ -907,6 +1015,21 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
9071015

9081016
let (instance, mut llfn) = match *callee.layout.ty.kind() {
9091017
ty::FnDef(def_id, generic_args) => {
1018+
if bx.tcx().is_lang_item(def_id, LangItem::AsyncDropInPlaceDyn) {
1019+
let mir::Operand::Move(dropee) = args[0].node else {
1020+
bug!();
1021+
};
1022+
return self.codegen_async_drop_dyn(
1023+
helper,
1024+
bx,
1025+
&terminator.source_info,
1026+
dropee,
1027+
destination,
1028+
target.unwrap(),
1029+
unwind,
1030+
mergeable_succ,
1031+
);
1032+
}
9101033
let instance = ty::Instance::expect_resolve(
9111034
bx.tcx(),
9121035
bx.typing_env(),

compiler/rustc_const_eval/src/interpret/call.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
825825
// only supports calling `VtblEntry::Method`; it would choke on a `MetadataDropInPlace`. So
826826
// instead we do the virtual call stuff ourselves. It's easier here than in `eval_fn_call`
827827
// since we can just get a place of the underlying type and use `mplace_to_ref`.
828+
// FIXME: Support AsyncDrop (async_drop_in_place) here
828829
let place = match place.layout.ty.kind() {
829830
ty::Dynamic(data, _, ty::Dyn) => {
830831
// Dropping a trait object. Need to find actual drop fn.

compiler/rustc_hir/src/lang_items.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@ language_item_table! {
191191
Destruct, sym::destruct, destruct_trait, Target::Trait, GenericRequirement::None;
192192
AsyncDrop, sym::async_drop, async_drop_trait, Target::Trait, GenericRequirement::None;
193193
AsyncDropInPlace, sym::async_drop_in_place, async_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1);
194+
AsyncDropInPlaceDyn, sym::async_drop_in_place_dyn, async_drop_in_place_dyn_fn, Target::Fn, GenericRequirement::Exact(1);
195+
AsyncDropInPlaceSelf, sym::async_drop_in_place_self, async_drop_in_place_self_fn, Target::Fn, GenericRequirement::None;
194196

195197
CoerceUnsized, sym::coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1);
196198
DispatchFromDyn, sym::dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1);

compiler/rustc_middle/src/ty/instance.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,30 @@ impl<'tcx> Instance<'tcx> {
809809
)
810810
}
811811

812+
// async_drop_in_place, with return value converted into Pin<Box<Future>>, for usage in vtable.
813+
// May returns None for core lib compilation (before lang item definition in alloc lib).
814+
pub fn resolve_async_drop_in_place_dyn(
815+
tcx: TyCtxt<'tcx>,
816+
ty: Ty<'tcx>,
817+
) -> Option<ty::Instance<'tcx>> {
818+
// `async_drop_in_place<T>::{closure}` is a special case, because such coroutine is its async drop future itself.
819+
// To drop this coroutine we need to continue poll it.
820+
// So, it async drop constructor function in vtable returns its address (from argument), boxed and pinned.
821+
let (item, args) = if ty.is_async_drop_in_place_coroutine(tcx) {
822+
(LangItem::AsyncDropInPlaceSelf, tcx.mk_args(&[]))
823+
} else {
824+
(LangItem::AsyncDropInPlaceDyn, tcx.mk_args(&[ty.into()]))
825+
};
826+
let Some(def_id) = tcx.lang_items().get(item) else { return None };
827+
Some(Instance::expect_resolve(
828+
tcx,
829+
ty::TypingEnv::fully_monomorphized(),
830+
def_id,
831+
args,
832+
ty.ty_adt_def().and_then(|adt| tcx.hir_span_if_local(adt.did())).unwrap_or(DUMMY_SP),
833+
))
834+
}
835+
812836
pub fn resolve_async_drop_in_place_poll(
813837
tcx: TyCtxt<'tcx>,
814838
def_id: DefId,

compiler/rustc_middle/src/ty/vtable.rs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ use crate::ty::{self, Instance, TraitRef, Ty, TyCtxt};
1313
pub enum VtblEntry<'tcx> {
1414
/// destructor of this type (used in vtable header)
1515
MetadataDropInPlace,
16+
/// async destructor of this type, function returns Pin<Box<Future>>> (used in vtable header)
17+
MetadataAsyncDropInPlace,
1618
/// layout size of this type (used in vtable header)
1719
MetadataSize,
1820
/// layout align of this type (used in vtable header)
@@ -31,6 +33,7 @@ impl<'tcx> fmt::Debug for VtblEntry<'tcx> {
3133
// so we implement this manually.
3234
match self {
3335
VtblEntry::MetadataDropInPlace => write!(f, "MetadataDropInPlace"),
36+
VtblEntry::MetadataAsyncDropInPlace => write!(f, "MetadataAsyncDropInPlace"),
3437
VtblEntry::MetadataSize => write!(f, "MetadataSize"),
3538
VtblEntry::MetadataAlign => write!(f, "MetadataAlign"),
3639
VtblEntry::Vacant => write!(f, "Vacant"),
@@ -42,12 +45,17 @@ impl<'tcx> fmt::Debug for VtblEntry<'tcx> {
4245

4346
// Needs to be associated with the `'tcx` lifetime
4447
impl<'tcx> TyCtxt<'tcx> {
45-
pub const COMMON_VTABLE_ENTRIES: &'tcx [VtblEntry<'tcx>] =
46-
&[VtblEntry::MetadataDropInPlace, VtblEntry::MetadataSize, VtblEntry::MetadataAlign];
48+
pub const COMMON_VTABLE_ENTRIES: &'tcx [VtblEntry<'tcx>] = &[
49+
VtblEntry::MetadataDropInPlace,
50+
VtblEntry::MetadataAsyncDropInPlace,
51+
VtblEntry::MetadataSize,
52+
VtblEntry::MetadataAlign,
53+
];
4754
}
4855

4956
pub const COMMON_VTABLE_ENTRIES_DROPINPLACE: usize = 0;
50-
pub const COMMON_VTABLE_ENTRIES_SIZE: usize = 1;
57+
pub const COMMON_VTABLE_ENTRIES_ASYNCDROPINPLACE: usize = 1;
58+
pub const COMMON_VTABLE_ENTRIES_SIZE: usize = 2;
5159
pub const COMMON_VTABLE_ENTRIES_ALIGN: usize = 2;
5260

5361
// Note that we don't have access to a self type here, this has to be purely based on the trait (and
@@ -129,6 +137,18 @@ pub(super) fn vtable_allocation_provider<'tcx>(
129137
Scalar::from_maybe_pointer(Pointer::null(), &tcx)
130138
}
131139
}
140+
VtblEntry::MetadataAsyncDropInPlace => {
141+
if tcx.features().async_drop()
142+
&& ty.needs_async_drop(tcx, ty::TypingEnv::fully_monomorphized())
143+
&& let Some(instance) = ty::Instance::resolve_async_drop_in_place_dyn(tcx, ty)
144+
{
145+
let fn_alloc_id = tcx.reserve_and_set_fn_alloc(instance, CTFE_ALLOC_SALT);
146+
let fn_ptr = Pointer::from(fn_alloc_id);
147+
Scalar::from_pointer(fn_ptr, &tcx)
148+
} else {
149+
Scalar::from_maybe_pointer(Pointer::null(), &tcx)
150+
}
151+
}
132152
VtblEntry::MetadataSize => Scalar::from_uint(size, ptr_size),
133153
VtblEntry::MetadataAlign => Scalar::from_uint(align, ptr_size),
134154
VtblEntry::Vacant => continue,

0 commit comments

Comments
 (0)