wip

added proc macros

+211 -46
+51
Cargo.lock
··· 11 11 ] 12 12 13 13 [[package]] 14 + name = "futures-compat" 15 + version = "0.1.0" 16 + dependencies = [ 17 + "futures-core", 18 + ] 19 + 20 + [[package]] 14 21 name = "futures-core" 15 22 version = "0.1.0" 16 23 17 24 [[package]] 25 + name = "futures-derive" 26 + version = "0.1.0" 27 + dependencies = [ 28 + "proc-macro2", 29 + "quote", 30 + "syn", 31 + ] 32 + 33 + [[package]] 18 34 name = "futures-util" 19 35 version = "0.1.0" 20 36 dependencies = [ 21 37 "futures-core", 22 38 ] 39 + 40 + [[package]] 41 + name = "proc-macro2" 42 + version = "1.0.95" 43 + source = "registry+https://github.com/rust-lang/crates.io-index" 44 + checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 45 + dependencies = [ 46 + "unicode-ident", 47 + ] 48 + 49 + [[package]] 50 + name = "quote" 51 + version = "1.0.40" 52 + source = "registry+https://github.com/rust-lang/crates.io-index" 53 + checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 54 + dependencies = [ 55 + "proc-macro2", 56 + ] 57 + 58 + [[package]] 59 + name = "syn" 60 + version = "2.0.104" 61 + source = "registry+https://github.com/rust-lang/crates.io-index" 62 + checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" 63 + dependencies = [ 64 + "proc-macro2", 65 + "quote", 66 + "unicode-ident", 67 + ] 68 + 69 + [[package]] 70 + name = "unicode-ident" 71 + version = "1.0.18" 72 + source = "registry+https://github.com/rust-lang/crates.io-index" 73 + checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
+1 -1
Cargo.toml
··· 1 1 [workspace] 2 2 resolver = "3" 3 - members = [ "futures-combinators", "futures-core", "futures-util"] 3 + members = [ "futures-core", "futures-combinators", "futures-compat", "futures-derive", "futures-util"] 4 4 5 5 [workspace.package] 6 6 version = "0.1.0"
+4 -4
futures-combinators/src/join.rs
··· 27 27 pub trait JoinExt<'scope> { 28 28 fn along_with<Fut>(self, other: Fut) -> Join2<'scope, Self, Fut> 29 29 where 30 - Self: Sized + 'scope + ScopedFuture<'scope>, 31 - Fut: ScopedFuture<'scope> + 'scope, 30 + Self: Sized + ScopedFuture<'scope>, 31 + Fut: ScopedFuture<'scope>, 32 32 { 33 33 (self, other).join() 34 34 } ··· 60 60 refs: $namespace::WakerRefs<'scope>, 61 61 } 62 62 63 - impl<'scope, $($F: ScopedFuture<'scope> + 'scope),+> ScopedFuture<'scope> 63 + impl<'scope, $($F: ScopedFuture<'scope>),+> ScopedFuture<'scope> 64 64 for $StructName<'scope, $($F),+> 65 65 { 66 66 type Output = ($($F::Output),+); ··· 106 106 } 107 107 } 108 108 109 - impl<'scope, $($F: ScopedFuture<'scope> + 'scope),+> Join<'scope> for ($($F),+) { 109 + impl<'scope, $($F: ScopedFuture<'scope>),+> Join<'scope> for ($($F),+) { 110 110 type Output = ($($F::Output),*); 111 111 type Future = $StructName<'scope, $($F),+>; 112 112
+12
futures-compat/Cargo.toml
··· 1 + [package] 2 + name = "futures-compat" 3 + version.workspace = true 4 + rust-version.workspace = true 5 + edition.workspace = true 6 + license.workspace = true 7 + authors.workspace = true 8 + repository.workspace = true 9 + homepage.workspace = true 10 + 11 + [dependencies] 12 + futures-core = { path = "../futures-core" }
+17 -1
futures-core/src/compat.rs futures-compat/src/lib.rs
··· 12 12 //! unsoundness if a ScopedFuture internally registers the waker with something 13 13 //! that expects it to live for 'scope, and then the ScopedFutureWrapper is 14 14 //! dropped 15 + //! 16 + //! 17 + //! ## TRIGGER WARNING 18 + //! 19 + //! This code is not for the faint of heart. Read at your own risk. 15 20 16 - use crate::{ScopedFuture, Wake}; 17 21 use std::{ 18 22 cell::UnsafeCell, 19 23 marker::PhantomData, ··· 21 25 pin::Pin, 22 26 task::{Context, Poll, Waker}, 23 27 }; 28 + 29 + use futures_core::{ScopedFuture, Wake}; 24 30 25 31 /// RawWaker: fat ptr (*const (), &'static RawWakerVTable) 26 32 /// &'scope dyn Wake fat ptr: (&'scope (), &'scope WakeVTable) ··· 142 148 } 143 149 } 144 150 } 151 + 152 + fn test(a: &i32) -> impl ScopedFuture<'_> + '_ { 153 + async fn inner(a: &i32) -> i32 { 154 + a + 1 155 + } 156 + 157 + let x = inner(a); 158 + 159 + unsafe { UnscopedFutureWrapper::from_future(inner(a)) } 160 + }
-1
futures-core/src/lib.rs
··· 1 1 use std::task::Poll; 2 2 3 - mod compat; 4 3 mod task; 5 4 6 5 pub use crate::task::Wake;
+17
futures-derive/Cargo.toml
··· 1 + [lib] 2 + proc-macro = true 3 + 4 + [package] 5 + name = "futures-derive" 6 + version.workspace = true 7 + rust-version.workspace = true 8 + edition.workspace = true 9 + license.workspace = true 10 + authors.workspace = true 11 + repository.workspace = true 12 + homepage.workspace = true 13 + 14 + [dependencies] 15 + proc-macro2 = "1.0" 16 + quote = "1.0" 17 + syn = { version = "2.0", features = ["full", "visit-mut" ] }
+103
futures-derive/src/lib.rs
··· 1 + use proc_macro::TokenStream; 2 + use quote::quote; 3 + use syn::{ 4 + Expr, ExprAwait, FnArg, ItemFn, Pat, ReturnType, Signature, 5 + parse_macro_input, visit_mut::VisitMut, 6 + }; 7 + 8 + #[proc_macro_attribute] 9 + pub fn async_scoped(_: TokenStream, item: TokenStream) -> TokenStream { 10 + let mut input = parse_macro_input!(item as ItemFn); 11 + 12 + // Wraps *every* async expression within the function block with 13 + // `ScopedFutureWrapper`, allowing them to be treated as regular `Future` 14 + // impls. 15 + // 16 + // This will cause a compiler error if any expression being awaited is not 17 + // a `ScopedFuture`, which is intentional because the `Future` and 18 + // `ScopedFuture` systems are incompatible. 19 + ScopedFutureWrappingVisitor.visit_item_fn_mut(&mut input); 20 + 21 + // Wrap the function with `UnscopedFutureWrapper` to convert it back into 22 + // a `ScopedFuture`. 23 + wrap_async_with_scoped(&input).into() 24 + } 25 + 26 + /// Takes async fn that returns anonymous `Future` impl. 27 + /// Generates fn that returns `UnscopedFutureWrapper` wrapper for the the anonymous `Future` impl. 28 + /// 29 + /// ```rust 30 + /// fn [original name]<'a, 'b>(a: &'a A, b: &'b B) -> impl ScopedFuture<'a + 'b, Output = T> + 'a + 'b { 31 + /// async fn [__inner]<'a, 'b>(a: &'a A, b: &'b B) -> T { [body] } // compilers turns this into -> impl Future<Output = T> + 'a + 'b 32 + /// unsafe { UnscopedFutureWrapper::from_future(__inner()) } 33 + /// } 34 + /// ``` 35 + /// 36 + /// see https://rust-lang.github.io/rfcs/2394-async_await.html#lifetime-capture-in-the-anonymous-future 37 + /// for more context on lifetime capture 38 + /// - resulting ScopedFuture needs to be constrained to not outlive the lifetimes of any references 39 + /// 40 + /// to actually implement this (capture all lifetimes) we use `ScopedFuture<'_> + '_` so the compiler can infer 41 + /// lifetimes from the anonymous future impl returned by the actual inner async fn 42 + fn wrap_async_with_scoped( 43 + ItemFn { 44 + attrs, 45 + vis, 46 + sig: 47 + Signature { 48 + constness, 49 + unsafety, 50 + ident, 51 + generics, 52 + inputs, 53 + output, 54 + .. 55 + }, 56 + block, 57 + }: &ItemFn, 58 + ) -> proc_macro2::TokenStream { 59 + let output = match output { 60 + ReturnType::Default => quote! { () }, 61 + ReturnType::Type(_, ty) => quote! { #ty }, 62 + }; 63 + 64 + let inner_args: Vec<syn::Ident> = inputs 65 + .iter() 66 + .filter_map(|param| match param { 67 + FnArg::Receiver(_) => Some(quote::format_ident!("self")), 68 + FnArg::Typed(typed) => { 69 + if let Pat::Ident(ident) = &*typed.pat { 70 + Some(ident.ident.to_owned()) 71 + } else { 72 + None 73 + } 74 + } 75 + }) 76 + .collect(); 77 + 78 + quote! { 79 + #(#attrs)* #vis #constness #unsafety fn #ident #generics (#inputs) -> impl ScopedFuture<'_, Output = #output> + '_ { 80 + async fn #constness #unsafety fn __inner (#inputs) -> #output #block 81 + 82 + let future = __inner(#(#inner_args),*); 83 + 84 + unsafe { futures_compat::UnscopedFutureWrapper::from_future(future) } 85 + } 86 + } 87 + } 88 + 89 + /// Uses the `syn::visit_mut` api to wrap every `.await` expression in 90 + /// `ScopedFutureWrapper`. 91 + struct ScopedFutureWrappingVisitor; 92 + 93 + impl VisitMut for ScopedFutureWrappingVisitor { 94 + fn visit_expr_mut(&mut self, expr: &mut syn::Expr) { 95 + if let Expr::Await(ExprAwait { attrs, base, .. }) = expr { 96 + *expr = syn::parse_quote! { 97 + unsafe { futures_compat::ScopedFutureWrapper::from_scoped(#(#attrs)* #base) }.await 98 + }; 99 + } 100 + 101 + syn::visit_mut::visit_expr_mut(self, expr); 102 + } 103 + }
+5
futures/Cargo.toml
··· 9 9 homepage.workspace = true 10 10 11 11 [dependencies] 12 + futures-core = { path = "../futures-core" } 13 + futures-combinators = { path = "../futures-combinators" } 14 + futures-compat = { path = "../futures-compat" } 15 + futures-derive = { path = "../futures-derive" } 16 + futures-util = { path = "../futures-util" }
+1 -39
futures/src/lib.rs
··· 1 - mod combinators; 2 - mod future; 3 - mod utils; 4 - 5 - /// from yoshuawuyts/futures-concurrency 6 - /// Wait for all futures to complete. 7 - /// 8 - /// Awaits multiple futures simultaneously, returning the output of the futures 9 - /// in the same container type they were created once all complete. 10 - 11 - // scoped future combinators: 12 - // 13 - // Join<N> 14 - // TryJoin 15 - // Race 16 - // RaceOk 17 - // 18 - // add Deadline(a, rest) (deadline_against()) 19 - // also functionality like (a, b, c).join().race_against(d, e, f) 20 - // 21 - // UnorderedJoinQueueStream? is this VecJoinStream? 22 - // OrderedJoinQueueStream 23 - 24 - // pub trait ScopedStream<'scope> { 25 - // type Item; 26 - 27 - // fn poll_next(self: Pin<&mut Self>, cx: &'scope mut dyn ScopedWake) -> Poll<Option<Self::Item>>; 28 - // } 29 - 30 - #[cfg(test)] 31 - mod tests { 32 - use super::*; 33 - 34 - #[test] 35 - fn it_works() { 36 - let result = add(2, 2); 37 - assert_eq!(result, 4); 38 - } 39 - } 1 + fn test() {}