Skip to content

Commit a5631d1

Browse files
committed
Async drop support for dyn traits
1 parent 26b4adc commit a5631d1

34 files changed

Lines changed: 742 additions & 231 deletions

compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1475,6 +1475,9 @@ fn build_vtable_type_di_node<'ll, 'tcx>(
14751475
ty::VtblEntry::MetadataDropInPlace => {
14761476
("drop_in_place".to_string(), void_pointer_type_di_node)
14771477
}
1478+
ty::VtblEntry::MetadataAsyncDropInPlace => {
1479+
("async_drop_in_place".to_string(), void_pointer_type_di_node)
1480+
}
14781481
ty::VtblEntry::Method(_) => {
14791482
// Note: This code does not try to give a proper name to each method
14801483
// because their might be multiple methods with the same name

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -624,7 +624,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
624624
// \-------/
625625
//
626626
let virtual_drop = Instance {
627-
def: ty::InstanceKind::Virtual(drop_fn.def_id(), 0), // idx 0: the drop function
627+
def: ty::InstanceKind::Virtual(
628+
drop_fn.def_id(),
629+
ty::COMMON_VTABLE_ENTRIES_DROPINPLACE,
630+
),
628631
args: drop_fn.args,
629632
};
630633
debug!("ty = {:?}", ty);
@@ -678,6 +681,87 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
678681
)
679682
}
680683

