From f0022807465b577842aacbde44a52ef33b28f454 Mon Sep 17 00:00:00 2001 From: Joshua Wong Date: Wed, 14 Feb 2024 23:38:04 -0600 Subject: [PATCH] fix unsound functions by returning !Unpin coroutines --- src/lib.rs | 53 +++++++++++++++++++++-------------------------------- 1 file changed, 21 insertions(+), 32 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index af0ff9d..1edffed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,7 +53,7 @@ pub mod macro_impl; use core::{ future::Future, ops::{ControlFlow, Coroutine, CoroutineState}, - pin::Pin, + pin::pin, }; use frunk::{ coproduct::{CNil, CoprodInjector, CoprodUninjector, CoproductEmbedder, CoproductSubsetter}, @@ -76,7 +76,7 @@ pub fn run(mut f: F) -> R where F: Coroutine, Yield = CNil, Return = R>, { - let pinned = core::pin::pin!(f); + let pinned = pin!(f); match pinned.resume(Coproduct::Inl(Begin)) { CoroutineState::Yielded(never) => match never {}, CoroutineState::Complete(ret) => ret, @@ -108,14 +108,13 @@ impl EffectGroup for E { /// Create a new effectful computation by applying a "pure" function to the return value of an /// existing computation. pub fn map( - mut g: impl Coroutine, + g: impl Coroutine, f: impl FnOnce(T) -> U, ) -> impl Coroutine { - move |mut injs: I| { + static move |mut injs: I| { + let mut pinned = pin!(g); loop { - // safety: see handle_group() - let pinned = unsafe { Pin::new_unchecked(&mut g) }; - match pinned.resume(injs) { + match pinned.as_mut().resume(injs) { CoroutineState::Yielded(effs) => injs = yield effs, CoroutineState::Complete(ret) => return f(ret), } @@ -196,7 +195,7 @@ pub fn handle_group< BeginIndex, EmbedIndices, >( - mut g: G, + g: G, mut handler: impl FnMut(Es) -> ControlFlow, ) -> impl Coroutine where @@ -208,14 +207,11 @@ where PostIs: CoproductEmbedder, G: Coroutine, { - move |_begin: PostIs| { + static move |_begin: PostIs| { let mut injection = PreIs::inject(Begin); + let mut pinned = pin!(g); loop { - // safety: im 90% sure that since we are inside Coroutine::resume which takes - // Pin<&mut self>, all locals in this function are effectively pinned and this call is - // simply projecting that - let pinned = unsafe { Pin::new_unchecked(&mut g) }; - match pinned.resume(injection) { + match pinned.as_mut().resume(injection) { CoroutineState::Yielded(effs) => match effs.subset() { Ok(effs) => match handler(effs) { ControlFlow::Continue(injs) => injection = injs.embed(), @@ -238,18 +234,16 @@ where /// because it is impossible to construct a computation that is both asynchronous and effectful. /// /// For more flexible interactions with Futures, see [`effects::future`]. -pub async fn handle_async(mut g: G, mut handler: impl FnMut(Eff) -> Fut) -> G::Return +pub async fn handle_async(g: G, mut handler: impl FnMut(Eff) -> Fut) -> G::Return where Eff: Effect, G: Coroutine, Begin), Yield = Coprod!(Eff)>, Fut: Future>, { let mut injs = Coproduct::inject(Begin); + let mut pinned = pin!(g); loop { - // safety: see handle_group() - remember that futures are pinned in the same way as - // coroutines - let pinned = unsafe { Pin::new_unchecked(&mut g) }; - match pinned.resume(injs) { + match pinned.as_mut().resume(injs) { CoroutineState::Yielded(effs) => { let eff = match effs { Coproduct::Inl(v) => v, @@ -285,11 +279,9 @@ where Fut: Future>, { let mut injs = Is::inject(Begin); + let mut pinned = pin!(g); loop { - // safety: see handle_group() - remember that futures are pinned in the same way as - // coroutines - let pinned = unsafe { Pin::new_unchecked(&mut g) }; - match pinned.resume(injs) { + match pinned.as_mut().resume(injs) { CoroutineState::Yielded(effs) => match handler(effs).await { ControlFlow::Continue(new_injs) => injs = new_injs, ControlFlow::Break(ret) => return ret, @@ -329,7 +321,7 @@ pub fn transform< EmbedIndices2, EmbedIndices3, >( - mut g: G1, + g: G1, mut handler: impl FnMut(E) -> H, ) -> impl Coroutine where @@ -350,21 +342,18 @@ where > + CoproductSubsetter, G1: Coroutine, { - move |_begin: PostIs| { + static move |_begin: PostIs| { let mut injection = PreIs::inject(Begin); + let mut pinned = pin!(g); loop { - // safety: see handle_group() - let pinned = unsafe { Pin::new_unchecked(&mut g) }; - match pinned.resume(injection) { + match pinned.as_mut().resume(injection) { CoroutineState::Yielded(effs) => match effs.uninject() { // the effect we are handling Ok(eff) => { - let mut handling = handler(eff); + let mut handling = pin!(handler(eff)); let mut handler_inj = HandlerIs::inject(Begin); 'run_handler: loop { - // safety: same again - let pinned = unsafe { Pin::new_unchecked(&mut handling) }; - match pinned.resume(handler_inj) { + match handling.as_mut().resume(handler_inj) { CoroutineState::Yielded(effs) => { handler_inj = PostIs::subset(yield effs.embed()).ok().unwrap(); },