diff --git a/Cargo.toml b/Cargo.toml index 56d24c2e90..f972a73d2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,9 @@ members = [ "futures-util", "futures-test", + "futures/tests/macro-tests", + "futures/tests/macro-reexport", + "examples/functional", "examples/imperative", ] diff --git a/futures-macro/src/join.rs b/futures-macro/src/join.rs index 2a7b11ca6b..e932605a0f 100644 --- a/futures-macro/src/join.rs +++ b/futures-macro/src/join.rs @@ -4,15 +4,10 @@ use proc_macro::TokenStream; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{format_ident, quote}; use syn::parse::{Parse, ParseStream}; -use syn::{parenthesized, parse_quote, Expr, Ident, Token}; - -mod kw { - syn::custom_keyword!(futures_crate_path); -} +use syn::{Expr, Ident, Token}; #[derive(Default)] struct Join { - futures_crate_path: Option, fut_exprs: Vec, } @@ -20,16 +15,6 @@ impl Parse for Join { fn parse(input: ParseStream<'_>) -> syn::Result { let mut join = Join::default(); - // When `futures_crate_path(::path::to::futures::lib)` is provided, - // it sets the path through which futures library functions will be - // accessed. - if input.peek(kw::futures_crate_path) { - input.parse::()?; - let content; - parenthesized!(content in input); - join.futures_crate_path = Some(content.parse()?); - } - while !input.is_empty() { join.fut_exprs.push(input.parse::()?); @@ -43,7 +28,6 @@ impl Parse for Join { } fn bind_futures( - futures_crate: &syn::Path, fut_exprs: Vec, span: Span, ) -> (Vec, Vec) { @@ -56,7 +40,7 @@ fn bind_futures( future_let_bindings.push(quote! { // Move future into a local so that it is pinned in one place and // is no longer accessible by the end user. - let mut #name = #futures_crate::future::maybe_done(#expr); + let mut #name = __futures_crate::future::maybe_done(#expr); }); name }) @@ -69,39 +53,35 @@ fn bind_futures( pub(crate) fn join(input: TokenStream) -> TokenStream { let parsed = syn::parse_macro_input!(input as Join); - let futures_crate = parsed - .futures_crate_path - .unwrap_or_else(|| parse_quote!(::futures_util)); - // should be def_site, but that's unstable let span = Span::call_site(); - let (future_let_bindings, future_names) = bind_futures(&futures_crate, parsed.fut_exprs, span); + let (future_let_bindings, future_names) = bind_futures(parsed.fut_exprs, span); let poll_futures = future_names.iter().map(|fut| { quote! { - __all_done &= #futures_crate::core_reexport::future::Future::poll( - unsafe { #futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }, __cx).is_ready(); + __all_done &= __futures_crate::future::Future::poll( + unsafe { __futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }, __cx).is_ready(); } }); let take_outputs = future_names.iter().map(|fut| { quote! { - unsafe { #futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }.take_output().unwrap(), + unsafe { __futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }.take_output().unwrap(), } }); TokenStream::from(quote! { { #( #future_let_bindings )* - #futures_crate::future::poll_fn(move |__cx: &mut #futures_crate::task::Context<'_>| { + __futures_crate::future::poll_fn(move |__cx: &mut __futures_crate::task::Context<'_>| { let mut __all_done = true; #( #poll_futures )* if __all_done { - #futures_crate::core_reexport::task::Poll::Ready(( + __futures_crate::task::Poll::Ready(( #( #take_outputs )* )) } else { - #futures_crate::core_reexport::task::Poll::Pending + __futures_crate::task::Poll::Pending } }).await } }) @@ -111,29 +91,25 @@ pub(crate) fn join(input: TokenStream) -> TokenStream { pub(crate) fn try_join(input: TokenStream) -> TokenStream { let parsed = syn::parse_macro_input!(input as Join); - let futures_crate = parsed - .futures_crate_path - .unwrap_or_else(|| parse_quote!(::futures_util)); - // should be def_site, but that's unstable let span = Span::call_site(); - let (future_let_bindings, future_names) = bind_futures(&futures_crate, parsed.fut_exprs, span); + let (future_let_bindings, future_names) = bind_futures(parsed.fut_exprs, span); let poll_futures = future_names.iter().map(|fut| { quote! { - if #futures_crate::core_reexport::future::Future::poll( - unsafe { #futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }, __cx).is_pending() + if __futures_crate::future::Future::poll( + unsafe { __futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }, __cx).is_pending() { __all_done = false; - } else if unsafe { #futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }.output_mut().unwrap().is_err() { + } else if unsafe { __futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }.output_mut().unwrap().is_err() { // `.err().unwrap()` rather than `.unwrap_err()` so that we don't introduce // a `T: Debug` bound. // Also, for an error type of ! any code after `err().unwrap()` is unreachable. #[allow(unreachable_code)] - return #futures_crate::core_reexport::task::Poll::Ready( - #futures_crate::core_reexport::result::Result::Err( - unsafe { #futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }.take_output().unwrap().err().unwrap() + return __futures_crate::task::Poll::Ready( + __futures_crate::core_reexport::result::Result::Err( + unsafe { __futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }.take_output().unwrap().err().unwrap() ) ); } @@ -145,7 +121,7 @@ pub(crate) fn try_join(input: TokenStream) -> TokenStream { // an `E: Debug` bound. // Also, for an ok type of ! any code after `ok().unwrap()` is unreachable. #[allow(unreachable_code)] - unsafe { #futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }.take_output().unwrap().ok().unwrap(), + unsafe { __futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }.take_output().unwrap().ok().unwrap(), } }); @@ -153,17 +129,17 @@ pub(crate) fn try_join(input: TokenStream) -> TokenStream { #( #future_let_bindings )* #[allow(clippy::diverging_sub_expression)] - #futures_crate::future::poll_fn(move |__cx: &mut #futures_crate::task::Context<'_>| { + __futures_crate::future::poll_fn(move |__cx: &mut __futures_crate::task::Context<'_>| { let mut __all_done = true; #( #poll_futures )* if __all_done { - #futures_crate::core_reexport::task::Poll::Ready( - #futures_crate::core_reexport::result::Result::Ok(( + __futures_crate::task::Poll::Ready( + __futures_crate::core_reexport::result::Result::Ok(( #( #take_outputs )* )) ) } else { - #futures_crate::core_reexport::task::Poll::Pending + __futures_crate::task::Poll::Pending } }).await } }) diff --git a/futures-macro/src/lib.rs b/futures-macro/src/lib.rs index 932ec2e4f1..b565c290c6 100644 --- a/futures-macro/src/lib.rs +++ b/futures-macro/src/lib.rs @@ -23,24 +23,24 @@ mod select; /// The `join!` macro. #[proc_macro_hack] -pub fn join(input: TokenStream) -> TokenStream { +pub fn join_internal(input: TokenStream) -> TokenStream { crate::join::join(input) } /// The `try_join!` macro. #[proc_macro_hack] -pub fn try_join(input: TokenStream) -> TokenStream { +pub fn try_join_internal(input: TokenStream) -> TokenStream { crate::join::try_join(input) } /// The `select!` macro. #[proc_macro_hack] -pub fn select(input: TokenStream) -> TokenStream { +pub fn select_internal(input: TokenStream) -> TokenStream { crate::select::select(input) } /// The `select_biased!` macro. #[proc_macro_hack] -pub fn select_biased(input: TokenStream) -> TokenStream { +pub fn select_biased_internal(input: TokenStream) -> TokenStream { crate::select::select_biased(input) } diff --git a/futures-macro/src/select.rs b/futures-macro/src/select.rs index f7f688b6ae..f615b5399c 100644 --- a/futures-macro/src/select.rs +++ b/futures-macro/src/select.rs @@ -3,16 +3,14 @@ use proc_macro::TokenStream; use proc_macro2::Span; use quote::{format_ident, quote}; -use syn::{parenthesized, parse_quote, Expr, Ident, Pat, Token}; +use syn::{parse_quote, Expr, Ident, Pat, Token}; use syn::parse::{Parse, ParseStream}; mod kw { syn::custom_keyword!(complete); - syn::custom_keyword!(futures_crate_path); } struct Select { - futures_crate_path: Option, // span of `complete`, then expression after `=> ...` complete: Option, default: Option, @@ -30,23 +28,12 @@ enum CaseKind { impl Parse for Select { fn parse(input: ParseStream<'_>) -> syn::Result { let mut select = Select { - futures_crate_path: None, complete: None, default: None, normal_fut_exprs: vec![], normal_fut_handlers: vec![], }; - // When `futures_crate_path(::path::to::futures::lib)` is provided, - // it sets the path through which futures library functions will be - // accessed. - if input.peek(kw::futures_crate_path) { - input.parse::()?; - let content; - parenthesized!(content in input); - select.futures_crate_path = Some(content.parse()?); - } - while !input.is_empty() { let case_kind = if input.peek(kw::complete) { // `complete` @@ -147,8 +134,6 @@ pub(crate) fn select_biased(input: TokenStream) -> TokenStream { fn select_inner(input: TokenStream, random: bool) -> TokenStream { let parsed = syn::parse_macro_input!(input as Select); - let futures_crate: syn::Path = parsed.futures_crate_path.unwrap_or_else(|| parse_quote!(::futures_util)); - // should be def_site, but that's unstable let span = Span::call_site(); @@ -175,8 +160,8 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { // We check for this condition here in order to be able to // safely use Pin::new_unchecked(&mut #path) later on. future_let_bindings.push(quote! { - #futures_crate::async_await::assert_fused_future(&#path); - #futures_crate::async_await::assert_unpin(&#path); + __futures_crate::async_await::assert_fused_future(&#path); + __futures_crate::async_await::assert_unpin(&#path); }); path }, @@ -214,28 +199,28 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { // 2. The Future is created in scope of the select! function and will // not be moved for the duration of it. It is thereby stack-pinned quote! { - let mut #variant_name = |__cx: &mut #futures_crate::task::Context<'_>| { + let mut #variant_name = |__cx: &mut __futures_crate::task::Context<'_>| { let mut #bound_future_name = unsafe { ::core::pin::Pin::new_unchecked(&mut #bound_future_name) }; - if #futures_crate::future::FusedFuture::is_terminated(&#bound_future_name) { + if __futures_crate::future::FusedFuture::is_terminated(&#bound_future_name) { None } else { - Some(#futures_crate::future::FutureExt::poll_unpin( + Some(__futures_crate::future::FutureExt::poll_unpin( &mut #bound_future_name, __cx, ).map(#enum_ident::#variant_name)) } }; let #variant_name: &mut dyn FnMut( - &mut #futures_crate::task::Context<'_> - ) -> Option<#futures_crate::task::Poll<_>> = &mut #variant_name; + &mut __futures_crate::task::Context<'_> + ) -> Option<__futures_crate::task::Poll<_>> = &mut #variant_name; } }); let none_polled = if parsed.complete.is_some() { quote! { - #futures_crate::task::Poll::Ready(#enum_ident::Complete) + __futures_crate::task::Poll::Ready(#enum_ident::Complete) } } else { quote! { @@ -267,13 +252,13 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { let await_select_fut = if parsed.default.is_some() { // For select! with default this returns the Poll result quote! { - __poll_fn(&mut #futures_crate::task::Context::from_waker( - #futures_crate::task::noop_waker_ref() + __poll_fn(&mut __futures_crate::task::Context::from_waker( + __futures_crate::task::noop_waker_ref() )) } } else { quote! { - #futures_crate::future::poll_fn(__poll_fn).await + __futures_crate::future::poll_fn(__poll_fn).await } }; @@ -281,7 +266,7 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { // For select! with default __select_result is a Poll, otherwise not quote! { match __select_result { - #futures_crate::task::Poll::Ready(result) => match result { + __futures_crate::task::Poll::Ready(result) => match result { #branches }, _ => #default_expr @@ -297,7 +282,7 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { let shuffle = if random { quote! { - #futures_crate::async_await::shuffle(&mut __select_arr); + __futures_crate::async_await::shuffle(&mut __select_arr); } } else { quote!() @@ -309,7 +294,7 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { let __select_result = { #( #future_let_bindings )* - let mut __poll_fn = |__cx: &mut #futures_crate::task::Context<'_>| { + let mut __poll_fn = |__cx: &mut __futures_crate::task::Context<'_>| { let mut __any_polled = false; #( #poll_functions )* @@ -318,12 +303,12 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { #shuffle for poller in &mut __select_arr { let poller: &mut &mut dyn FnMut( - &mut #futures_crate::task::Context<'_> - ) -> Option<#futures_crate::task::Poll<_>> = poller; + &mut __futures_crate::task::Context<'_> + ) -> Option<__futures_crate::task::Poll<_>> = poller; match poller(__cx) { - Some(x @ #futures_crate::task::Poll::Ready(_)) => + Some(x @ __futures_crate::task::Poll::Ready(_)) => return x, - Some(#futures_crate::task::Poll::Pending) => { + Some(__futures_crate::task::Poll::Pending) => { __any_polled = true; } None => {} @@ -333,7 +318,7 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { if !__any_polled { #none_polled } else { - #futures_crate::task::Poll::Pending + __futures_crate::task::Poll::Pending } }; diff --git a/futures-util/src/async_await/join_mod.rs b/futures-util/src/async_await/join_mod.rs index 2e27f2149a..909cd3b61b 100644 --- a/futures-util/src/async_await/join_mod.rs +++ b/futures-util/src/async_await/join_mod.rs @@ -2,8 +2,6 @@ use proc_macro_hack::proc_macro_hack; -#[doc(hidden)] -#[macro_export] macro_rules! document_join_macro { ($join:item $try_join:item) => { /// Polls multiple futures simultaneously, returning a tuple @@ -73,10 +71,32 @@ macro_rules! document_join_macro { } } +#[doc(hidden)] +#[proc_macro_hack(support_nested)] +pub use futures_macro::join_internal; + +#[doc(hidden)] +#[proc_macro_hack(support_nested)] +pub use futures_macro::try_join_internal; + document_join_macro! { - #[proc_macro_hack(support_nested)] - pub use futures_macro::join; + #[macro_export] + macro_rules! join { + ($($tokens:tt)*) => {{ + use $crate::__reexport as __futures_crate; + $crate::join_internal! { + $( $tokens )* + } + }} + } - #[proc_macro_hack(support_nested)] - pub use futures_macro::try_join; + #[macro_export] + macro_rules! try_join { + ($($tokens:tt)*) => {{ + use $crate::__reexport as __futures_crate; + $crate::try_join_internal! { + $( $tokens )* + } + }} + } } diff --git a/futures-util/src/async_await/select_mod.rs b/futures-util/src/async_await/select_mod.rs index 38153c7f7a..0471f09182 100644 --- a/futures-util/src/async_await/select_mod.rs +++ b/futures-util/src/async_await/select_mod.rs @@ -2,8 +2,6 @@ use proc_macro_hack::proc_macro_hack; -#[doc(hidden)] -#[macro_export] macro_rules! document_select_macro { // This branch is required for `futures 0.3.1`, from before select_biased was introduced ($select:item) => { @@ -158,7 +156,7 @@ macro_rules! document_select_macro { }; ($select:item $select_biased:item) => { - $crate::document_select_macro!($select); + document_select_macro!($select); /// Polls multiple futures and streams simultaneously, executing the branch /// for the future that finishes first. Unlike [`select!`], if multiple futures are ready, @@ -310,11 +308,34 @@ macro_rules! document_select_macro { }; } +#[cfg(feature = "std")] +#[doc(hidden)] +#[proc_macro_hack(support_nested)] +pub use futures_macro::select_internal; + +#[doc(hidden)] +#[proc_macro_hack(support_nested)] +pub use futures_macro::select_biased_internal; + document_select_macro! { #[cfg(feature = "std")] - #[proc_macro_hack(support_nested)] - pub use futures_macro::select; + #[macro_export] + macro_rules! select { + ($($tokens:tt)*) => {{ + use $crate::__reexport as __futures_crate; + $crate::select_internal! { + $( $tokens )* + } + }} + } - #[proc_macro_hack(support_nested)] - pub use futures_macro::select_biased; + #[macro_export] + macro_rules! select_biased { + ($($tokens:tt)*) => {{ + use $crate::__reexport as __futures_crate; + $crate::select_biased_internal! { + $( $tokens )* + } + }} + } } diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs index 21645b148d..67067a63ea 100644 --- a/futures-util/src/lib.rs +++ b/futures-util/src/lib.rs @@ -51,6 +51,14 @@ pub use self::async_await::*; #[doc(hidden)] pub use futures_core::core_reexport; +// Not public API. +#[cfg(feature = "async-await")] +#[doc(hidden)] +pub mod __reexport { + #[doc(hidden)] + pub use crate::*; +} + macro_rules! cfg_target_has_atomic { ($($item:item)*) => {$( #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] diff --git a/futures/src/lib.rs b/futures/src/lib.rs index c4acbc0fd1..c8e8248d73 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -537,69 +537,10 @@ pub mod never { // proc-macro re-export -------------------------------------- -// Not public API. -#[doc(hidden)] -pub use futures_core::core_reexport; - -// Not public API. -#[cfg(feature = "async-await")] -#[doc(hidden)] -pub use futures_util::async_await; - -// Not public API. #[cfg(feature = "async-await")] -#[doc(hidden)] -pub mod inner_macro { - pub use futures_util::join; - pub use futures_util::try_join; - #[cfg(feature = "std")] - pub use futures_util::select; - pub use futures_util::select_biased; -} - +pub use futures_util::{join, try_join}; +#[cfg(feature = "std")] #[cfg(feature = "async-await")] -futures_util::document_join_macro! { - #[macro_export] - macro_rules! join { // replace `::futures_util` with `::futures` as the crate path - ($($tokens:tt)*) => { - $crate::inner_macro::join! { - futures_crate_path ( ::futures ) - $( $tokens )* - } - } - } - - #[macro_export] - macro_rules! try_join { // replace `::futures_util` with `::futures` as the crate path - ($($tokens:tt)*) => { - $crate::inner_macro::try_join! { - futures_crate_path ( ::futures ) - $( $tokens )* - } - } - } -} - +pub use futures_util::select; #[cfg(feature = "async-await")] -futures_util::document_select_macro! { - #[cfg(feature = "std")] - #[macro_export] - macro_rules! select { // replace `::futures_util` with `::futures` as the crate path - ($($tokens:tt)*) => { - $crate::inner_macro::select! { - futures_crate_path ( ::futures ) - $( $tokens )* - } - } - } - - #[macro_export] - macro_rules! select_biased { // replace `::futures_util` with `::futures` as the crate path - ($($tokens:tt)*) => { - $crate::inner_macro::select_biased! { - futures_crate_path ( ::futures ) - $( $tokens )* - } - } - } -} +pub use futures_util::select_biased; diff --git a/futures/tests/macro-reexport/Cargo.toml b/futures/tests/macro-reexport/Cargo.toml new file mode 100644 index 0000000000..940b537ea7 --- /dev/null +++ b/futures/tests/macro-reexport/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "macro-reexport" +version = "0.1.0" +authors = ["Taiki Endo "] +edition = "2018" +publish = false + +[dependencies] +futures03 = { path = "../..", package = "futures" } diff --git a/futures/tests/macro-reexport/src/lib.rs b/futures/tests/macro-reexport/src/lib.rs new file mode 100644 index 0000000000..49691c9e07 --- /dev/null +++ b/futures/tests/macro-reexport/src/lib.rs @@ -0,0 +1,8 @@ +// normal reexport +pub use futures03::{join, try_join, select, select_biased}; + +// reexport + rename +pub use futures03::{ + join as join2, try_join as try_join2, + select as select2, select_biased as select_biased2, +}; diff --git a/futures/tests/macro-tests/Cargo.toml b/futures/tests/macro-tests/Cargo.toml new file mode 100644 index 0000000000..3d30563868 --- /dev/null +++ b/futures/tests/macro-tests/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "macro-tests" +version = "0.1.0" +authors = ["Taiki Endo "] +edition = "2018" +publish = false + +[dependencies] +futures03 = { path = "../..", package = "futures" } +macro-reexport = { path = "../macro-reexport" } diff --git a/futures/tests/macro-tests/src/main.rs b/futures/tests/macro-tests/src/main.rs new file mode 100644 index 0000000000..5d11f60834 --- /dev/null +++ b/futures/tests/macro-tests/src/main.rs @@ -0,0 +1,69 @@ +// Check that it works even if proc-macros are reexported. + +fn main() { + use futures03::{executor::block_on, future}; + + // join! macro + let _ = block_on(async { + let _ = futures03::join!(async {}, async {}); + let _ = macro_reexport::join!(async {}, async {}); + let _ = macro_reexport::join2!(async {}, async {}); + }); + + // try_join! macro + let _ = block_on(async { + let _ = futures03::try_join!(async { Ok::<(), ()>(()) }, async { Ok::<(), ()>(()) }); + let _ = macro_reexport::try_join!(async { Ok::<(), ()>(()) }, async { Ok::<(), ()>(()) }); + let _ = macro_reexport::try_join2!(async { Ok::<(), ()>(()) }, async { Ok::<(), ()>(()) }); + Ok::<(), ()>(()) + }); + + // select! macro + let _ = block_on(async { + let mut a = future::ready(()); + let mut b = future::pending::<()>(); + let _ = futures03::select! { + _ = a => {}, + _ = b => unreachable!(), + }; + + let mut a = future::ready(()); + let mut b = future::pending::<()>(); + let _ = macro_reexport::select! { + _ = a => {}, + _ = b => unreachable!(), + }; + + let mut a = future::ready(()); + let mut b = future::pending::<()>(); + let _ = macro_reexport::select2! { + _ = a => {}, + _ = b => unreachable!(), + }; + }); + + // select_biased! macro + let _ = block_on(async { + let mut a = future::ready(()); + let mut b = future::pending::<()>(); + let _ = futures03::select_biased! { + _ = a => {}, + _ = b => unreachable!(), + }; + + let mut a = future::ready(()); + let mut b = future::pending::<()>(); + let _ = macro_reexport::select_biased! { + _ = a => {}, + _ = b => unreachable!(), + }; + + let mut a = future::ready(()); + let mut b = future::pending::<()>(); + let _ = macro_reexport::select_biased2! { + _ = a => {}, + _ = b => unreachable!(), + }; + }); + +}