diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 177fc1deca12a..702ccc3e12317 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -675,7 +675,7 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro
TerminatorKind::SwitchInt { discr, targets: _ } => {
self.consume_operand(loc, (discr, span), flow_state);
}
- TerminatorKind::Drop { place, target: _, unwind: _, replace } => {
+ TerminatorKind::Drop { place, target: _, unwind: _, replace, drop: _, async_fut: _ } => {
debug!(
"visit_terminator_drop \
loc: {:?} term: {:?} place: {:?} span: {:?}",
diff --git a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs
index 5c9056272cc0d..524237683d009 100644
--- a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs
+++ b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs
@@ -99,7 +99,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'cx, 'tcx> {
TerminatorKind::SwitchInt { discr, targets: _ } => {
self.consume_operand(location, discr);
}
- TerminatorKind::Drop { place: drop_place, target: _, unwind: _, replace } => {
+ TerminatorKind::Drop { place: drop_place, target: _, unwind: _, replace, drop: _, async_fut: _ } => {
let write_kind =
if *replace { WriteKind::Replace } else { WriteKind::StorageDeadOrDrop };
self.access_place(
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index cf28e62177fb4..6caf158d6d9ca 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -1684,8 +1684,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
}
TerminatorKind::Unreachable => {}
- TerminatorKind::Drop { target, unwind, .. }
- | TerminatorKind::Assert { target, unwind, .. } => {
+ TerminatorKind::Drop { target, unwind, drop, .. } => {
+ self.assert_iscleanup(body, block_data, target, is_cleanup);
+ self.assert_iscleanup_unwind(body, block_data, unwind, is_cleanup);
+ if let Some(drop) = drop {
+ self.assert_iscleanup(body, block_data, drop, is_cleanup);
+ }
+ }
+ TerminatorKind::Assert { target, unwind, .. } => {
self.assert_iscleanup(body, block_data, target, is_cleanup);
self.assert_iscleanup_unwind(body, block_data, unwind, is_cleanup);
}
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index 0afd6d0e670b3..76fb793bc71a4 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -481,7 +481,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
| TerminatorKind::CoroutineDrop => {
bug!("shouldn't exist at codegen {:?}", bb_data.terminator());
}
- TerminatorKind::Drop { place, target, unwind: _, replace: _ } => {
+ TerminatorKind::Drop { place, target, unwind: _, replace: _, drop: _, async_fut: _ } => {
let drop_place = codegen_place(fx, *place);
crate::abi::codegen_drop(fx, source_info, drop_place);
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index e35b4029b4506..7667876725181 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -1232,7 +1232,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
MergingSucc::False
}
- mir::TerminatorKind::Drop { place, target, unwind, replace: _ } => {
+ mir::TerminatorKind::Drop { place, target, unwind, replace: _, drop: _, async_fut: _ } => {
self.codegen_drop_terminator(helper, bx, place, target, unwind, mergeable_succ())
}
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index 274ff25fb9b58..94b77ddd243c4 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -568,6 +568,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
RemainderByZero(op) => RemainderByZero(eval_to_int(op)?),
ResumedAfterReturn(coroutine_kind) => ResumedAfterReturn(*coroutine_kind),
ResumedAfterPanic(coroutine_kind) => ResumedAfterPanic(*coroutine_kind),
+ ResumedAfterDrop(coroutine_kind) => ResumedAfterDrop(*coroutine_kind),
MisalignedPointerDereference { ref required, ref found } => {
MisalignedPointerDereference {
required: eval_to_int(required)?,
diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs
index 7b993279f18e8..19c47f050228f 100644
--- a/compiler/rustc_const_eval/src/interpret/terminator.rs
+++ b/compiler/rustc_const_eval/src/interpret/terminator.rs
@@ -170,7 +170,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
}
- Drop { place, target, unwind, replace: _ } => {
+ Drop { place, target, unwind, replace: _, drop: _, async_fut: _ } => {
let frame = self.frame();
let ty = place.ty(&frame.body.local_decls, *self.tcx).ty;
let ty = self.subst_from_frame_and_normalize_erasing_regions(frame, ty)?;
@@ -542,6 +542,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
| ty::InstanceDef::ReifyShim(..)
| ty::InstanceDef::ClosureOnceShim { .. }
| ty::InstanceDef::FnPtrShim(..)
+ | ty::InstanceDef::FutureDropPollShim(..)
| ty::InstanceDef::DropGlue(..)
| ty::InstanceDef::CloneShim(..)
| ty::InstanceDef::FnPtrAddrShim(..)
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 5564902396eda..fe3e34a4fe931 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -363,9 +363,12 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
);
}
}
- TerminatorKind::Drop { target, unwind, .. } => {
+ TerminatorKind::Drop { target, unwind, drop, .. } => {
self.check_edge(location, *target, EdgeKind::Normal);
self.check_unwind_edge(location, *unwind);
+ if let Some(drop) = drop {
+ self.check_edge(location, *drop, EdgeKind::Normal);
+ }
}
TerminatorKind::Call { args, destination, target, unwind, .. } => {
if let Some(target) = target {
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 6eed2178ead8d..809b8f884f13f 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -355,6 +355,8 @@ declare_features! (
(unstable, associated_type_defaults, "1.2.0", Some(29661)),
/// Allows `async || body` closures.
(unstable, async_closure, "1.37.0", Some(62290)),
+ /// Allows implementing `AsyncDrop`.
+ (incomplete, async_drop, "CURRENT_RUSTC_VERSION", None),
/// Allows `#[track_caller]` on async functions.
(unstable, async_fn_track_caller, "1.73.0", Some(110011)),
/// Allows `for await` loops.
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index 9fb318e2ae70d..687d46d05b82c 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -163,6 +163,9 @@ 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::None;
+ AsyncDropInPlace, sym::async_drop_in_place, async_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1);
+ FutureDropPoll, sym::future_drop_poll, future_drop_poll_fn, Target::Fn, GenericRequirement::Exact(1);
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);
@@ -260,6 +263,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);
+ DropInPlaceFuture, sym::drop_in_place_future,drop_in_place_future_fn, Target::Fn, GenericRequirement::Minimum(1);
AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None;
Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1);
diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl
index 27d555d7e26c7..0b9367e73d8eb 100644
--- a/compiler/rustc_middle/messages.ftl
+++ b/compiler/rustc_middle/messages.ftl
@@ -5,15 +5,21 @@ middle_assert_async_resume_after_panic = `async fn` resumed after panicking
middle_assert_async_resume_after_return = `async fn` resumed after completion
+middle_assert_async_resume_after_drop = `async fn` resumed after async drop
+
middle_assert_coroutine_resume_after_panic = coroutine resumed after panicking
middle_assert_coroutine_resume_after_return = coroutine resumed after completion
+middle_assert_coroutine_resume_after_drop = coroutine resumed after async drop
+
middle_assert_divide_by_zero =
attempt to divide `{$val}` by zero
middle_assert_gen_resume_after_panic = `gen` fn or block cannot be further iterated on after it panicked
+middle_assert_gen_resume_after_drop = `gen` fn or block cannot be further iterated on after it async dropped
+
middle_assert_misaligned_ptr_deref =
misaligned pointer dereference: address must be a multiple of {$required} but is {$found}
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 36f5ba161d5f1..f6815ab1c9552 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -255,6 +255,9 @@ pub struct CoroutineInfo<'tcx> {
/// Coroutine drop glue.
pub coroutine_drop: Option
>,
+ /// Coroutine async drop glue.
+ pub coroutine_drop_async: Option>,
+
/// The layout of a coroutine. Produced by the state transformation.
pub coroutine_layout: Option>,
@@ -275,6 +278,7 @@ impl<'tcx> CoroutineInfo<'tcx> {
yield_ty: Some(yield_ty),
resume_ty: Some(resume_ty),
coroutine_drop: None,
+ coroutine_drop_async: None,
coroutine_layout: None,
}
}
@@ -578,6 +582,11 @@ impl<'tcx> Body<'tcx> {
self.coroutine.as_ref().and_then(|coroutine| coroutine.coroutine_drop.as_ref())
}
+ #[inline]
+ pub fn coroutine_drop_async(&self) -> Option<&Body<'tcx>> {
+ self.coroutine.as_ref().and_then(|coroutine| coroutine.coroutine_drop_async.as_ref())
+ }
+
#[inline]
pub fn coroutine_kind(&self) -> Option {
self.coroutine.as_ref().map(|coroutine| coroutine.coroutine_kind)
diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs
index 91fdf0b312991..a8f20e206918e 100644
--- a/compiler/rustc_middle/src/mir/mono.rs
+++ b/compiler/rustc_middle/src/mir/mono.rs
@@ -402,6 +402,7 @@ impl<'tcx> CodegenUnit<'tcx> {
| InstanceDef::FnPtrShim(..)
| InstanceDef::Virtual(..)
| InstanceDef::ClosureOnceShim { .. }
+ | InstanceDef::FutureDropPollShim(..)
| InstanceDef::DropGlue(..)
| InstanceDef::CloneShim(..)
| InstanceDef::ThreadLocalShim(..)
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index 3b60eba2dfe0a..e010980c0da50 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -855,7 +855,9 @@ impl<'tcx> TerminatorKind<'tcx> {
Call { target: None, unwind: _, .. } => vec![],
Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()],
Yield { drop: None, .. } => vec!["resume".into()],
- Drop { unwind: UnwindAction::Cleanup(_), .. } => vec!["return".into(), "unwind".into()],
+ Drop { unwind: UnwindAction::Cleanup(_), drop: Some(_), .. } => vec!["return".into(), "unwind".into(), "drop".into() ],
+ Drop { unwind: UnwindAction::Cleanup(_), drop: None, .. } => vec!["return".into(), "unwind".into()],
+ Drop { unwind: _, drop: Some(_), .. } => vec!["return".into(), "drop".into() ],
Drop { unwind: _, .. } => vec!["return".into()],
Assert { unwind: UnwindAction::Cleanup(_), .. } => {
vec!["success".into(), "unwind".into()]
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index a5c229879a748..87846e2b8533d 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -656,7 +656,16 @@ pub enum TerminatorKind<'tcx> {
/// The `replace` flag indicates whether this terminator was created as part of an assignment.
/// This should only be used for diagnostic purposes, and does not have any operational
/// meaning.
- Drop { place: Place<'tcx>, target: BasicBlock, unwind: UnwindAction, replace: bool },
+ Drop {
+ place: Place<'tcx>,
+ target: BasicBlock,
+ unwind: UnwindAction,
+ replace: bool,
+ /// Cleanup to be done if the coroutine is dropped at this suspend point (for async drop).
+ drop: Option,
+ /// Prepared async future local (for async drop)
+ async_fut: Option,
+ },
/// Roughly speaking, evaluates the `func` operand and the arguments, and starts execution of
/// the referred to function. The operand types must match the argument types of the function.
@@ -888,6 +897,7 @@ pub enum AssertKind {
RemainderByZero(O),
ResumedAfterReturn(CoroutineKind),
ResumedAfterPanic(CoroutineKind),
+ ResumedAfterDrop(CoroutineKind),
MisalignedPointerDereference { required: O, found: O },
}
diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs
index fdbbf468ecea2..e142c0bd4e134 100644
--- a/compiler/rustc_middle/src/mir/terminator.rs
+++ b/compiler/rustc_middle/src/mir/terminator.rs
@@ -177,6 +177,16 @@ impl AssertKind {
ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
"`gen fn` should just keep returning `None` after panicking"
}
+ ResumedAfterDrop(CoroutineKind::Coroutine(_)) => "coroutine resumed after async drop",
+ ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
+ "`async fn` resumed after async drop"
+ }
+ ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
+ "`async gen fn` resumed after async drop"
+ }
+ ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
+ "`gen fn` should just keep returning `None` after async drop"
+ }
BoundsCheck { .. } | MisalignedPointerDereference { .. } => {
bug!("Unexpected AssertKind")
@@ -287,6 +297,18 @@ impl AssertKind {
ResumedAfterPanic(CoroutineKind::Coroutine(_)) => {
middle_assert_coroutine_resume_after_panic
}
+ ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
+ middle_assert_async_resume_after_drop
+ }
+ ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
+ todo!()
+ }
+ ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
+ middle_assert_gen_resume_after_drop
+ }
+ ResumedAfterDrop(CoroutineKind::Coroutine(_)) => {
+ middle_assert_coroutine_resume_after_drop
+ }
MisalignedPointerDereference { .. } => middle_assert_misaligned_ptr_deref,
}
@@ -320,7 +342,7 @@ impl AssertKind {
add!("left", format!("{left:#?}"));
add!("right", format!("{right:#?}"));
}
- ResumedAfterReturn(_) | ResumedAfterPanic(_) => {}
+ ResumedAfterReturn(_) | ResumedAfterPanic(_) | ResumedAfterDrop(_) => {}
MisalignedPointerDereference { required, found } => {
add!("required", format!("{required:#?}"));
add!("found", format!("{found:#?}"));
@@ -337,7 +359,8 @@ pub struct Terminator<'tcx> {
pub type Successors<'a> = impl DoubleEndedIterator- + 'a;
pub type SuccessorsMut<'a> =
- iter::Chain, slice::IterMut<'a, BasicBlock>>;
+ iter::Chain,
+ iter::Chain, slice::IterMut<'a, BasicBlock>>>;
impl<'tcx> Terminator<'tcx> {
#[inline]
@@ -371,13 +394,17 @@ impl<'tcx> TerminatorKind<'tcx> {
pub fn successors(&self) -> Successors<'_> {
use self::TerminatorKind::*;
match *self {
+ Drop { target: t, unwind: UnwindAction::Cleanup(u), drop: Some(ref d), .. } => {
+ Some(t).into_iter().chain(Some(u).into_iter().chain(slice::from_ref(d).into_iter().copied()))
+ }
Call { target: Some(t), unwind: UnwindAction::Cleanup(ref u), .. }
| Yield { resume: t, drop: Some(ref u), .. }
- | Drop { target: t, unwind: UnwindAction::Cleanup(ref u), .. }
+ | Drop { target: t, unwind: UnwindAction::Cleanup(ref u), drop: None, .. }
+ | Drop { target: t, unwind: _, drop: Some(ref u), .. }
| Assert { target: t, unwind: UnwindAction::Cleanup(ref u), .. }
| FalseUnwind { real_target: t, unwind: UnwindAction::Cleanup(ref u) }
| InlineAsm { destination: Some(t), unwind: UnwindAction::Cleanup(ref u), .. } => {
- Some(t).into_iter().chain(slice::from_ref(u).into_iter().copied())
+ None.into_iter().chain(Some(t).into_iter().chain(slice::from_ref(u).into_iter().copied()))
}
Goto { target: t }
| Call { target: None, unwind: UnwindAction::Cleanup(t), .. }
@@ -388,7 +415,7 @@ impl<'tcx> TerminatorKind<'tcx> {
| FalseUnwind { real_target: t, unwind: _ }
| InlineAsm { destination: None, unwind: UnwindAction::Cleanup(t), .. }
| InlineAsm { destination: Some(t), unwind: _, .. } => {
- Some(t).into_iter().chain((&[]).into_iter().copied())
+ None.into_iter().chain(Some(t).into_iter().chain((&[]).into_iter().copied()))
}
UnwindResume
| UnwindTerminate(_)
@@ -397,14 +424,16 @@ impl<'tcx> TerminatorKind<'tcx> {
| Unreachable
| Call { target: None, unwind: _, .. }
| InlineAsm { destination: None, unwind: _, .. } => {
- None.into_iter().chain((&[]).into_iter().copied())
+ None.into_iter().chain(None.into_iter().chain((&[]).into_iter().copied()))
}
SwitchInt { ref targets, .. } => {
- None.into_iter().chain(targets.targets.iter().copied())
+ None.into_iter().chain(None.into_iter().chain(targets.targets.iter().copied()))
+ }
+ FalseEdge { real_target, ref imaginary_target } => {
+ None.into_iter().chain(Some(real_target)
+ .into_iter()
+ .chain(slice::from_ref(imaginary_target).into_iter().copied()))
}
- FalseEdge { real_target, ref imaginary_target } => Some(real_target)
- .into_iter()
- .chain(slice::from_ref(imaginary_target).into_iter().copied()),
}
}
@@ -412,16 +441,20 @@ impl<'tcx> TerminatorKind<'tcx> {
pub fn successors_mut(&mut self) -> SuccessorsMut<'_> {
use self::TerminatorKind::*;
match *self {
+ Drop { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), drop: Some(ref mut d), .. } => {
+ Some(t).into_iter().chain(slice::from_mut(u).into_iter().chain(slice::from_mut(d)))
+ }
Call { target: Some(ref mut t), unwind: UnwindAction::Cleanup(ref mut u), .. }
| Yield { resume: ref mut t, drop: Some(ref mut u), .. }
- | Drop { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), .. }
+ | Drop { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), drop: None, .. }
+ | Drop { target: ref mut t, unwind: _, drop: Some(ref mut u), .. }
| Assert { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), .. }
| FalseUnwind { real_target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u) }
| InlineAsm {
destination: Some(ref mut t),
unwind: UnwindAction::Cleanup(ref mut u),
..
- } => Some(t).into_iter().chain(slice::from_mut(u)),
+ } => Some(t).into_iter().chain(slice::from_mut(u).into_iter().chain(&mut [])),
Goto { target: ref mut t }
| Call { target: None, unwind: UnwindAction::Cleanup(ref mut t), .. }
| Call { target: Some(ref mut t), unwind: _, .. }
@@ -431,7 +464,7 @@ impl<'tcx> TerminatorKind<'tcx> {
| FalseUnwind { real_target: ref mut t, unwind: _ }
| InlineAsm { destination: None, unwind: UnwindAction::Cleanup(ref mut t), .. }
| InlineAsm { destination: Some(ref mut t), unwind: _, .. } => {
- Some(t).into_iter().chain(&mut [])
+ Some(t).into_iter().chain((&mut []).into_iter().chain(&mut []))
}
UnwindResume
| UnwindTerminate(_)
@@ -439,10 +472,10 @@ impl<'tcx> TerminatorKind<'tcx> {
| Return
| Unreachable
| Call { target: None, unwind: _, .. }
- | InlineAsm { destination: None, unwind: _, .. } => None.into_iter().chain(&mut []),
- SwitchInt { ref mut targets, .. } => None.into_iter().chain(&mut targets.targets),
+ | InlineAsm { destination: None, unwind: _, .. } => None.into_iter().chain((&mut []).into_iter().chain(&mut [])),
+ SwitchInt { ref mut targets, .. } => None.into_iter().chain((&mut targets.targets).into_iter().chain(&mut [])),
FalseEdge { ref mut real_target, ref mut imaginary_target } => {
- Some(real_target).into_iter().chain(slice::from_mut(imaginary_target))
+ Some(real_target).into_iter().chain(slice::from_mut(imaginary_target).into_iter().chain(&mut []))
}
}
}
@@ -566,7 +599,8 @@ impl<'tcx> TerminatorKind<'tcx> {
Goto { target } => TerminatorEdges::Single(target),
Assert { target, unwind, expected: _, msg: _, cond: _ }
- | Drop { target, unwind, place: _, replace: _ }
+ // TODO: Maybe we need AssignOnReturn for async drop here ?
+ | Drop { target, unwind, place: _, replace: _, drop: _, async_fut: _ }
| FalseUnwind { real_target: target, unwind } => match unwind {
UnwindAction::Cleanup(unwind) => TerminatorEdges::Double(target, unwind),
UnwindAction::Continue | UnwindAction::Terminate(_) | UnwindAction::Unreachable => {
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 4696f54c89787..30767e8e891f7 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -348,6 +348,7 @@ macro_rules! make_mir_visitor {
ty::InstanceDef::DropGlue(_def_id, None) => {}
ty::InstanceDef::FnPtrShim(_def_id, ty) |
+ ty::InstanceDef::FutureDropPollShim(_def_id, ty) |
ty::InstanceDef::DropGlue(_def_id, Some(ty)) |
ty::InstanceDef::CloneShim(_def_id, ty) |
ty::InstanceDef::FnPtrAddrShim(_def_id, ty) => {
@@ -505,6 +506,8 @@ macro_rules! make_mir_visitor {
target: _,
unwind: _,
replace: _,
+ drop: _,
+ async_fut: _,
} => {
self.visit_place(
place,
@@ -616,7 +619,7 @@ macro_rules! make_mir_visitor {
OverflowNeg(op) | DivisionByZero(op) | RemainderByZero(op) => {
self.visit_operand(op, location);
}
- ResumedAfterReturn(_) | ResumedAfterPanic(_) => {
+ ResumedAfterReturn(_) | ResumedAfterPanic(_) | ResumedAfterDrop(_) => {
// Nothing to visit
}
MisalignedPointerDereference { required, found } => {
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 2f7ae68c69211..1d454a44492cb 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1347,6 +1347,10 @@ rustc_queries! {
query is_unpin_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
desc { "computing whether `{}` is `Unpin`", env.value }
}
+ /// Query backing `Ty::is_async_drop`.
+ query is_async_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
+ desc { "computing whether `{}` is `AsyncDrop`", 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 }
diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs
index 293fdb026b685..06d88343b377a 100644
--- a/compiler/rustc_middle/src/ty/instance.rs
+++ b/compiler/rustc_middle/src/ty/instance.rs
@@ -92,6 +92,9 @@ pub enum InstanceDef<'tcx> {
/// native support.
ThreadLocalShim(DefId),
+ /// future_drop_poll for async drop of future
+ FutureDropPollShim(DefId, Ty<'tcx>),
+
/// `core::ptr::drop_in_place::`.
///
/// The `DefId` is for `core::ptr::drop_in_place`.
@@ -168,6 +171,7 @@ impl<'tcx> InstanceDef<'tcx> {
| InstanceDef::Intrinsic(def_id)
| InstanceDef::ThreadLocalShim(def_id)
| InstanceDef::ClosureOnceShim { call_once: def_id, track_caller: _ }
+ | InstanceDef::FutureDropPollShim(def_id, _)
| InstanceDef::DropGlue(def_id, _)
| InstanceDef::CloneShim(def_id, _)
| InstanceDef::FnPtrAddrShim(def_id, _) => def_id,
@@ -178,7 +182,9 @@ impl<'tcx> InstanceDef<'tcx> {
pub fn def_id_if_not_guaranteed_local_codegen(self) -> Option {
match self {
ty::InstanceDef::Item(def) => Some(def),
- ty::InstanceDef::DropGlue(def_id, Some(_)) | InstanceDef::ThreadLocalShim(def_id) => {
+ ty::InstanceDef::DropGlue(def_id, Some(_))
+ | InstanceDef::ThreadLocalShim(def_id)
+ | ty::InstanceDef::FutureDropPollShim(def_id, ..) => {
Some(def_id)
}
InstanceDef::VTableShim(..)
@@ -280,6 +286,7 @@ impl<'tcx> InstanceDef<'tcx> {
| InstanceDef::ThreadLocalShim(..)
| InstanceDef::FnPtrAddrShim(..)
| InstanceDef::FnPtrShim(..)
+ | InstanceDef::FutureDropPollShim(..)
| InstanceDef::DropGlue(_, Some(_)) => false,
InstanceDef::ClosureOnceShim { .. }
| InstanceDef::DropGlue(..)
@@ -319,6 +326,7 @@ fn fmt_instance(
InstanceDef::Virtual(_, num) => write!(f, " - virtual#{num}"),
InstanceDef::FnPtrShim(_, ty) => write!(f, " - shim({ty})"),
InstanceDef::ClosureOnceShim { .. } => write!(f, " - shim"),
+ InstanceDef::FutureDropPollShim(_, ty) => write!(f, " - shim({ty})"),
InstanceDef::DropGlue(_, None) => write!(f, " - shim(None)"),
InstanceDef::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({ty}))"),
InstanceDef::CloneShim(_, ty) => write!(f, " - shim({ty})"),
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index dac60ae58a0ca..36490a49c4b23 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -2353,6 +2353,7 @@ impl<'tcx> TyCtxt<'tcx> {
| ty::InstanceDef::FnPtrShim(..)
| ty::InstanceDef::Virtual(..)
| ty::InstanceDef::ClosureOnceShim { .. }
+ | ty::InstanceDef::FutureDropPollShim(..)
| ty::InstanceDef::DropGlue(..)
| ty::InstanceDef::CloneShim(..)
| ty::InstanceDef::ThreadLocalShim(..)
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 1f81678d6a54c..a6e50b9823305 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -1169,6 +1169,45 @@ impl<'tcx> Ty<'tcx> {
}
}
+ /// Checks whether values of this type `T` implement the `AsyncDrop` trait.
+ pub fn is_async_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
+ !self.is_trivially_not_async_drop() && tcx.is_async_drop_raw(param_env.and(self))
+ }
+
+ /// Fast path helper for testing if a type is `AsyncDrop`.
+ ///
+ /// Returning true means the type is known to be `!AsyncDrop`. Returning
+ /// `false` means nothing -- could be `AsyncDrop`, might not be.
+ fn is_trivially_not_async_drop(self) -> bool {
+ match self.kind() {
+ ty::Int(_)
+ | ty::Uint(_)
+ | ty::Float(_)
+ | ty::Bool
+ | ty::Char
+ | ty::Str
+ | ty::Never
+ | ty::Ref(..)
+ | ty::RawPtr(_)
+ | ty::FnDef(..)
+ | ty::Error(_)
+ | ty::FnPtr(_) => true,
+ ty::Tuple(fields) => fields.iter().all(Self::is_trivially_not_async_drop),
+ ty::Slice(elem_ty) | ty::Array(elem_ty, _) => elem_ty.is_trivially_not_async_drop(),
+ ty::Adt(..)
+ | ty::Bound(..)
+ | ty::Closure(..)
+ | ty::Dynamic(..)
+ | ty::Foreign(_)
+ | ty::Coroutine(..)
+ | ty::CoroutineWitness(..)
+ | ty::Infer(_)
+ | ty::Alias(..)
+ | ty::Param(_)
+ | ty::Placeholder(_) => 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).
diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
index c669d3fd6230d..e2d8be352df7f 100644
--- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
+++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
@@ -69,6 +69,8 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
target: self.parse_return_to(args[1])?,
unwind: self.parse_unwind_action(args[2])?,
replace: false,
+ drop: None,
+ async_fut: None,
})
},
@call(mir_call, args) => {
diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
index 6e8af7bb6df6c..556d13533660d 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
@@ -732,6 +732,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
target: success,
unwind: UnwindAction::Continue,
replace: false,
+ drop: None,
+ async_fut: None,
},
);
this.diverge_from(block);
diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs
index 6827797df372c..01162dcaa4fd3 100644
--- a/compiler/rustc_mir_build/src/build/scope.rs
+++ b/compiler/rustc_mir_build/src/build/scope.rs
@@ -90,6 +90,7 @@ use rustc_index::{IndexSlice, IndexVec};
use rustc_middle::middle::region;
use rustc_middle::mir::*;
use rustc_middle::thir::{ExprId, LintLevel};
+use rustc_middle::ty::{self, TyCtxt};
use rustc_session::lint::Level;
use rustc_span::source_map::Spanned;
use rustc_span::{Span, DUMMY_SP};
@@ -374,6 +375,8 @@ impl DropTree {
unwind: UnwindAction::Terminate(UnwindTerminateReason::InCleanup),
place: drop_data.0.local.into(),
replace: false,
+ drop: None,
+ async_fut: None,
};
cfg.terminate(block, drop_data.0.source_info, terminator);
}
@@ -703,6 +706,24 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.cfg.terminate(block, source_info, TerminatorKind::UnwindResume);
}
+ fn is_async_drop_impl(
+ tcx: TyCtxt<'tcx>,
+ local_decls: &IndexVec>,
+ param_env: ty::ParamEnv<'tcx>,
+ local: Local,
+ ) -> bool {
+ let ty = local_decls[local].ty;
+ if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) = ty.kind() {
+ if tcx.type_of(def_id).instantiate(tcx, args).is_coroutine() {
+ return true;
+ }
+ }
+ ty.is_async_drop(tcx, param_env) || ty.is_coroutine()
+ }
+ fn is_async_drop(&self, local: Local) -> bool {
+ Self::is_async_drop_impl(self.tcx, &self.local_decls, self.param_env, local)
+ }
+
fn leave_top_scope(&mut self, block: BasicBlock) -> BasicBlock {
// If we are emitting a `drop` statement, we need to have the cached
// diverge cleanup pads ready in case that drop panics.
@@ -710,15 +731,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let is_coroutine = self.coroutine.is_some();
let unwind_to = if needs_cleanup { self.diverge_cleanup() } else { DropIdx::MAX };
+ let scope = self.scopes.scopes.last().expect("leave_top_scope called with no scopes");
+ let has_async_drops = is_coroutine && scope.drops.iter().any(
+ |v| v.kind == DropKind::Value && self.is_async_drop(v.local)
+ );
+ let dropline_to = if has_async_drops {
+ Some(self.diverge_dropline())
+ } else { None };
let scope = self.scopes.scopes.last().expect("leave_top_scope called with no scopes");
unpack!(build_scope_drops(
&mut self.cfg,
&mut self.scopes.unwind_drops,
+ &mut self.scopes.coroutine_drops,
scope,
block,
unwind_to,
+ dropline_to,
is_coroutine && needs_cleanup,
self.arg_count,
+ |v: Local| Self::is_async_drop_impl(self.tcx, &self.local_decls, self.param_env, v),
))
}
@@ -1109,22 +1140,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.scopes.unwind_drops.add_entry(start, next_drop);
}
- /// Sets up a path that performs all required cleanup for dropping a
- /// coroutine, starting from the given block that ends in
- /// [TerminatorKind::Yield].
- ///
- /// This path terminates in CoroutineDrop.
- pub(crate) fn coroutine_drop_cleanup(&mut self, yield_block: BasicBlock) {
- debug_assert!(
- matches!(
- self.cfg.block_data(yield_block).terminator().kind,
- TerminatorKind::Yield { .. }
- ),
- "coroutine_drop_cleanup called on block with non-yield terminator."
- );
- let (uncached_scope, mut cached_drop) = self
- .scopes
- .scopes
+ /// Returns the [DropIdx] for the innermost drop for dropline (coroutine drop path).
+ /// The `DropIdx` will be created if it doesn't already exist.
+ fn diverge_dropline(&mut self) -> DropIdx {
+ // It is okay to use dummy span because the getting scope index on the topmost scope
+ // must always succeed.
+ self.diverge_dropline_target(self.scopes.topmost(), DUMMY_SP)
+ }
+
+ /// Similar to diverge_cleanup_target, but for dropline (coroutine drop path)
+ fn diverge_dropline_target(&mut self, target_scope: region::Scope, span: Span) -> DropIdx {
+ debug_assert!(self.coroutine.is_some(), "diverge_dropline_target is valid only for coroutine");
+ let target = self.scopes.scope_index(target_scope, span);
+ let (uncached_scope, mut cached_drop) = self.scopes.scopes[..=target]
.iter()
.enumerate()
.rev()
@@ -1133,13 +1161,34 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
})
.unwrap_or((0, ROOT_NODE));
- for scope in &mut self.scopes.scopes[uncached_scope..] {
+ if uncached_scope > target {
+ return cached_drop;
+ }
+
+ for scope in &mut self.scopes.scopes[uncached_scope..=target] {
for drop in &scope.drops {
cached_drop = self.scopes.coroutine_drops.add_drop(*drop, cached_drop);
}
scope.cached_coroutine_drop_block = Some(cached_drop);
}
+ cached_drop
+ }
+
+ /// Sets up a path that performs all required cleanup for dropping a
+ /// coroutine, starting from the given block that ends in
+ /// [TerminatorKind::Yield].
+ ///
+ /// This path terminates in CoroutineDrop.
+ pub(crate) fn coroutine_drop_cleanup(&mut self, yield_block: BasicBlock) {
+ debug_assert!(
+ matches!(
+ self.cfg.block_data(yield_block).terminator().kind,
+ TerminatorKind::Yield { .. }
+ ),
+ "coroutine_drop_cleanup called on block with non-yield terminator."
+ );
+ let cached_drop = self.diverge_dropline();
self.scopes.coroutine_drops.add_entry(yield_block, cached_drop);
}
@@ -1170,6 +1219,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
target: assign,
unwind: UnwindAction::Cleanup(assign_unwind),
replace: true,
+ drop: None,
+ async_fut: None,
},
);
self.diverge_from(block);
@@ -1222,16 +1273,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
/// Builds drops for `pop_scope` and `leave_top_scope`.
-fn build_scope_drops<'tcx>(
+fn build_scope_drops<'tcx, F>(
cfg: &mut CFG<'tcx>,
unwind_drops: &mut DropTree,
+ coroutine_drops: &mut DropTree,
scope: &Scope,
mut block: BasicBlock,
mut unwind_to: DropIdx,
+ mut dropline_to: Option,
storage_dead_on_unwind: bool,
arg_count: usize,
-) -> BlockAnd<()> {
- debug!("build_scope_drops({:?} -> {:?})", block, scope);
+ is_async_drop: F,
+) -> BlockAnd<()> where F: Fn(Local) -> bool {
+ debug!("build_scope_drops({:?} -> {:?}), dropline_to={:?}", block, scope, dropline_to);
// Build up the drops in evaluation order. The end result will
// look like:
@@ -1265,6 +1319,12 @@ fn build_scope_drops<'tcx>(
debug_assert_eq!(unwind_drops.drops[unwind_to].0.kind, drop_data.kind);
unwind_to = unwind_drops.drops[unwind_to].1;
+ if let Some(idx) = dropline_to {
+ debug_assert_eq!(coroutine_drops.drops[idx].0.local, drop_data.local);
+ debug_assert_eq!(coroutine_drops.drops[idx].0.kind, drop_data.kind);
+ dropline_to = Some(coroutine_drops.drops[idx].1);
+ }
+
// If the operand has been moved, and we are not on an unwind
// path, then don't generate the drop. (We only take this into
// account for non-unwind paths so as not to disturb the
@@ -1274,6 +1334,9 @@ fn build_scope_drops<'tcx>(
}
unwind_drops.add_entry(block, unwind_to);
+ if let Some(to) = dropline_to && is_async_drop(local) {
+ coroutine_drops.add_entry(block, to);
+ }
let next = cfg.start_new_block();
cfg.terminate(
@@ -1284,6 +1347,8 @@ fn build_scope_drops<'tcx>(
target: next,
unwind: UnwindAction::Continue,
replace: false,
+ drop: None,
+ async_fut: None,
},
);
block = next;
@@ -1294,6 +1359,11 @@ fn build_scope_drops<'tcx>(
debug_assert_eq!(unwind_drops.drops[unwind_to].0.kind, drop_data.kind);
unwind_to = unwind_drops.drops[unwind_to].1;
}
+ if let Some(idx) = dropline_to {
+ debug_assert_eq!(coroutine_drops.drops[idx].0.local, drop_data.local);
+ debug_assert_eq!(coroutine_drops.drops[idx].0.kind, drop_data.kind);
+ dropline_to = Some(coroutine_drops.drops[idx].1);
+ }
// Only temps and vars need their storage dead.
assert!(local.index() > arg_count);
cfg.push(block, Statement { source_info, kind: StatementKind::StorageDead(local) });
@@ -1351,6 +1421,36 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
}
}
}
+ // Link the exit drop tree to dropline drop tree (coroutine drop path) for async drops
+ if is_coroutine && drops.drops.iter().any(|(drop, _)|
+ drop.kind == DropKind::Value && self.is_async_drop(drop.local))
+ {
+ let dropline_target = self.diverge_dropline_target(else_scope, span);
+ let mut dropline_indices = IndexVec::from_elem_n(dropline_target, 1);
+ for (drop_idx, drop_data) in drops.drops.iter_enumerated().skip(1) {
+ match drop_data.0.kind {
+ DropKind::Storage => {
+ let coroutine_drop = self
+ .scopes
+ .coroutine_drops
+ .add_drop(drop_data.0, dropline_indices[drop_data.1]);
+ dropline_indices.push(coroutine_drop);
+ }
+ DropKind::Value => {
+ let coroutine_drop = self
+ .scopes
+ .coroutine_drops
+ .add_drop(drop_data.0, dropline_indices[drop_data.1]);
+ if self.is_async_drop(drop_data.0.local) {
+ self.scopes
+ .coroutine_drops
+ .add_entry(blocks[drop_idx].unwrap(), dropline_indices[drop_data.1]);
+ }
+ dropline_indices.push(coroutine_drop);
+ }
+ }
+ }
+ }
blocks[ROOT_NODE].map(BasicBlock::unit)
}
@@ -1396,9 +1496,9 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
// to be captured by the coroutine. I'm not sure how important this
// optimization is, but it is here.
for (drop_idx, drop_data) in drops.drops.iter_enumerated() {
- if let DropKind::Value = drop_data.0.kind {
+ if let DropKind::Value = drop_data.0.kind && let Some(bb) = blocks[drop_idx] {
debug_assert!(drop_data.1 < drops.drops.next_index());
- drops.entry_points.push((drop_data.1, blocks[drop_idx].unwrap()));
+ drops.entry_points.push((drop_data.1, bb));
}
}
Self::build_unwind_tree(cfg, drops, fn_span, resume_block);
@@ -1444,6 +1544,8 @@ impl<'tcx> DropTreeBuilder<'tcx> for CoroutineDrop {
let term = cfg.block_data_mut(from).terminator_mut();
if let TerminatorKind::Yield { ref mut drop, .. } = term.kind {
*drop = Some(to);
+ } else if let TerminatorKind::Drop { ref mut drop, .. } = term.kind {
+ *drop = Some(to);
} else {
span_bug!(
term.source_info.span,
diff --git a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs
index 862876f53c76c..6a2cb712699e8 100644
--- a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs
+++ b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs
@@ -3,11 +3,13 @@ use rustc_hir::lang_items::LangItem;
use rustc_index::Idx;
use rustc_middle::mir::patch::MirPatch;
use rustc_middle::mir::*;
+use rustc_middle::traits;
use rustc_middle::traits::Reveal;
use rustc_middle::ty::util::IntTypeExt;
-use rustc_middle::ty::GenericArgsRef;
+use rustc_middle::ty::{GenericArg, GenericArgsRef};
+//use rustc_middle::ty::{, ParamEnv, Ty, TyCtxt};
use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_span::source_map::Spanned;
+use rustc_span::source_map::{dummy_spanned, Spanned};
use rustc_span::DUMMY_SP;
use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT};
use std::{fmt, iter};
@@ -166,6 +168,7 @@ where
path: D::Path,
succ: BasicBlock,
unwind: Unwind,
+ dropline: Option,
}
/// "Elaborates" a drop of `place`/`path` and patches `bb`'s terminator to execute it.
@@ -184,11 +187,12 @@ pub fn elaborate_drop<'b, 'tcx, D>(
succ: BasicBlock,
unwind: Unwind,
bb: BasicBlock,
+ dropline: Option,
) where
D: DropElaborator<'b, 'tcx>,
'tcx: 'b,
{
- DropCtxt { elaborator, source_info, place, path, succ, unwind }.elaborate_drop(bb)
+ DropCtxt { elaborator, source_info, place, path, succ, unwind, dropline }.elaborate_drop(bb)
}
impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
@@ -205,6 +209,140 @@ where
self.elaborator.tcx()
}
+ // Generates three blocks:
+ // * #1:pin_obj_bb: call Pin::new_unchecked(&mut obj)
+ // * #2:call_drop_bb: fut = call obj.()
+ // * #3:drop_term_bb: drop (obj, fut, ...)
+ // We keep async drop unexpanded to poll-loop here, to expand it later, at StateTransform -
+ // into states expand.
+ fn build_async_drop(&mut self, bb: BasicBlock) {
+ let drop_ty = self.place_ty(self.place);
+ let tcx = self.tcx();
+ let span = self.source_info.span;
+ let pin_obj_bb = bb;
+
+ // Resolving AsyncDrop::drop() impl function for drop_ty
+ let trait_ref = ty::TraitRef::from_lang_item(
+ tcx,
+ LangItem::AsyncDrop,
+ span,
+ [drop_ty],
+ );
+ let (drop_trait, trait_args) =
+ match tcx.codegen_select_candidate((ty::ParamEnv::reveal_all(), trait_ref)) {
+ Ok(traits::ImplSource::UserDefined(traits::ImplSourceUserDefinedData {
+ impl_def_id, args, ..
+ })) => (*impl_def_id, *args),
+ impl_source => {
+ bug!("invalid `AsyncDrop` impl_source: {:?}", impl_source);
+ }
+ };
+ let drop_fn_def_id = tcx.associated_item_def_ids(drop_trait)[0];
+ let drop_fn = Ty::new_fn_def(tcx, drop_fn_def_id, trait_args);
+ let sig = drop_fn.fn_sig(tcx);
+ let sig = tcx.instantiate_bound_regions_with_erased(sig);
+ let fut_ty = sig.output();
+ let fut = Place::from(self.new_temp(fut_ty));
+
+ // #1:pin_obj_bb >>> obj_ref = &mut obj
+ let obj_ref_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, drop_ty);
+ let obj_ref_place = Place::from(self.new_temp(obj_ref_ty));
+ let term_loc = self.elaborator.body().terminator_loc(pin_obj_bb);
+ self.elaborator.patch().add_assign(
+ term_loc, obj_ref_place, Rvalue::Ref(
+ tcx.lifetimes.re_erased,
+ BorrowKind::Mut { kind: MutBorrowKind::Default },
+ self.place,
+ ));
+
+ // pin_obj_place preparation
+ let pin_obj_new_unchecked_fn = Ty::new_fn_def(
+ tcx,
+ tcx.require_lang_item(LangItem::PinNewUnchecked, Some(span)),
+ //
+ [GenericArg::from(obj_ref_ty), GenericArg::from(ty::Const::from_bool(tcx, true))],
+ );
+ let pin_obj_ty = pin_obj_new_unchecked_fn.fn_sig(tcx).output().no_bound_vars().unwrap();
+ let pin_obj_place = Place::from(self.new_temp(pin_obj_ty));
+ let pin_obj_new_unchecked_fn = Operand::Constant(Box::new(ConstOperand {
+ span: span,
+ user_ty: None,
+ const_: Const::zero_sized(pin_obj_new_unchecked_fn),
+ }));
+
+ // #3:drop_term_bb
+ let drop_term_bb = self.new_block(self.unwind,
+ TerminatorKind::Drop {
+ place: self.place,
+ target: self.succ,
+ unwind: self.unwind.into_action(),
+ replace: false,
+ drop: self.dropline,
+ async_fut: Some(fut.local),
+ });
+
+ // #2:call_drop_bb
+ let call_drop_bb = self.new_block(self.unwind, TerminatorKind::Call {
+ func: Operand::function_handle(
+ tcx,
+ drop_fn_def_id,
+ tcx.mk_args(trait_args),
+ span,
+ ),
+ args: vec![Spanned {
+ node: Operand::Move(Place::from(pin_obj_place)),
+ span: DUMMY_SP,
+ }],
+ destination: fut,
+ target: Some(drop_term_bb),
+ unwind: self.unwind.into_action(),
+ call_source: CallSource::Misc,
+ fn_span: self.source_info.span,
+ });
+
+ // StorageDead(fut) in self.succ block (at the begin)
+ self.elaborator.patch().add_statement(
+ Location { block: self.succ, statement_index: 0 },
+ StatementKind::StorageDead(fut.local)
+ );
+
+ // #1:pin_obj_bb >>> call Pin::new_unchecked(&mut obj)
+ self.elaborator.patch().patch_terminator(
+ pin_obj_bb,
+ TerminatorKind::Call {
+ func: pin_obj_new_unchecked_fn,
+ args: vec![dummy_spanned(Operand::Move(obj_ref_place))],
+ destination: pin_obj_place,
+ target: Some(call_drop_bb),
+ unwind: self.unwind.into_action(),
+ call_source: CallSource::Misc,
+ fn_span: span,
+ },
+ );
+ }
+
+ fn build_drop(&mut self, bb: BasicBlock) {
+ let drop_ty = self.place_ty(self.place);
+ let is_coroutine = self.elaborator.body().coroutine.is_some();
+ if is_coroutine && !self.elaborator.body()[bb].is_cleanup &&
+ drop_ty.is_async_drop(self.tcx(), self.elaborator.param_env())
+ {
+ self.build_async_drop(bb);
+ } else {
+ self.elaborator.patch().patch_terminator(
+ bb,
+ TerminatorKind::Drop {
+ place: self.place,
+ target: self.succ,
+ unwind: self.unwind.into_action(),
+ replace: false,
+ drop: self.dropline,
+ async_fut: None,
+ },
+ );
+ }
+ }
+
/// This elaborates a single drop instruction, located at `bb`, and
/// patches over it.
///
@@ -232,15 +370,7 @@ where
.patch_terminator(bb, TerminatorKind::Goto { target: self.succ });
}
DropStyle::Static => {
- self.elaborator.patch().patch_terminator(
- bb,
- TerminatorKind::Drop {
- place: self.place,
- target: self.succ,
- unwind: self.unwind.into_action(),
- replace: false,
- },
- );
+ self.build_drop(bb);
}
DropStyle::Conditional => {
let drop_bb = self.complete_drop(self.succ, self.unwind);
@@ -301,6 +431,7 @@ where
place,
succ,
unwind,
+ dropline: None,
}
.elaborated_drop_block()
} else {
@@ -312,6 +443,7 @@ where
place,
succ,
unwind,
+ dropline: None,
// Using `self.path` here to condition the drop on
// our own drop flag.
path: self.path,
@@ -745,6 +877,8 @@ where
target: loop_block,
unwind: unwind.into_action(),
replace: false,
+ drop: None,
+ async_fut: None,
},
);
@@ -923,6 +1057,8 @@ where
target,
unwind: unwind.into_action(),
replace: false,
+ drop: self.dropline,
+ async_fut: None,
};
self.new_block(unwind, block)
}
diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs
index 720515f262db8..84b692cb82852 100644
--- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs
@@ -365,7 +365,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
) -> TerminatorEdges<'mir, 'tcx> {
let mut edges = terminator.edges();
if self.skip_unreachable_unwind
- && let mir::TerminatorKind::Drop { target, unwind, place, replace: _ } = terminator.kind
+ && let mir::TerminatorKind::Drop { target, unwind, place, replace: _, drop: _, async_fut: _ } = terminator.kind
&& matches!(unwind, mir::UnwindAction::Cleanup(_))
&& self.is_unwind_dead(place, state)
{
diff --git a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs
index de6d20ae3e807..2508a3c5780c2 100644
--- a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs
+++ b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs
@@ -79,7 +79,7 @@ fn add_move_for_packed_drop<'tcx>(
is_cleanup: bool,
) {
debug!("add_move_for_packed_drop({:?} @ {:?})", terminator, loc);
- let TerminatorKind::Drop { ref place, target, unwind, replace } = terminator.kind else {
+ let TerminatorKind::Drop { ref place, target, unwind, replace, drop, async_fut } = terminator.kind else {
unreachable!();
};
@@ -102,6 +102,8 @@ fn add_move_for_packed_drop<'tcx>(
target: storage_dead_block,
unwind,
replace,
+ drop,
+ async_fut
},
);
}
diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs
index 347f9b49efe88..c6bcb793e3884 100644
--- a/compiler/rustc_mir_transform/src/coroutine.rs
+++ b/compiler/rustc_mir_transform/src/coroutine.rs
@@ -66,7 +66,9 @@ use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
use rustc_middle::mir::*;
use rustc_middle::ty::CoroutineArgs;
use rustc_middle::ty::InstanceDef;
+use rustc_middle::ty::GenericArg;
use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::util::Discr;
use rustc_mir_dataflow::impls::{
MaybeBorrowedLocals, MaybeLiveLocals, MaybeRequiresStorage, MaybeStorageLive,
};
@@ -75,6 +77,8 @@ use rustc_mir_dataflow::Analysis;
use rustc_span::def_id::{DefId, LocalDefId};
use rustc_span::symbol::sym;
use rustc_span::Span;
+use rustc_span::source_map::dummy_spanned;
+use rustc_span::DUMMY_SP;
use rustc_target::abi::{FieldIdx, VariantIdx};
use rustc_target::spec::PanicStrategy;
use std::{iter, ops};
@@ -637,7 +641,7 @@ fn replace_local<'tcx>(
/// The async lowering step and the type / lifetime inference / checking are
/// still using the `ResumeTy` indirection for the time being, and that indirection
/// is removed here. After this transform, the coroutine body only knows about `&mut Context<'_>`.
-fn transform_async_context<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+fn transform_async_context<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> Ty<'tcx> {
let context_mut_ref = Ty::new_task_context(tcx);
// replace the type of the `resume` argument
@@ -669,6 +673,310 @@ fn transform_async_context<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
_ => {}
}
}
+ context_mut_ref
+}
+
+// rv = call fut.poll()
+fn build_poll_call<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ body: &mut Body<'tcx>,
+ poll_unit_place: &Place<'tcx>,
+ switch_block: BasicBlock,
+ fut_pin_place: &Place<'tcx>,
+ fut_ty: Ty<'tcx>,
+ context_ref_place: &Place<'tcx>,
+ unwind: UnwindAction,
+ is_coroutine_drop: bool,
+) -> BasicBlock {
+ let lang_item = if is_coroutine_drop { LangItem::FutureDropPoll } else { LangItem::FuturePoll };
+ let poll_fn = tcx.require_lang_item(lang_item, None);
+ let poll_fn = Ty::new_fn_def(tcx, poll_fn, [fut_ty]);
+ let poll_fn = Operand::Constant(Box::new(ConstOperand {
+ span: DUMMY_SP,
+ user_ty: None,
+ const_: Const::zero_sized(poll_fn),
+ }));
+ let call = TerminatorKind::Call {
+ func: poll_fn.clone(),
+ args: vec![
+ dummy_spanned(Operand::Move(*fut_pin_place)),
+ dummy_spanned(Operand::Move(*context_ref_place)),
+ ],
+ destination: *poll_unit_place,
+ target: Some(switch_block),
+ unwind: unwind,
+ call_source: CallSource::Misc,
+ fn_span: DUMMY_SP,
+ };
+ insert_term_block(body, call)
+}
+
+// pin_fut = Pin::new_unchecked(&mut fut)
+fn build_pin_fut<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ body: &mut Body<'tcx>,
+ fut_place: Place<'tcx>,
+ unwind: UnwindAction,
+) -> (BasicBlock, Place<'tcx>) {
+ let span = body.span;
+ let source_info = SourceInfo::outermost(span);
+ let fut_ty = fut_place.ty(&body.local_decls, tcx).ty;
+ let fut_ref_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, fut_ty);
+ let fut_ref_place = Place::from(body.local_decls.push(LocalDecl::new(fut_ref_ty, span)));
+ let pin_fut_new_unchecked_fn = Ty::new_fn_def(
+ tcx,
+ tcx.require_lang_item(LangItem::PinNewUnchecked, Some(span)),
+ //
+ [GenericArg::from(fut_ref_ty), GenericArg::from(ty::Const::from_bool(tcx, true))],
+ );
+ let fut_pin_ty = pin_fut_new_unchecked_fn.fn_sig(tcx).output().skip_binder();
+ let fut_pin_place = Place::from(body.local_decls.push(LocalDecl::new(fut_pin_ty, span)));
+ let pin_fut_new_unchecked_fn = Operand::Constant(Box::new(ConstOperand {
+ span: span,
+ user_ty: None,
+ const_: Const::zero_sized(pin_fut_new_unchecked_fn),
+ }));
+
+ let fut_ref_assign = Statement {
+ source_info,
+ kind: StatementKind::Assign(Box::new((
+ fut_ref_place,
+ Rvalue::Ref(
+ tcx.lifetimes.re_erased,
+ BorrowKind::Mut { kind: MutBorrowKind::Default },
+ fut_place,
+ ),
+ ))),
+ };
+
+ // call Pin::new_unchecked(&mut fut)
+ let pin_fut_bb = body.basic_blocks_mut().push(BasicBlockData {
+ statements: [fut_ref_assign].to_vec(),
+ terminator: Some(Terminator { source_info, kind: TerminatorKind::Call {
+ func: pin_fut_new_unchecked_fn,
+ args: vec![dummy_spanned(Operand::Move(fut_ref_place))],
+ destination: fut_pin_place,
+ target: None, // will be fixed later
+ unwind,
+ call_source: CallSource::Misc,
+ fn_span: span,
+ }}),
+ is_cleanup: false,
+ });
+ (pin_fut_bb, fut_pin_place)
+}
+
+// Build Poll switch for async drop
+// match rv {
+// Ready() => ready_block
+// Pending => yield_block
+//}
+fn build_poll_switch<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ body: &mut Body<'tcx>,
+ poll_enum: Ty<'tcx>,
+ poll_unit_place: &Place<'tcx>,
+ ready_block: BasicBlock,
+ yield_block: BasicBlock,
+) -> BasicBlock {
+ let poll_enum_adt = poll_enum.ty_adt_def().unwrap();
+
+ let Discr { val: poll_ready_discr, ty: poll_discr_ty } = poll_enum
+ .discriminant_for_variant(
+ tcx,
+ poll_enum_adt
+ .variant_index_with_id(tcx.require_lang_item(LangItem::PollReady, None)),
+ ).unwrap();
+ let poll_pending_discr = poll_enum
+ .discriminant_for_variant(
+ tcx,
+ poll_enum_adt
+ .variant_index_with_id(tcx.require_lang_item(LangItem::PollPending, None)),
+ ).unwrap().val;
+ let source_info = SourceInfo::outermost(body.span);
+ let poll_discr_place = Place::from(body.local_decls.push(LocalDecl::new(poll_discr_ty, source_info.span)));
+ let discr_assign = Statement {
+ source_info,
+ kind: StatementKind::Assign(Box::new((
+ poll_discr_place,
+ Rvalue::Discriminant(*poll_unit_place),
+ ))),
+ };
+ let unreachable_block = insert_term_block(body, TerminatorKind::Unreachable);
+ body.basic_blocks_mut().push(BasicBlockData {
+ statements: [discr_assign].to_vec(),
+ terminator: Some(Terminator { source_info, kind: TerminatorKind::SwitchInt {
+ discr: Operand::Move(poll_discr_place),
+ targets: SwitchTargets::new(
+ [(poll_ready_discr, ready_block), (poll_pending_discr, yield_block)]
+ .into_iter(),
+ unreachable_block,
+ ),
+ }}),
+ is_cleanup: false,
+ })
+}
+
+// Gather blocks, reachable through 'drop' targets of Yield and Drop terminators (chained)
+fn gather_dropline_blocks<'tcx>(body: &mut Body<'tcx>) -> BitSet {
+ let mut dropline: BitSet = BitSet::new_empty(body.basic_blocks.len());
+ for (bb, data) in traversal::reverse_postorder(body) {
+ if dropline.contains(bb) {
+ data.terminator().successors().for_each(|v| { dropline.insert(v); });
+ } else {
+ match data.terminator().kind {
+ TerminatorKind::Yield { drop: Some(v), .. } => { dropline.insert(v); },
+ TerminatorKind::Drop { drop: Some(v), .. } => { dropline.insert(v); },
+ _ => ()
+ }
+ }
+ }
+ dropline
+}
+
+/// Expand Drop terminator for async drops into mainline poll-switch and dropline poll-switch
+fn expand_async_drops<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ body: &mut Body<'tcx>,
+ context_mut_ref: Ty<'tcx>,
+ coroutine_kind: hir::CoroutineKind,
+ coroutine_ty: Ty<'tcx>,
+) -> bool {
+ let dropline = gather_dropline_blocks(body);
+ let mut has_async_drops = false;
+ for bb in START_BLOCK..body.basic_blocks.next_index() {
+ // Drops in unwind path (cleanup blocks) are not expanded to async drops, only sync drops in unwind path
+ if body[bb].is_cleanup {
+ if let TerminatorKind::Drop { ref mut async_fut, .. } = body[bb].terminator_mut().kind {
+ *async_fut = None;
+ }
+ continue;
+ }
+ let TerminatorKind::Drop {
+ place, target, unwind, replace: _, drop, async_fut
+ } = body[bb].terminator().kind else {
+ continue;
+ };
+
+ let place_ty = place.ty(&body.local_decls, tcx).ty;
+ if place_ty == coroutine_ty {
+ continue;
+ }
+
+ let (fut_local, is_coroutine_drop) = if let Some(fut_local) = async_fut {
+ (fut_local, false)
+ } else if let ty::Coroutine(def_id, ..) = place_ty.kind() {
+ if tcx.optimized_mir(def_id).coroutine_drop_async().is_some() {
+ // Coroutine is async dropped using its original state struct
+ (place.local, true)
+ } else {
+ continue;
+ }
+ } else {
+ continue;
+ };
+ has_async_drops = true;
+
+ let is_dropline_bb = dropline.contains(bb);
+ let fut_place = Place::from(fut_local);
+ let fut_ty = fut_place.ty(&body.local_decls, tcx).ty;
+
+ // poll-code:
+ // state_call_drop:
+ // #bb_pin: fut_pin = Pin::new_unchecked(&mut fut)
+ // #bb_call: rv = call fut.poll() (or future_drop_poll(fut) for internal future drops)
+ // #bb_check: match (rv)
+ // pending => return rv (yield)
+ // ready => *continue_bb|drop_bb*
+
+ // Compute Poll<> (aka Poll with void return)
+ let poll_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Poll, None));
+ let poll_enum = Ty::new_adt(tcx, poll_adt_ref, tcx.mk_args(&[Ty::new_unit(tcx).into()]));
+ let poll_decl = LocalDecl::new(poll_enum, body.span);
+ let poll_unit_place = Place::from(body.local_decls.push(poll_decl));
+
+ // First state-loop yield for mainline
+ let context_ref_place =
+ Place::from(body.local_decls.push(LocalDecl::new(context_mut_ref, body.span)));
+ let yield_block = insert_term_block(body, TerminatorKind::Unreachable); // `kind`` replaced later to yield
+ let switch_block = build_poll_switch(tcx, body, poll_enum, &poll_unit_place, target, yield_block);
+ let (pin_bb, fut_pin_place) = build_pin_fut(tcx, body, fut_place.clone(), UnwindAction::Continue);
+ let call_bb = build_poll_call(
+ tcx,
+ body,
+ &poll_unit_place,
+ switch_block,
+ &fut_pin_place,
+ fut_ty,
+ &context_ref_place,
+ unwind,
+ is_coroutine_drop,
+ );
+
+ // Second state-loop yield for transition to dropline (when coroutine async drop started)
+ let mut dropline_transition_bb: Option = None;
+ let mut dropline_yield_bb: Option = None;
+ let mut dropline_context_ref: Option> = None;
+ let mut dropline_call_bb: Option = None;
+ if !is_dropline_bb {
+ let context_ref_place2: Place<'_> =
+ Place::from(body.local_decls.push(LocalDecl::new(context_mut_ref, body.span)));
+ let drop_yield_block = insert_term_block(body, TerminatorKind::Unreachable); // `kind`` replaced later to yield
+ let drop_switch_block = build_poll_switch(tcx, body, poll_enum, &poll_unit_place, drop.unwrap(), drop_yield_block);
+ let (pin_bb2, fut_pin_place2) = build_pin_fut(tcx, body, fut_place, UnwindAction::Continue);
+ let drop_call_bb = build_poll_call(
+ tcx,
+ body,
+ &poll_unit_place,
+ drop_switch_block,
+ &fut_pin_place2,
+ fut_ty,
+ &context_ref_place2,
+ unwind,
+ is_coroutine_drop,
+ );
+ dropline_transition_bb = Some(pin_bb2);
+ dropline_yield_bb = Some(drop_yield_block);
+ dropline_context_ref = Some(context_ref_place2);
+ dropline_call_bb = Some(drop_call_bb);
+ }
+
+ // value needed only for return-yields or gen-coroutines, so just const here
+ let value = Operand::Constant(Box::new(ConstOperand {
+ span: body.span,
+ user_ty: None,
+ const_: Const::from_bool(tcx, false),
+ }));
+ use rustc_middle::mir::AssertKind::ResumedAfterDrop;
+ let panic_bb = insert_panic_block(tcx, body, ResumedAfterDrop(coroutine_kind));
+
+ if is_dropline_bb {
+ body[yield_block].terminator_mut().kind = TerminatorKind::Yield {
+ value: value.clone(), resume: panic_bb, resume_arg: context_ref_place, drop: Some(pin_bb)
+ };
+ } else {
+ body[yield_block].terminator_mut().kind = TerminatorKind::Yield {
+ value: value.clone(), resume: pin_bb, resume_arg: context_ref_place, drop: dropline_transition_bb
+ };
+ body[dropline_yield_bb.unwrap()].terminator_mut().kind = TerminatorKind::Yield {
+ value, resume: panic_bb, resume_arg: dropline_context_ref.unwrap(), drop: dropline_transition_bb
+ };
+ }
+
+ if let TerminatorKind::Call { ref mut target, .. } = body[pin_bb].terminator_mut().kind {
+ *target = Some(call_bb);
+ } else { bug!() }
+ if !is_dropline_bb {
+ if let TerminatorKind::Call { ref mut target, .. } =
+ body[dropline_transition_bb.unwrap()].terminator_mut().kind
+ {
+ *target = dropline_call_bb;
+ } else { bug!() }
+ }
+
+ body[bb].terminator_mut().kind = TerminatorKind::Goto { target: pin_bb };
+ }
+ has_async_drops
}
fn eliminate_get_context_call<'tcx>(bb_data: &mut BasicBlockData<'tcx>) -> Local {
@@ -1143,9 +1451,8 @@ fn insert_switch<'tcx>(
body: &mut Body<'tcx>,
cases: Vec<(usize, BasicBlock)>,
transform: &TransformVisitor<'tcx>,
- default: TerminatorKind<'tcx>,
+ default_block: BasicBlock,
) {
- let default_block = insert_term_block(body, default);
let (assign, discr) = transform.get_discr(body);
let switch_targets =
SwitchTargets::new(cases.iter().map(|(i, bb)| ((*i) as u128, *bb)), default_block);
@@ -1183,14 +1490,14 @@ fn elaborate_coroutine_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let mut elaborator = DropShimElaborator { body, patch: MirPatch::new(body), tcx, param_env };
for (block, block_data) in body.basic_blocks.iter_enumerated() {
- let (target, unwind, source_info) = match block_data.terminator() {
+ let (target, unwind, source_info, dropline) = match block_data.terminator() {
Terminator {
source_info,
- kind: TerminatorKind::Drop { place, target, unwind, replace: _ },
+ kind: TerminatorKind::Drop { place, target, unwind, replace: _, drop, async_fut: _ },
} => {
if let Some(local) = place.as_local() {
if local == SELF_ARG {
- (target, unwind, source_info)
+ (target, unwind, source_info, *drop)
} else {
continue;
}
@@ -1218,6 +1525,7 @@ fn elaborate_coroutine_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
*target,
unwind,
block,
+ dropline,
);
}
elaborator.patch.apply(body);
@@ -1227,11 +1535,16 @@ fn create_coroutine_drop_shim<'tcx>(
tcx: TyCtxt<'tcx>,
transform: &TransformVisitor<'tcx>,
coroutine_ty: Ty<'tcx>,
- body: &mut Body<'tcx>,
+ body: &Body<'tcx>,
drop_clean: BasicBlock,
) -> Body<'tcx> {
let mut body = body.clone();
- body.arg_count = 1; // make sure the resume argument is not included here
+ // Take the coroutine info out of the body, since the drop shim is
+ // not a coroutine body itself; it just has its drop built out of it.
+ let _ = body.coroutine.take();
+ // Make sure the resume argument is not included here, since we're
+ // building a body for `drop_in_place`.
+ body.arg_count = 1;
let source_info = SourceInfo::outermost(body.span);
@@ -1242,7 +1555,8 @@ fn create_coroutine_drop_shim<'tcx>(
// The returned state and the poisoned state fall through to the default
// case which is just to return
- insert_switch(&mut body, cases, transform, TerminatorKind::Return);
+ let default_block = insert_term_block(&mut body, TerminatorKind::Return);
+ insert_switch(&mut body, cases, transform, default_block);
for block in body.basic_blocks_mut() {
let kind = &mut block.terminator_mut().kind;
@@ -1280,6 +1594,89 @@ fn create_coroutine_drop_shim<'tcx>(
body
}
+// Create async drop shim function to drop coroutine itself
+fn create_coroutine_drop_shim_async<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ transform: &TransformVisitor<'tcx>,
+ coroutine_ty: Ty<'tcx>,
+ body: &Body<'tcx>,
+ drop_clean: BasicBlock,
+ can_unwind: bool,
+) -> Body<'tcx> {
+ let mut body = body.clone();
+ // Take the coroutine info out of the body, since the drop shim is
+ // not a coroutine body itself; it just has its drop built out of it.
+ let _ = body.coroutine.take();
+
+ // Poison the coroutine when it unwinds
+ if can_unwind {
+ generate_poison_block_and_redirect_unwinds_there(transform, &mut body);
+ }
+
+ let source_info = SourceInfo::outermost(body.span);
+
+ let mut cases = create_cases(&mut body, transform, Operation::Drop);
+
+ cases.insert(0, (UNRESUMED, drop_clean));
+
+ use rustc_middle::mir::AssertKind::ResumedAfterPanic;
+ // Panic when resumed on the returned or poisoned state
+ if can_unwind {
+ cases.insert(
+ 1,
+ (POISONED, insert_panic_block(tcx, &mut body, ResumedAfterPanic(transform.coroutine_kind))),
+ );
+ }
+
+ // RETURNED state also goes to default_block with `return Ready<()>`.
+ // For fully-polled coroutine, async drop has nothing to do.
+ let default_block = insert_poll_ready_block(tcx, &mut body);
+ insert_switch(&mut body, cases, transform, default_block);
+
+ for block in body.basic_blocks_mut() {
+ let kind = &mut block.terminator_mut().kind;
+ if let TerminatorKind::CoroutineDrop = *kind {
+ *kind = TerminatorKind::Return;
+ block.statements.push(return_poll_ready_assign(tcx, source_info));
+ }
+ }
+
+ // Replace the return variable: Poll to Poll<()>
+ let poll_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Poll, None));
+ let poll_enum = Ty::new_adt(tcx, poll_adt_ref, tcx.mk_args(&[Ty::new_unit(tcx).into()]));
+ body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(poll_enum, source_info);
+
+ make_coroutine_state_argument_indirect(tcx, &mut body);
+
+ match transform.coroutine_kind {
+ // Iterator::next doesn't accept a pinned argument,
+ // unlike for all other coroutine kinds.
+ CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => {}
+ _ => {
+ make_coroutine_state_argument_pinned(tcx, &mut body);
+ }
+ }
+
+ // Make sure we remove dead blocks to remove
+ // unrelated code from the resume part of the function
+ simplify::remove_dead_blocks(&mut body);
+
+ pm::run_passes_no_validate(tcx, &mut body, &[&abort_unwinding_calls::AbortUnwindingCalls], None);
+
+ // Update the body's def to become the drop glue.
+ let coroutine_instance = body.source.instance;
+ let drop_poll = tcx.require_lang_item(LangItem::FutureDropPoll, None);
+ let drop_instance = InstanceDef::FutureDropPollShim(drop_poll, coroutine_ty);
+
+ // Temporary change MirSource to coroutine's instance so that dump_mir produces more sensible
+ // filename.
+ body.source.instance = coroutine_instance;
+ dump_mir(tcx, false, "coroutine_drop_async", &0, &body, |_, _| Ok(()));
+ body.source.instance = drop_instance;
+
+ body
+}
+
fn insert_term_block<'tcx>(body: &mut Body<'tcx>, kind: TerminatorKind<'tcx>) -> BasicBlock {
let source_info = SourceInfo::outermost(body.span);
body.basic_blocks_mut().push(BasicBlockData {
@@ -1289,6 +1686,40 @@ fn insert_term_block<'tcx>(body: &mut Body<'tcx>, kind: TerminatorKind<'tcx>) ->
})
}
+fn return_poll_ready_assign<'tcx>(tcx: TyCtxt<'tcx>, source_info: SourceInfo) -> Statement<'tcx> {
+ // Poll::Ready(())
+ let poll_def_id = tcx.require_lang_item(LangItem::Poll, None);
+ let args = tcx.mk_args(&[Ty::new_unit(tcx).into()]);
+ let val = Operand::Constant(Box::new(ConstOperand {
+ span: source_info.span,
+ user_ty: None,
+ const_: Const::zero_sized(tcx.types.unit),
+ }));
+ let ready_val = Rvalue::Aggregate(
+ Box::new(AggregateKind::Adt(
+ poll_def_id,
+ VariantIdx::from_usize(0),
+ args,
+ None,
+ None,
+ )),
+ IndexVec::from_raw(vec![val]),
+ );
+ Statement {
+ kind: StatementKind::Assign(Box::new((Place::return_place(), ready_val))),
+ source_info,
+ }
+}
+
+fn insert_poll_ready_block<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> BasicBlock {
+ let source_info = SourceInfo::outermost(body.span);
+ body.basic_blocks_mut().push(BasicBlockData {
+ statements: [return_poll_ready_assign(tcx, source_info)].to_vec(),
+ terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }),
+ is_cleanup: false,
+ })
+}
+
fn insert_panic_block<'tcx>(
tcx: TyCtxt<'tcx>,
body: &mut Body<'tcx>,
@@ -1373,43 +1804,50 @@ fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool {
false
}
+// Poison the coroutine when it unwinds
+fn generate_poison_block_and_redirect_unwinds_there<'tcx>(
+ transform: &TransformVisitor<'tcx>,
+ body: &mut Body<'tcx>,
+) {
+ let source_info = SourceInfo::outermost(body.span);
+ let poison_block = body.basic_blocks_mut().push(BasicBlockData {
+ statements: vec![transform.set_discr(VariantIdx::new(POISONED), source_info)],
+ terminator: Some(Terminator { source_info, kind: TerminatorKind::UnwindResume }),
+ is_cleanup: true,
+ });
+
+ for (idx, block) in body.basic_blocks_mut().iter_enumerated_mut() {
+ let source_info = block.terminator().source_info;
+
+ if let TerminatorKind::UnwindResume = block.terminator().kind {
+ // An existing `Resume` terminator is redirected to jump to our dedicated
+ // "poisoning block" above.
+ if idx != poison_block {
+ *block.terminator_mut() = Terminator {
+ source_info,
+ kind: TerminatorKind::Goto { target: poison_block },
+ };
+ }
+ } else if !block.is_cleanup {
+ // Any terminators that *can* unwind but don't have an unwind target set are also
+ // pointed at our poisoning block (unless they're part of the cleanup path).
+ if let Some(unwind @ UnwindAction::Continue) = block.terminator_mut().unwind_mut() {
+ *unwind = UnwindAction::Cleanup(poison_block);
+ }
+ }
+ }
+}
+
fn create_coroutine_resume_function<'tcx>(
tcx: TyCtxt<'tcx>,
transform: TransformVisitor<'tcx>,
body: &mut Body<'tcx>,
can_return: bool,
+ can_unwind: bool,
) {
- let can_unwind = can_unwind(tcx, body);
-
// Poison the coroutine when it unwinds
if can_unwind {
- let source_info = SourceInfo::outermost(body.span);
- let poison_block = body.basic_blocks_mut().push(BasicBlockData {
- statements: vec![transform.set_discr(VariantIdx::new(POISONED), source_info)],
- terminator: Some(Terminator { source_info, kind: TerminatorKind::UnwindResume }),
- is_cleanup: true,
- });
-
- for (idx, block) in body.basic_blocks_mut().iter_enumerated_mut() {
- let source_info = block.terminator().source_info;
-
- if let TerminatorKind::UnwindResume = block.terminator().kind {
- // An existing `Resume` terminator is redirected to jump to our dedicated
- // "poisoning block" above.
- if idx != poison_block {
- *block.terminator_mut() = Terminator {
- source_info,
- kind: TerminatorKind::Goto { target: poison_block },
- };
- }
- } else if !block.is_cleanup {
- // Any terminators that *can* unwind but don't have an unwind target set are also
- // pointed at our poisoning block (unless they're part of the cleanup path).
- if let Some(unwind @ UnwindAction::Continue) = block.terminator_mut().unwind_mut() {
- *unwind = UnwindAction::Cleanup(poison_block);
- }
- }
- }
+ generate_poison_block_and_redirect_unwinds_there(&transform, body);
}
let mut cases = create_cases(body, &transform, Operation::Resume);
@@ -1441,7 +1879,8 @@ fn create_coroutine_resume_function<'tcx>(
cases.insert(1, (RETURNED, block));
}
- insert_switch(body, cases, &transform, TerminatorKind::Unreachable);
+ let default_block = insert_term_block(body, TerminatorKind::Unreachable);
+ insert_switch(body, cases, &transform, default_block);
make_coroutine_state_argument_indirect(tcx, body);
@@ -1463,16 +1902,22 @@ fn create_coroutine_resume_function<'tcx>(
dump_mir(tcx, false, "coroutine_resume", &0, body, |_, _| Ok(()));
}
-fn insert_clean_drop(body: &mut Body<'_>) -> BasicBlock {
- let return_block = insert_term_block(body, TerminatorKind::Return);
+fn insert_clean_drop<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, has_async_drops: bool) -> BasicBlock {
+ let source_info = SourceInfo::outermost(body.span);
+ let return_block = if has_async_drops {
+ insert_poll_ready_block(tcx, body)
+ } else {
+ insert_term_block(body, TerminatorKind::Return)
+ };
let term = TerminatorKind::Drop {
place: Place::from(SELF_ARG),
target: return_block,
unwind: UnwindAction::Continue,
replace: false,
+ drop: None, // TODO
+ async_fut: None // TODO
};
- let source_info = SourceInfo::outermost(body.span);
// Create a block to destroy an unresumed coroutines. This can only destroy upvars.
body.basic_blocks_mut().push(BasicBlockData {
@@ -1594,7 +2039,9 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
};
let old_ret_ty = body.return_ty();
- assert!(body.coroutine_drop().is_none());
+ assert!(body.coroutine_drop().is_none() && body.coroutine_drop_async().is_none());
+
+ dump_mir(tcx, false, "coroutine_before", &0, body, |_, _| Ok(()));
// The first argument is the coroutine type passed by value
let coroutine_ty = body.local_decls.raw[1].ty;
@@ -1647,12 +2094,15 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
// RETURN_PLACE then is a fresh unused local with type ret_ty.
let old_ret_local = replace_local(RETURN_PLACE, new_ret_ty, body, tcx);
+ let mut has_async_drops = false;
// Replace all occurrences of `ResumeTy` with `&mut Context<'_>` within async bodies.
if matches!(
coroutine_kind,
CoroutineKind::Desugared(CoroutineDesugaring::Async | CoroutineDesugaring::AsyncGen, _)
) {
- transform_async_context(tcx, body);
+ let context_mut_ref = transform_async_context(tcx, body);
+ has_async_drops = expand_async_drops(tcx, body, context_mut_ref, coroutine_kind, coroutine_ty);
+ dump_mir(tcx, false, "coroutine_async_drop_expand", &0, body, |_, _| Ok(()));
}
// We also replace the resume argument and insert an `Assign`.
@@ -1742,7 +2192,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
// Insert `drop(coroutine_struct)` which is used to drop upvars for coroutines in
// the unresumed state.
// This is expanded to a drop ladder in `elaborate_coroutine_drops`.
- let drop_clean = insert_clean_drop(body);
+ let drop_clean = insert_clean_drop(tcx, body, has_async_drops);
dump_mir(tcx, false, "coroutine_pre-elab", &0, body, |_, _| Ok(()));
@@ -1753,13 +2203,27 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
dump_mir(tcx, false, "coroutine_post-transform", &0, body, |_, _| Ok(()));
- // Create a copy of our MIR and use it to create the drop shim for the coroutine
- let drop_shim = create_coroutine_drop_shim(tcx, &transform, coroutine_ty, body, drop_clean);
+ let can_unwind = can_unwind(tcx, body);
- body.coroutine.as_mut().unwrap().coroutine_drop = Some(drop_shim);
+ // Create a copy of our MIR and use it to create the drop shim for the coroutine
+ if has_async_drops {
+ // If coroutine has async drops, generating async drop shim
+ let mut drop_shim = create_coroutine_drop_shim_async(
+ tcx, &transform, coroutine_ty, body, drop_clean, can_unwind
+ );
+ // Run derefer to fix Derefs that are not in the first place
+ deref_finder(tcx, &mut drop_shim);
+ body.coroutine.as_mut().unwrap().coroutine_drop_async = Some(drop_shim);
+ } else {
+ // If coroutine has no async drops, generating sync drop shim
+ let mut drop_shim = create_coroutine_drop_shim(tcx, &transform, coroutine_ty, body, drop_clean);
+ // Run derefer to fix Derefs that are not in the first place
+ deref_finder(tcx, &mut drop_shim);
+ body.coroutine.as_mut().unwrap().coroutine_drop = Some(drop_shim);
+ }
// Create the Coroutine::resume / Future::poll function
- create_coroutine_resume_function(tcx, transform, body, can_return);
+ create_coroutine_resume_function(tcx, transform, body, can_return, can_unwind);
// Run derefer to fix Derefs that are not in the first place
deref_finder(tcx, body);
diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs
index 8575f552f0ae7..0ae2eaea7a837 100644
--- a/compiler/rustc_mir_transform/src/elaborate_drops.rs
+++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs
@@ -331,7 +331,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
// This function should mirror what `collect_drop_flags` does.
for (bb, data) in self.body.basic_blocks.iter_enumerated() {
let terminator = data.terminator();
- let TerminatorKind::Drop { place, target, unwind, replace } = terminator.kind else {
+ let TerminatorKind::Drop { place, target, unwind, replace, drop, async_fut: _ } = terminator.kind else {
continue;
};
@@ -375,6 +375,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
target,
unwind,
bb,
+ drop,
)
}
LookupResult::Parent(None) => {}
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index 67668a216dee3..b692ea04ad501 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -317,6 +317,7 @@ impl<'tcx> Inliner<'tcx> {
| InstanceDef::ReifyShim(_)
| InstanceDef::FnPtrShim(..)
| InstanceDef::ClosureOnceShim { .. }
+ | InstanceDef::FutureDropPollShim(..)
| InstanceDef::DropGlue(..)
| InstanceDef::CloneShim(..)
| InstanceDef::ThreadLocalShim(..)
@@ -491,7 +492,7 @@ impl<'tcx> Inliner<'tcx> {
checker.visit_basic_block_data(bb, blk);
let term = blk.terminator();
- if let TerminatorKind::Drop { ref place, target, unwind, replace: _ } = term.kind {
+ if let TerminatorKind::Drop { ref place, target, unwind, replace: _, drop: _, async_fut: _ } = term.kind {
work_list.push(target);
// If the place doesn't actually need dropping, treat it like a regular goto.
diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs
index d30e0bad81301..1bc5571a0b1a2 100644
--- a/compiler/rustc_mir_transform/src/inline/cycle.rs
+++ b/compiler/rustc_mir_transform/src/inline/cycle.rs
@@ -92,7 +92,7 @@ pub(crate) fn mir_callgraph_reachable<'tcx>(
// This shim does not call any other functions, thus there can be no recursion.
InstanceDef::FnPtrAddrShim(..) => continue,
- InstanceDef::DropGlue(..) => {
+ InstanceDef::DropGlue(..) | InstanceDef::FutureDropPollShim(..) => {
// FIXME: A not fully substituted 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.
diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs
index 89414ce940e05..01dd1ea61fe9f 100644
--- a/compiler/rustc_mir_transform/src/shim.rs
+++ b/compiler/rustc_mir_transform/src/shim.rs
@@ -65,10 +65,31 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
build_call_shim(tcx, instance, Some(Adjustment::RefMut), CallKind::Direct(call_mut))
}
+ ty::InstanceDef::FutureDropPollShim(_def_id, ty) => {
+ let ty::Coroutine(coroutine_def_id, args) = ty.kind() else {
+ bug!("FutureDropPollShim not for coroutine type: ({:?})", instance);
+ };
+
+ let body = tcx.optimized_mir(*coroutine_def_id).coroutine_drop_async().unwrap();
+ let mut body = EarlyBinder::bind(body.clone()).instantiate(tcx, args);
+ debug!("make_shim({:?}) = {:?}", instance, body);
+
+ pm::run_passes(
+ tcx,
+ &mut body,
+ &[
+ &abort_unwinding_calls::AbortUnwindingCalls,
+ &add_call_guards::CriticalCallEdges,
+ ],
+ Some(MirPhase::Runtime(RuntimePhase::Optimized)),
+ );
+ return body;
+ }
ty::InstanceDef::DropGlue(def_id, ty) => {
- // FIXME(#91576): Drop shims for coroutines aren't subject to the MIR passes at the end
- // of this function. Is this intentional?
+ //if let Some(ty::Coroutine(..)) = ty.map(Ty::kind) {
+ // bug!("DropGlue shim must be routed to drop_in_place_future: ({:?})", instance);
+ //}
if let Some(ty::Coroutine(coroutine_def_id, args)) = ty.map(Ty::kind) {
let body = tcx.optimized_mir(*coroutine_def_id).coroutine_drop().unwrap();
let mut body = EarlyBinder::bind(body.clone()).instantiate(tcx, args);
@@ -83,10 +104,8 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
],
Some(MirPhase::Runtime(RuntimePhase::Optimized)),
);
-
return body;
}
-
build_drop_shim(tcx, def_id, ty)
}
ty::InstanceDef::ThreadLocalShim(..) => build_thread_local_shim(tcx, instance),
@@ -246,6 +265,7 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option>)
return_block,
elaborate_drops::Unwind::To(resume_block),
START_BLOCK,
+ None,
);
elaborator.patch
};
@@ -575,6 +595,8 @@ impl<'tcx> CloneShimBuilder<'tcx> {
target: unwind,
unwind: UnwindAction::Terminate(UnwindTerminateReason::InCleanup),
replace: false,
+ drop: None,
+ async_fut: None,
},
/* is_cleanup */ true,
);
@@ -841,6 +863,8 @@ fn build_call_shim<'tcx>(
target: BasicBlock::new(2),
unwind: UnwindAction::Continue,
replace: false,
+ drop: None,
+ async_fut: None,
},
false,
);
@@ -857,6 +881,8 @@ fn build_call_shim<'tcx>(
target: BasicBlock::new(4),
unwind: UnwindAction::Terminate(UnwindTerminateReason::InCleanup),
replace: false,
+ drop: None,
+ async_fut: None,
},
/* is_cleanup */ true,
);
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index b86244e5a4bfd..12200cff5a8c5 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -980,6 +980,7 @@ fn visit_instance_use<'tcx>(
}
}
ty::InstanceDef::DropGlue(_, Some(_))
+ | ty::InstanceDef::FutureDropPollShim(..)
| ty::InstanceDef::VTableShim(..)
| ty::InstanceDef::ReifyShim(..)
| ty::InstanceDef::ClosureOnceShim { .. }
diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs
index 7ff182381b835..7bc49777a84a3 100644
--- a/compiler/rustc_monomorphize/src/partitioning.rs
+++ b/compiler/rustc_monomorphize/src/partitioning.rs
@@ -621,6 +621,7 @@ fn characteristic_def_id_of_mono_item<'tcx>(
| ty::InstanceDef::FnPtrShim(..)
| ty::InstanceDef::ClosureOnceShim { .. }
| ty::InstanceDef::Intrinsic(..)
+ | ty::InstanceDef::FutureDropPollShim(..)
| ty::InstanceDef::DropGlue(..)
| ty::InstanceDef::Virtual(..)
| ty::InstanceDef::CloneShim(..)
@@ -783,6 +784,7 @@ fn mono_item_visibility<'tcx>(
| InstanceDef::Virtual(..)
| InstanceDef::Intrinsic(..)
| InstanceDef::ClosureOnceShim { .. }
+ | InstanceDef::FutureDropPollShim(..)
| InstanceDef::DropGlue(..)
| InstanceDef::CloneShim(..)
| InstanceDef::FnPtrAddrShim(..) => return Visibility::Hidden,
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
index e433460e2ad9d..28760c104e6ee 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
@@ -456,6 +456,9 @@ impl<'tcx> Stable<'tcx> for mir::AssertMessage<'tcx> {
AssertKind::ResumedAfterPanic(coroutine) => {
stable_mir::mir::AssertMessage::ResumedAfterPanic(coroutine.stable(tables))
}
+ AssertKind::ResumedAfterDrop(coroutine) => {
+ stable_mir::mir::AssertMessage::ResumedAfterDrop(coroutine.stable(tables))
+ }
AssertKind::MisalignedPointerDereference { required, found } => {
stable_mir::mir::AssertMessage::MisalignedPointerDereference {
required: required.stable(tables),
@@ -592,7 +595,7 @@ impl<'tcx> Stable<'tcx> for mir::TerminatorKind<'tcx> {
mir::TerminatorKind::UnwindTerminate(_) => TerminatorKind::Abort,
mir::TerminatorKind::Return => TerminatorKind::Return,
mir::TerminatorKind::Unreachable => TerminatorKind::Unreachable,
- mir::TerminatorKind::Drop { place, target, unwind, replace: _ } => {
+ mir::TerminatorKind::Drop { place, target, unwind, replace: _, drop: _, async_fut: _ } => {
TerminatorKind::Drop {
place: place.stable(tables),
target: target.as_usize(),
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
index cffbdc376f1fa..cfd6bface695c 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
@@ -800,6 +800,7 @@ impl<'tcx> Stable<'tcx> for ty::Instance<'tcx> {
| ty::InstanceDef::FnPtrAddrShim(..)
| ty::InstanceDef::ClosureOnceShim { .. }
| ty::InstanceDef::ThreadLocalShim(..)
+ | ty::InstanceDef::FutureDropPollShim(..)
| ty::InstanceDef::DropGlue(..)
| ty::InstanceDef::CloneShim(..)
| ty::InstanceDef::FnPtrShim(..) => stable_mir::mir::mono::InstanceKind::Shim,
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index efd5e3727b9fc..2e97efdd5a934 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -429,6 +429,8 @@ symbols! {
async_call_mut,
async_call_once,
async_closure,
+ async_drop,
+ async_drop_in_place,
async_fn,
async_fn_in_trait,
async_fn_mut,
@@ -703,6 +705,7 @@ symbols! {
dreg_low8,
drop,
drop_in_place,
+ drop_in_place_future,
drop_types_in_const,
dropck_eyepatch,
dropck_parametricity,
@@ -828,6 +831,7 @@ symbols! {
fsub_fast,
fundamental,
future,
+ future_drop_poll,
future_trait,
gdb_script_file,
ge,
diff --git a/compiler/rustc_ty_utils/src/common_traits.rs b/compiler/rustc_ty_utils/src/common_traits.rs
index 51b908881eb49..9bb6f10cebee5 100644
--- a/compiler/rustc_ty_utils/src/common_traits.rs
+++ b/compiler/rustc_ty_utils/src/common_traits.rs
@@ -22,6 +22,10 @@ fn is_unpin_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>)
is_item_raw(tcx, query, LangItem::Unpin)
}
+fn is_async_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
+ is_item_raw(tcx, query, LangItem::AsyncDrop)
+}
+
fn is_item_raw<'tcx>(
tcx: TyCtxt<'tcx>,
query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
@@ -34,5 +38,7 @@ 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, is_async_drop_raw, ..*providers
+ };
}
diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs
index 2d76cf994e437..96b498694b2df 100644
--- a/compiler/rustc_ty_utils/src/instance.rs
+++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -37,22 +37,31 @@ fn resolve_instance<'tcx>(
if ty.needs_drop(tcx, param_env) {
debug!(" => nontrivial drop glue");
match *ty.kind() {
+ ty::Coroutine(coroutine_def_id, ..) => {
+ if tcx.optimized_mir(coroutine_def_id).coroutine_drop_async().is_some() {
+ ty::InstanceDef::Item(tcx.lang_items().drop_in_place_future_fn().unwrap())
+ // drop_in_place_future
+ //bug!("resolve_instance for drop_in_place with async drop coroutine")
+ } else {
+ ty::InstanceDef::DropGlue(def_id, Some(ty))
+ }
+ },
ty::Closure(..)
- | ty::Coroutine(..)
| ty::Tuple(..)
| ty::Adt(..)
| ty::Dynamic(..)
| ty::Array(..)
- | ty::Slice(..) => {}
+ | ty::Slice(..) => { ty::InstanceDef::DropGlue(def_id, Some(ty)) }
// Drop shims can only be built from ADTs.
_ => return Ok(None),
}
-
- ty::InstanceDef::DropGlue(def_id, Some(ty))
} else {
debug!(" => trivial drop glue");
ty::InstanceDef::DropGlue(def_id, None)
}
+ } else if Some(def_id) == tcx.lang_items().future_drop_poll_fn() {
+ let ty = args.type_at(0);
+ ty::InstanceDef::FutureDropPollShim(def_id, ty)
} else {
debug!(" => free item");
// FIXME(effects): we may want to erase the effect param if that is present on this item.
diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs
index 38877f7a77fc4..d4668cf17999f 100644
--- a/compiler/stable_mir/src/mir/body.rs
+++ b/compiler/stable_mir/src/mir/body.rs
@@ -264,6 +264,7 @@ pub enum AssertMessage {
RemainderByZero(Operand),
ResumedAfterReturn(CoroutineKind),
ResumedAfterPanic(CoroutineKind),
+ ResumedAfterDrop(CoroutineKind),
MisalignedPointerDereference { required: Operand, found: Operand },
}
@@ -316,6 +317,22 @@ impl AssertMessage {
_,
)) => Ok("`gen fn` should just keep returning `AssertMessage::None` after panicking"),
+ AssertMessage::ResumedAfterDrop(CoroutineKind::Coroutine(_)) => {
+ Ok("coroutine resumed after async drop")
+ }
+ AssertMessage::ResumedAfterDrop(CoroutineKind::Desugared(
+ CoroutineDesugaring::Async,
+ _,
+ )) => Ok("`async fn` resumed after async drop"),
+ AssertMessage::ResumedAfterDrop(CoroutineKind::Desugared(
+ CoroutineDesugaring::Gen,
+ _,
+ )) => Ok("`async gen fn` resumed after async drop"),
+ AssertMessage::ResumedAfterDrop(CoroutineKind::Desugared(
+ CoroutineDesugaring::AsyncGen,
+ _,
+ )) => Ok("`gen fn` should just keep returning `AssertMessage::None` after async drop"),
+
AssertMessage::BoundsCheck { .. } => Ok("index out of bounds"),
AssertMessage::MisalignedPointerDereference { .. } => {
Ok("misaligned pointer dereference")
diff --git a/compiler/stable_mir/src/mir/pretty.rs b/compiler/stable_mir/src/mir/pretty.rs
index 8b7b488d312cf..72ab427e54bba 100644
--- a/compiler/stable_mir/src/mir/pretty.rs
+++ b/compiler/stable_mir/src/mir/pretty.rs
@@ -286,7 +286,7 @@ pub fn pretty_assert_message(msg: &AssertMessage) -> String {
pretty.push_str(format!("\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\",{pretty_required}, {pretty_found}").as_str());
pretty
}
- AssertMessage::ResumedAfterReturn(_) | AssertMessage::ResumedAfterPanic(_) => {
+ AssertMessage::ResumedAfterReturn(_) | AssertMessage::ResumedAfterPanic(_) | AssertMessage::ResumedAfterDrop(_) => {
msg.description().unwrap().to_string()
}
}
diff --git a/compiler/stable_mir/src/mir/visit.rs b/compiler/stable_mir/src/mir/visit.rs
index 24296e9e8778b..6ba02033aefa0 100644
--- a/compiler/stable_mir/src/mir/visit.rs
+++ b/compiler/stable_mir/src/mir/visit.rs
@@ -427,7 +427,7 @@ pub trait MirVisitor {
| AssertMessage::RemainderByZero(op) => {
self.visit_operand(op, location);
}
- AssertMessage::ResumedAfterReturn(_) | AssertMessage::ResumedAfterPanic(_) => { //nothing to visit
+ AssertMessage::ResumedAfterReturn(_) | AssertMessage::ResumedAfterPanic(_) | AssertMessage::ResumedAfterDrop(_) => { //nothing to visit
}
AssertMessage::MisalignedPointerDereference { required, found } => {
self.visit_operand(required, location);
diff --git a/library/core/src/future/async_drop.rs b/library/core/src/future/async_drop.rs
new file mode 100644
index 0000000000000..3f8541b1f0fc3
--- /dev/null
+++ b/library/core/src/future/async_drop.rs
@@ -0,0 +1,64 @@
+#![unstable(feature = "async_drop", issue = "none")]
+
+use crate::pin::Pin;
+use crate::task::{Context, Poll};
+use core::future::Future;
+
+/// Async version of Drop trait.
+///
+/// When a value is no longer needed, Rust will run a "destructor" on that value.
+/// The most common way that a value is no longer needed is when it goes out of
+/// scope. Destructors may still run in other circumstances, but we're going to
+/// focus on scope for the examples here. To learn about some of those other cases,
+/// please see [the reference] section on destructors.
+///
+/// [the reference]: https://doc.rust-lang.org/reference/destructors.html
+///
+/// ## `Copy` and ([`Drop`]|`AsyncDrop`) are exclusive
+///
+/// You cannot implement both [`Copy`] and ([`Drop`]|`AsyncDrop`) on the same type. Types that
+/// are `Copy` get implicitly duplicated by the compiler, making it very
+/// hard to predict when, and how often destructors will be executed. As such,
+/// these types cannot have destructors.
+#[unstable(feature = "async_drop", issue = "none")]
+#[lang = "async_drop"]
+pub trait AsyncDrop {
+ /// Executes the async destructor for this type.
+ ///
+ /// This method is called implicitly when the value goes out of scope,
+ /// and cannot be called explicitly.
+ ///
+ /// When this method has been called, `self` has not yet been deallocated.
+ /// That only happens after the method is over.
+ ///
+ /// # Panics
+ #[allow(async_fn_in_trait)]
+ async fn drop(self: Pin<&mut Self>);
+}
+
+/// Async drop of coroutine.
+/// Coroutine with internal async drops will have its own drop function in async form.
+/// And required to be polled in loop.
+#[unstable(feature = "async_drop", issue = "none")]
+#[lang = "future_drop_poll"]
+#[allow(unconditional_recursion)]
+pub fn future_drop_poll(fut: Pin<&mut F>, cx: &mut Context<'_>) -> Poll<()>
+where
+ F: Future,
+{
+ // Code here does not matter - this is replaced by the
+ // real implementation by the compiler.
+
+ future_drop_poll(fut, cx)
+}
+
+#[unstable(feature = "async_drop", issue = "none")]
+#[cfg(all(not(no_global_oom_handling), not(test)))]
+#[lang = "drop_in_place_future"]
+fn drop_in_place_future(_to_drop: *mut F)
+where
+ F: Future,
+{
+ // TODO: Support poll loop for async drop of future (future_drop_poll)
+ panic!("Unimplemented drop_in_place_future");
+}
diff --git a/library/core/src/future/mod.rs b/library/core/src/future/mod.rs
index 0f77a2d83433f..71476f78558ea 100644
--- a/library/core/src/future/mod.rs
+++ b/library/core/src/future/mod.rs
@@ -12,6 +12,8 @@
use crate::ptr::NonNull;
use crate::task::Context;
+#[cfg(not(bootstrap))]
+mod async_drop;
mod future;
mod into_future;
mod join;
@@ -36,6 +38,14 @@ pub use ready::{ready, Ready};
#[stable(feature = "future_poll_fn", since = "1.64.0")]
pub use poll_fn::{poll_fn, PollFn};
+#[cfg(not(bootstrap))]
+#[unstable(feature = "async_drop", issue = "none")]
+pub use async_drop::AsyncDrop;
+
+#[cfg(not(bootstrap))]
+#[unstable(feature = "async_drop", issue = "none")]
+pub use async_drop::future_drop_poll;
+
/// This type is needed because:
///
/// a) Coroutines cannot implement `for<'a, 'b> Coroutine<&'a mut Context<'b>>`, so we need to pass
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
index 60c8baf424dbb..eff58d12f8dba 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
@@ -408,6 +408,7 @@ language_item_table! {
ExchangeMalloc, sym::exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None;
BoxFree, sym::box_free, box_free_fn, Target::Fn, GenericRequirement::Minimum(1);
DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1);
+ DropInPlaceFuture, sym::drop_in_place_future,drop_in_place_future_fn, Target::Fn, GenericRequirement::Minimum(1);
AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None;
Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1);
diff --git a/tests/ui/async-await/async-drop/async-drop-future-from-future.rs b/tests/ui/async-await/async-drop/async-drop-future-from-future.rs
new file mode 100644
index 0000000000000..460025583c38a
--- /dev/null
+++ b/tests/ui/async-await/async-drop/async-drop-future-from-future.rs
@@ -0,0 +1,100 @@
+// run-pass
+// check-run-results
+// Future `bar` with internal async drop `Foo` will have async drop itself.
+// And we trying to drop this future in sync context (`block_on` func)
+
+#![feature(async_drop)]
+#![allow(incomplete_features)]
+
+// edition: 2021
+
+use std::mem::ManuallyDrop;
+
+use std::{
+ future::{Future, future_drop_poll, AsyncDrop},
+ pin::{pin, Pin},
+ sync::{mpsc, Arc},
+ task::{Context, Poll, Wake, Waker},
+};
+
+struct Foo {
+ my_resource_handle: usize,
+}
+
+impl Foo {
+ fn new(my_resource_handle: usize) -> Self {
+ let out = Foo {
+ my_resource_handle,
+ };
+ println!("Foo::new({})", my_resource_handle);
+ out
+ }
+}
+
+impl Drop for Foo {
+ fn drop(&mut self) {
+ println!("Foo::drop({})", self.my_resource_handle);
+ }
+}
+
+impl AsyncDrop for Foo {
+ async fn drop(self: Pin<&mut Self>) {
+ println!("Foo::async drop({})", self.my_resource_handle);
+ }
+}
+
+fn main() {
+ block_on(bar(10));
+ println!("done")
+}
+
+async fn baz(ident_base: usize) {
+ let mut _first = Foo::new(ident_base);
+}
+
+async fn bar(ident_base: usize) {
+ let mut _first = Foo::new(ident_base);
+ baz(ident_base + 1).await;
+}
+
+fn block_on(fut_unpin: F) -> F::Output
+where
+ F: Future,
+{
+ let mut fut_pin = pin!(ManuallyDrop::new(fut_unpin));
+ let mut fut: Pin<&mut F> = unsafe {
+ Pin::map_unchecked_mut(fut_pin.as_mut(), |x| &mut **x)
+ };
+ let (waker, rx) = simple_waker();
+ let mut context = Context::from_waker(&waker);
+ let rv = loop {
+ match fut.as_mut().poll(&mut context) {
+ Poll::Ready(out) => break out,
+ // expect wake in polls
+ Poll::Pending => rx.try_recv().unwrap(),
+ }
+ };
+ loop {
+ match future_drop_poll(fut.as_mut(), &mut context) {
+ Poll::Ready(()) => break,
+ // expect wake in polls
+ Poll::Pending => rx.try_recv().unwrap(),
+ }
+ }
+ rv
+}
+
+fn simple_waker() -> (Waker, mpsc::Receiver<()>) {
+ struct SimpleWaker {
+ tx: std::sync::mpsc::Sender<()>,
+ }
+
+ impl Wake for SimpleWaker {
+ fn wake(self: Arc) {
+ self.tx.send(()).unwrap();
+ }
+ }
+
+ let (tx, rx) = mpsc::channel();
+ (Waker::from(Arc::new(SimpleWaker { tx })), rx)
+}
diff --git a/tests/ui/async-await/async-drop/async-drop-future-from-future.run.stdout b/tests/ui/async-await/async-drop/async-drop-future-from-future.run.stdout
new file mode 100644
index 0000000000000..c2663b3f23806
--- /dev/null
+++ b/tests/ui/async-await/async-drop/async-drop-future-from-future.run.stdout
@@ -0,0 +1,5 @@
+Foo::new(10)
+Foo::new(11)
+Foo::async drop(11)
+Foo::async drop(10)
+done
diff --git a/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.rs b/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.rs
new file mode 100644
index 0000000000000..c7cc10fc98fc1
--- /dev/null
+++ b/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.rs
@@ -0,0 +1,82 @@
+// run-pass
+// check-run-results
+// Future `bar` with internal async drop `Foo` will have async drop itself.
+// And we trying to drop this future in sync context (`block_on` func)
+
+#![feature(async_drop)]
+#![allow(incomplete_features)]
+
+// edition: 2021
+
+use std::{
+ future::{Future, AsyncDrop},
+ pin::{pin, Pin},
+ sync::{mpsc, Arc},
+ task::{Context, Poll, Wake, Waker},
+};
+
+struct Foo {
+ my_resource_handle: usize,
+}
+
+impl Foo {
+ fn new(my_resource_handle: usize) -> Self {
+ let out = Foo {
+ my_resource_handle,
+ };
+ println!("Foo::new({})", my_resource_handle);
+ out
+ }
+}
+
+impl Drop for Foo {
+ fn drop(&mut self) {
+ println!("Foo::drop({})", self.my_resource_handle);
+ }
+}
+
+impl AsyncDrop for Foo {
+ async fn drop(self: Pin<&mut Self>) {
+ println!("Foo::async drop({})", self.my_resource_handle);
+ }
+}
+
+fn main() {
+ block_on(bar(10));
+ println!("done")
+}
+
+async fn bar(ident_base: usize) {
+ let mut _first = Foo::new(ident_base);
+}
+
+fn block_on(fut: F) -> F::Output
+where
+ F: Future,
+{
+ let mut fut = pin!(fut);
+ let (waker, rx) = simple_waker();
+ let mut context = Context::from_waker(&waker);
+ loop {
+ match fut.as_mut().poll(&mut context) {
+ Poll::Ready(out) => break out,
+ // expect wake in polls
+ Poll::Pending => rx.try_recv().unwrap(),
+ }
+ }
+}
+
+fn simple_waker() -> (Waker, mpsc::Receiver<()>) {
+ struct SimpleWaker {
+ tx: std::sync::mpsc::Sender<()>,
+ }
+
+ impl Wake for SimpleWaker {
+ fn wake(self: Arc) {
+ self.tx.send(()).unwrap();
+ }
+ }
+
+ let (tx, rx) = mpsc::channel();
+ (Waker::from(Arc::new(SimpleWaker { tx })), rx)
+}
diff --git a/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.run.stderr b/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.run.stderr
new file mode 100644
index 0000000000000..870dae3949a45
--- /dev/null
+++ b/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.run.stderr
@@ -0,0 +1 @@
+Unimplemented drop_in_place_future
diff --git a/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.run.stdout b/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.run.stdout
new file mode 100644
index 0000000000000..4877483d9bbc7
--- /dev/null
+++ b/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.run.stdout
@@ -0,0 +1,2 @@
+Foo::new(10)
+Foo::async drop(10)
diff --git a/tests/ui/async-await/async-drop/async-drop-middle-drop.rs b/tests/ui/async-await/async-drop/async-drop-middle-drop.rs
new file mode 100644
index 0000000000000..a92a763ede255
--- /dev/null
+++ b/tests/ui/async-await/async-drop/async-drop-middle-drop.rs
@@ -0,0 +1,107 @@
+// run-pass
+// check-run-results
+// Test async drop of coroutine `bar` (with internal async drop), stopped at the middle of execution, with AsyncDrop object Foo active.
+
+#![feature(async_drop)]
+#![allow(incomplete_features)]
+
+// edition: 2021
+
+use std::mem::ManuallyDrop;
+
+use std::{
+ future::{Future, future_drop_poll, AsyncDrop},
+ pin::{pin, Pin},
+ sync::{mpsc, Arc},
+ task::{Context, Poll, Wake, Waker},
+};
+
+struct Foo {
+ my_resource_handle: usize,
+}
+
+impl Foo {
+ fn new(my_resource_handle: usize) -> Self {
+ let out = Foo {
+ my_resource_handle,
+ };
+ println!("Foo::new({})", my_resource_handle);
+ out
+ }
+}
+
+impl Drop for Foo {
+ fn drop(&mut self) {
+ println!("Foo::drop({})", self.my_resource_handle);
+ }
+}
+
+impl AsyncDrop for Foo {
+ async fn drop(self: Pin<&mut Self>) {
+ println!("Foo::async drop({})", self.my_resource_handle);
+ }
+}
+
+fn main() {
+ block_on_and_drop_in_the_middle(bar(10));
+ println!("done")
+}
+
+pub struct MiddleFuture {
+ first_call: bool,
+}
+impl Future for MiddleFuture {
+ type Output = ();
+ fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll {
+ if self.first_call {
+ println!("MiddleFuture first poll");
+ self.first_call = false;
+ Poll::Pending
+ } else {
+ println!("MiddleFuture Ready");
+ Poll::Ready(())
+ }
+ }
+}
+
+async fn bar(ident_base: usize) {
+ let middle = MiddleFuture { first_call: true };
+ let mut _first = Foo::new(ident_base);
+ middle.await; // Hanging `bar` future before Foo drop
+}
+
+fn block_on_and_drop_in_the_middle(fut_unpin: F) -> F::Output
+where
+ F: Future