684+
#[tracing::instrument(level = "trace", skip(self, helper, bx))]
685+
fn codegen_async_drop_dyn(
686+
&mut self,
687+
helper: TerminatorCodegenHelper<'tcx>,
688+
bx: &mut Bx,
689+
source_info: &mir::SourceInfo,
690+
dropee: mir::Place<'tcx>,
691+
destination: mir::Place<'tcx>,
692+
target: mir::BasicBlock,
693+
unwind: mir::UnwindAction,
694+
mergeable_succ: bool,
695+
) -> MergingSucc {
696+
let ty = dropee.ty(self.mir, bx.tcx()).ty;
697+
let ty = self.monomorphize(ty);
698+
let drop_fn = Instance::resolve_async_drop_in_place_dyn(bx.tcx(), ty).unwrap();
699+
let place = self.codegen_place(bx, dropee.as_ref());
700+
701+
let (args1, args2);
702+
let mut args = if let Some(llextra) = place.val.llextra {
703+
args2 = [place.val.llval, llextra];
704+
&args2[..]
705+
} else {
706+
args1 = [place.val.llval];
707+
&args1[..]
708+
};
709+
710+
let (drop_fn, fn_abi, drop_instance) = match ty.kind() {
711+
ty::Dynamic(_, _, ty::Dyn) => {
712+
let virtual_drop = Instance {
713+
def: ty::InstanceKind::Virtual(
714+
drop_fn.def_id(),
715+
ty::COMMON_VTABLE_ENTRIES_ASYNCDROPINPLACE,
716+
),
717+
args: drop_fn.args,
718+
};
719+
let fn_abi = bx.fn_abi_of_instance(virtual_drop, ty::List::empty());
720+
let vtable = args[1];
721+
// Truncate vtable off of args list
722+
args = &args[..1];
723+
(
724+
meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_ASYNCDROPINPLACE)
725+
.get_optional_fn(bx, vtable, ty, fn_abi),
726+
fn_abi,
727+
virtual_drop,
728+
)
729+
}
730+
_ => bug!("Non-virtual call for async drop terminator (ty is not dyn or dyn*)"),
731+
};
732+
// We generate a null check for the drop_fn. This saves a bunch of relocations being
733+
// generated for no-op drops.
734+
// FIXME: do we need it for dyn async drop?
735+
{
736+
let is_not_null = bx.append_sibling_block("is_not_null");
737+
let llty = bx.fn_ptr_backend_type(fn_abi);
738+
let null = bx.const_null(llty);
739+
let non_null =
740+
bx.icmp(base::bin_op_to_icmp_predicate(mir::BinOp::Ne, false), drop_fn, null);
741+
bx.cond_br(non_null, is_not_null, helper.llbb_with_cleanup(self, target));
742+
bx.switch_to_block(is_not_null);
743+
self.set_debug_loc(bx, *source_info);
744+
}
745+
assert!(!fn_abi.ret.is_indirect());
746+
let mut llargs = Vec::new();
747+
let return_dest = self.make_return_dest(bx, destination, &fn_abi.ret, &mut llargs);
748+
assert!(llargs.is_empty());
749+
750+
helper.do_call(
751+
self,
752+
bx,
753+
fn_abi,
754+
drop_fn,
755+
args,
756+
Some((return_dest, target)),
757+
unwind,
758+
&[],
759+
Some(drop_instance),
760+
CallKind::Normal,
761+
false,
762+
)
763+
}
764+
681765
fn codegen_assert_terminator(
682766
&mut self,
683767
helper: TerminatorCodegenHelper<'tcx>,
@@ -898,6 +982,21 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
898982

899983
let (instance, mut llfn) = match *callee.layout.ty.kind() {
900984
ty::FnDef(def_id, generic_args) => {
985+
if bx.tcx().is_lang_item(def_id, LangItem::AsyncDropInPlaceDyn) {
986+
let mir::Operand::Move(dropee) = args[0].node else {
987+
bug!();
988+
};
989+
return self.codegen_async_drop_dyn(
990+
helper,
991+
bx,
992+
&terminator.source_info,
993+
dropee,
994+
destination,
995+
target.unwrap(),
996+
unwind,
997+
mergeable_succ,
998+
);
999+
}
9011000
let instance = ty::Instance::expect_resolve(
9021001
bx.tcx(),
9031002
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
@@ -812,6 +812,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
812812
// only supports calling `VtblEntry::Method`; it would choke on a `MetadataDropInPlace`. So
813813
// instead we do the virtual call stuff ourselves. It's easier here than in `eval_fn_call`
814814
// since we can just get a place of the underlying type and use `mplace_to_ref`.
815+
// FIXME: Support AsyncDrop (async_drop_in_place) here
815816
let place = match place.layout.ty.kind() {
816817
ty::Dynamic(data, _, ty::Dyn) => {
817818
// 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
@@ -193,6 +193,8 @@ language_item_table! {
193193
Destruct, sym::destruct, destruct_trait, Target::Trait, GenericRequirement::None;
194194
AsyncDrop, sym::async_drop, async_drop_trait, Target::Trait, GenericRequirement::None;
195195
AsyncDropInPlace, sym::async_drop_in_place, async_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1);
196+
AsyncDropInPlaceDyn, sym::async_drop_in_place_dyn, async_drop_in_place_dyn_fn, Target::Fn, GenericRequirement::Exact(1);
197+
AsyncDropInPlaceSelf, sym::async_drop_in_place_self, async_drop_in_place_self_fn, Target::Fn, GenericRequirement::None;
196198

197199
CoerceUnsized, sym::coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1);
198200
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
@@ -753,6 +753,30 @@ impl<'tcx> Instance<'tcx> {
753753
)
754754
}
755755

756+
// async_drop_in_place, with return value converted into Pin<Box<Future>>, for usage in vtable.
757+
// May returns None for core lib compilation (before lang item definition in alloc lib).
758+
pub fn resolve_async_drop_in_place_dyn(
759+
tcx: TyCtxt<'tcx>,
760+
ty: Ty<'tcx>,
761+
) -> Option<ty::Instance<'tcx>> {
762+
// `async_drop_in_place<T>::{closure}` is a special case, because such coroutine is its async drop future itself.
763+
// To drop this coroutine we need to continue poll it.
764+
// So, it async drop constructor function in vtable returns its address (from argument), boxed and pinned.
765+
let (item, args) = if ty.is_async_drop_in_place_coroutine(tcx) {
766+
(LangItem::AsyncDropInPlaceSelf, tcx.mk_args(&[]))
767+
} else {
768+
(LangItem::AsyncDropInPlaceDyn, tcx.mk_args(&[ty.into()]))
769+
};
770+
let Some(def_id) = tcx.lang_items().get(item) else { return None };
771+
Some(Instance::expect_resolve(
772+
tcx,
773+
ty::TypingEnv::fully_monomorphized(),
774+
def_id,
775+
args,
776+
ty.ty_adt_def().and_then(|adt| tcx.hir_span_if_local(adt.did())).unwrap_or(DUMMY_SP),
777+
))
778+
}
779+
756780
pub fn resolve_async_drop_in_place_poll(
757781
tcx: TyCtxt<'tcx>,
758782
def_id: DefId,

compiler/rustc_middle/src/ty/vtable.rs

Lines changed: 24 additions & 4 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,13 +45,18 @@ 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;
51-
pub const COMMON_VTABLE_ENTRIES_ALIGN: usize = 2;
57+
pub const COMMON_VTABLE_ENTRIES_ASYNCDROPINPLACE: usize = 1;
58+
pub const COMMON_VTABLE_ENTRIES_SIZE: usize = 2;
59+
pub const COMMON_VTABLE_ENTRIES_ALIGN: usize = 3;
5260

5361
// Note that we don't have access to a self type here, this has to be purely based on the trait (and
5462
// supertrait) definitions. That means we can't call into the same vtable_entries code since that
@@ -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)