use_prelude!();
pub(in crate)
mod internals {
use super::*;
pub
struct YieldSlot<'yield_slot, YieldedItem, ResumeArg = ()> {
pub(in super)
item_slot: &'yield_slot ItemSlot<YieldedItem, ResumeArg>,
}
}
use internals::YieldSlot;
impl<YieldedItem, ResumeArg>
Drop
for
YieldSlot<'_, YieldedItem, ResumeArg>
{
fn drop (self: &'_ mut Self)
{
self.item_slot.yield_slot_dropped.set(true);
}
}
enum TransferBox<YieldedItem, ResumeArg> {
YieldedItem(YieldedItem),
ResumeArg(ResumeArg),
Empty,
}
impl<YieldedItem, ResumeArg>
Unpin
for
TransferBox<YieldedItem, ResumeArg>
{}
impl<YieldedItem, ResumeArg> TransferBox<YieldedItem, ResumeArg> {
fn take (this: &'_ Cell<Self>)
-> Self
{
this.replace(Self::Empty)
}
}
macro_rules! misusage {( $($tt:tt)* ) => (
::core::format_args! {
"Misusage of a `YieldSlot`: {}", ::core::format_args!($($tt)*),
}
)}
struct ItemSlot<YieldedItem, ResumeArg> {
transfer_box: Cell<TransferBox<YieldedItem, ResumeArg>>,
yield_slot_dropped: Cell<bool>,
}
impl<'yield_slot, YieldedItem, ResumeArg>
YieldSlot<'yield_slot, YieldedItem, ResumeArg>
{
#[inline]
fn new (
item_slot: &'yield_slot ItemSlot<YieldedItem, ResumeArg>,
) -> YieldSlot<'yield_slot, YieldedItem, ResumeArg>
{
Self { item_slot }
}
#[doc(hidden)]
pub
fn __put (
self: &'_ YieldSlot<'yield_slot, YieldedItem, ResumeArg>,
yielded_item: YieldedItem,
) -> impl '_ + Future<Output = ResumeArg>
{
let transfer_box = &self.item_slot.transfer_box;
let prev = transfer_box.replace(TransferBox::YieldedItem(yielded_item));
debug_assert!(
matches!(prev, TransferBox::Empty),
"{}", misusage!("slot was not empty"),
);
poll_fn(move |_| {
match TransferBox::take(transfer_box) {
| yielded_item @ TransferBox::YieldedItem { .. } => {
transfer_box.set(yielded_item);
Poll::Pending
},
| TransferBox::ResumeArg(resume_arg) => Poll::Ready(resume_arg),
| TransferBox::Empty => panic!("{}", misusage!("incorrect poll")),
}
})
}
#[doc(hidden)]
pub
fn __take_initial_arg (
self: &'_ YieldSlot<'yield_slot, YieldedItem, ResumeArg>,
) -> ResumeArg
{
match TransferBox::take(&self.item_slot.transfer_box) {
| TransferBox::ResumeArg(resume_arg) => resume_arg,
| _ => panic!("{}", misusage!("incorrect `take_initial_arg()`")),
}
}
}
pub
struct GeneratorFn<YieldedItem, F : Future, ResumeArg> {
item_slot: ItemSlot<YieldedItem, ResumeArg>,
future: Option<F>,
_pin_sensitive: PhantomPinned,
}
impl<YieldedItem, F : Future, ResumeArg>
Drop
for
GeneratorFn<YieldedItem, F, ResumeArg>
{
fn drop (self: &'_ mut Self)
{
let Self { ref mut future, ref mut item_slot, .. } = *self;
::unwind_safe::with_state(())
.try_eval(move |&mut ()| {
*future = None;
})
.finally(move |()| if item_slot.yield_slot_dropped.get_mut().not() {
macros::abort_with_msg!("\
`::next_gen` fatal runtime error: \
a `YieldSlot` was about to dangle!\
\n\
\n\
This is only possible if the internals of `::next_gen` \
were directly (ab)used, \
by making a `YieldSlot` escape the `#[generator] fn`.\
\n\
Since this could lead to memory unsafety, \
the program will now abort.\
");
})
}
}
struct GeneratorFnPinProjected<'pin, YieldedItem, F : Future, ResumeArg> {
item_slot: &'pin ItemSlot<YieldedItem, ResumeArg>,
future: Pin<&'pin mut F>,
}
impl<YieldedItem, F : Future, ResumeArg>
GeneratorFn<YieldedItem, F, ResumeArg>
{
fn project (self: Pin<&'_ mut GeneratorFn<YieldedItem, F, ResumeArg>>)
-> GeneratorFnPinProjected<'_, YieldedItem, F, ResumeArg>
{
unsafe {
let this = self.get_unchecked_mut();
GeneratorFnPinProjected {
item_slot: &this.item_slot,
future: Pin::new_unchecked(
this.future
.as_mut()
.expect("You must init a GeneratorFn before using it!")
),
}
}
}
pub
fn empty ()
-> GeneratorFn<YieldedItem, F, ResumeArg>
{
Self {
item_slot: ItemSlot {
transfer_box: TransferBox::Empty.into(),
yield_slot_dropped: false.into(),
},
future: None,
_pin_sensitive: PhantomPinned,
}
}
pub
fn init<'pin, 'yield_slot, Args> (
self: Pin<&'pin mut GeneratorFn<YieldedItem, F, ResumeArg>>,
generator_fn: impl FnOnce(YieldSlot<'yield_slot, YieldedItem, ResumeArg>, Args) -> F,
args: Args,
)
where
YieldedItem : 'yield_slot,
ResumeArg : 'yield_slot,
{
assert!(
self.future.is_none(),
"GeneratorFn cannot be initialized multiple times!",
);
unsafe {
let this = self.get_unchecked_mut();
let yield_slot =
YieldSlot::new(
::core::mem::transmute::<
&'pin ItemSlot<YieldedItem, ResumeArg>,
&'yield_slot ItemSlot<YieldedItem, ResumeArg>,
>(
&this.item_slot
)
)
;
this.future = Some(generator_fn(yield_slot, args));
}
}
#[inline]
pub
fn resume (
self: Pin<&'_ mut GeneratorFn<YieldedItem, F, ResumeArg>>,
resume_arg: ResumeArg,
) -> GeneratorState<YieldedItem, F::Output>
{
<Self as Generator<ResumeArg>>::resume(self, resume_arg)
}
}
impl<YieldedItem, F : Future, ResumeArg>
Generator<ResumeArg>
for
GeneratorFn<YieldedItem, F, ResumeArg>
{
type Yield = YieldedItem;
type Return = F::Output;
fn resume (
self: Pin<&'_ mut Self>,
resume_arg: ResumeArg,
) -> GeneratorState<YieldedItem, F::Output>
{
let this = self.project(); let transfer_box = &this.item_slot.transfer_box;
let prev = transfer_box.replace(TransferBox::ResumeArg(resume_arg));
debug_assert!(
matches!(prev, TransferBox::Empty),
"When starting a resume, `TransferBox` is empty",
);
macros::create_context!(cx);
match this.future.poll(&mut cx) {
| Poll::Pending => {
match TransferBox::take(transfer_box)
{
| TransferBox::YieldedItem(yielded_item) => {
return GeneratorState::Yielded(yielded_item);
},
| _ => panic!("{}", misusage!(
"missing `YieldedItem` in `transfer_box`",
)),
}
},
| Poll::Ready(value) => {
return GeneratorState::Returned(value);
},
}
}
}
#[cfg(feature = "alloc")]
pub use call_boxed::CallBoxed;
mod call_boxed;