Skip to content

Commit

Permalink
Support reexport for select!/select_biased! macros
Browse files Browse the repository at this point in the history
  • Loading branch information
taiki-e authored and cramertj committed Apr 22, 2020
1 parent d6c8bd8 commit 6a936af
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 89 deletions.
4 changes: 2 additions & 2 deletions futures-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ pub fn try_join_internal(input: TokenStream) -> TokenStream {

/// 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)
}
55 changes: 20 additions & 35 deletions futures-macro/src/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<syn::Path>,
// span of `complete`, then expression after `=> ...`
complete: Option<Expr>,
default: Option<Expr>,
Expand All @@ -30,23 +28,12 @@ enum CaseKind {
impl Parse for Select {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
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::<kw::futures_crate_path>()?;
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`
Expand Down Expand Up @@ -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();

Expand All @@ -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
},
Expand Down Expand Up @@ -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! {
Expand Down Expand Up @@ -267,21 +252,21 @@ 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
}
};

let execute_result_expr = if let Some(default_expr) = &parsed.default {
// 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
Expand All @@ -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!()
Expand All @@ -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 )*
Expand All @@ -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 => {}
Expand All @@ -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
}
};

Expand Down
35 changes: 28 additions & 7 deletions futures-util/src/async_await/select_mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 )*
}
}}
}
}
45 changes: 4 additions & 41 deletions futures/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -537,47 +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 {
#[cfg(feature = "std")]
pub use futures_util::select;
pub use futures_util::select_biased;
}

#[cfg(feature = "async-await")]
pub use futures_util::{join, try_join};

#[cfg(feature = "std")]
#[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;
#[cfg(feature = "async-await")]
pub use futures_util::select_biased;
7 changes: 5 additions & 2 deletions futures/tests/macro-reexport/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
// normal reexport
pub use futures03::{join, try_join};
pub use futures03::{join, try_join, select, select_biased};

// reexport + rename
pub use futures03::{join as join2, try_join as try_join2};
pub use futures03::{
join as join2, try_join as try_join2,
select as select2, select_biased as select_biased2,
};
50 changes: 48 additions & 2 deletions futures/tests/macro-tests/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Check that it works even if proc-macros are reexported.

fn main() {
use futures03::executor::block_on;
use futures03::{executor::block_on, future};

// join! macro
let _ = block_on(async {
Expand All @@ -18,6 +18,52 @@ fn main() {
Ok::<(), ()>(())
});

// TODO: add select! and select_biased!
// 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!(),
};
});

}

0 comments on commit 6a936af

Please sign in to comment.