diff --git a/tests-integration/Cargo.toml b/tests-integration/Cargo.toml index 3cca17a79..6f84afd96 100644 --- a/tests-integration/Cargo.toml +++ b/tests-integration/Cargo.toml @@ -6,7 +6,14 @@ edition = "2018" publish = false [features] -full = ["tokio/full", "tokio-test"] +full = [ + "macros", + "rt-core", + "rt-threaded", + + "tokio/full", + "tokio-test" +] macros = ["tokio/macros"] rt-core = ["tokio/rt-core"] rt-threaded = ["rt-core", "tokio/rt-threaded"] diff --git a/tests-integration/tests/macros_select.rs b/tests-integration/tests/macros_select.rs new file mode 100644 index 000000000..4c4fef7ce --- /dev/null +++ b/tests-integration/tests/macros_select.rs @@ -0,0 +1,33 @@ +#![cfg(feature = "macros")] + +use futures::channel::oneshot; +use futures::executor::block_on; +use std::thread; + +#[test] +fn join_with_select() { + block_on(async { + let (tx1, mut rx1) = oneshot::channel::(); + let (tx2, mut rx2) = oneshot::channel::(); + + thread::spawn(move || { + tx1.send(123).unwrap(); + tx2.send(456).unwrap(); + }); + + let mut a = None; + let mut b = None; + + while a.is_none() || b.is_none() { + tokio::select! { + v1 = (&mut rx1), if a.is_none() => a = Some(v1.unwrap()), + v2 = (&mut rx2), if b.is_none() => b = Some(v2.unwrap()), + } + } + + let (a, b) = (a.unwrap(), b.unwrap()); + + assert_eq!(a, 123); + assert_eq!(b, 456); + }); +} diff --git a/tokio-macros/Cargo.toml b/tokio-macros/Cargo.toml index d69d48d66..3cc3a3e49 100644 --- a/tokio-macros/Cargo.toml +++ b/tokio-macros/Cargo.toml @@ -25,6 +25,7 @@ proc-macro = true [features] [dependencies] +proc-macro2 = "1.0.7" quote = "1" syn = { version = "1.0.3", features = ["full"] } diff --git a/tokio-macros/src/lib.rs b/tokio-macros/src/lib.rs index 09b8b0938..175544705 100644 --- a/tokio-macros/src/lib.rs +++ b/tokio-macros/src/lib.rs @@ -14,10 +14,11 @@ //! Macros for use with Tokio -mod entry; - extern crate proc_macro; +mod entry; +mod select; + use proc_macro::TokenStream; /// Marks async function to be executed by selected runtime. @@ -198,3 +199,11 @@ pub fn test(args: TokenStream, item: TokenStream) -> TokenStream { pub fn test_basic(args: TokenStream, item: TokenStream) -> TokenStream { entry::test(args, item, false) } + +/// Implementation detail of the `select!` macro. This macro is **not** intended +/// to be used as part of the public API and is permitted to change. +#[proc_macro] +#[doc(hidden)] +pub fn select_priv_declare_output_enum(input: TokenStream) -> TokenStream { + select::declare_output_enum(input) +} diff --git a/tokio-macros/src/select.rs b/tokio-macros/src/select.rs new file mode 100644 index 000000000..ddb2e6a0c --- /dev/null +++ b/tokio-macros/src/select.rs @@ -0,0 +1,43 @@ +use proc_macro::{TokenStream, TokenTree}; +use proc_macro2::Span; +use quote::quote; +use syn::Ident; + +pub(crate) fn declare_output_enum(input: TokenStream) -> TokenStream { + // passed in is: `(_ _ _)` with one `_` per branch + let branches = match input.into_iter().next() { + Some(TokenTree::Group(group)) => group.stream().into_iter().count(), + _ => panic!("unexpected macro input"), + }; + + let variants = (0..branches) + .map(|num| Ident::new(&format!("_{}", num), Span::call_site())) + .collect::>(); + + // Use a bitfield to track which futures completed + let mask = Ident::new( + if branches <= 8 { + "u8" + } else if branches <= 16 { + "u16" + } else if branches <= 32 { + "u32" + } else if branches <= 64 { + "u64" + } else { + panic!("up to 64 branches supported"); + }, + Span::call_site(), + ); + + TokenStream::from(quote! { + pub(super) enum Out<#( #variants ),*> { + #( #variants(#variants), )* + // Include a `Disabled` variant signifying that all select branches + // failed to resolve. + Disabled, + } + + pub(super) type Mask = #mask; + }) +} diff --git a/tokio/src/future/mod.rs b/tokio/src/future/mod.rs index 9a155bf7f..c52256002 100644 --- a/tokio/src/future/mod.rs +++ b/tokio/src/future/mod.rs @@ -6,7 +6,7 @@ mod maybe_done; pub(crate) use maybe_done::{maybe_done, MaybeDone}; mod poll_fn; -pub(crate) use poll_fn::poll_fn; +pub use poll_fn::poll_fn; mod ready; pub(crate) use ready::{ok, Ready}; diff --git a/tokio/src/future/poll_fn.rs b/tokio/src/future/poll_fn.rs index ce2a55249..9b3d1370e 100644 --- a/tokio/src/future/poll_fn.rs +++ b/tokio/src/future/poll_fn.rs @@ -6,14 +6,14 @@ use std::pin::Pin; use std::task::{Context, Poll}; /// Future for the [`poll_fn`] function. -pub(crate) struct PollFn { +pub struct PollFn { f: F, } impl Unpin for PollFn {} /// Creates a new future wrapping around a function returning [`Poll`]. -pub(crate) fn poll_fn(f: F) -> PollFn +pub fn poll_fn(f: F) -> PollFn where F: FnMut(&mut Context<'_>) -> Poll, { diff --git a/tokio/src/lib.rs b/tokio/src/lib.rs index 42a341319..7989d5b34 100644 --- a/tokio/src/lib.rs +++ b/tokio/src/lib.rs @@ -282,15 +282,20 @@ //! } //! ``` -// macros used internally +// Includes re-exports used by macros. +// +// This module is not intended to be part of the public API. In general, any +// `doc(hidden)` code is not part of Tokio's public and stable API. #[macro_use] -mod macros; +#[doc(hidden)] +pub mod macros; cfg_fs! { pub mod fs; } -mod future; +#[doc(hidden)] +pub mod future; pub mod io; pub mod net; @@ -333,6 +338,12 @@ cfg_time! { mod util; cfg_macros! { + /// Implementation detail of the `select!` macro. This macro is **not** + /// intended to be used as part of the public API and is permitted to + /// change. + #[doc(hidden)] + pub use tokio_macros::select_priv_declare_output_enum; + doc_rt_core! { cfg_rt_threaded! { #[cfg(not(test))] // Work around for rust-lang/rust#62127 diff --git a/tokio/src/macros/mod.rs b/tokio/src/macros/mod.rs index 9136e5944..708dd6832 100644 --- a/tokio/src/macros/mod.rs +++ b/tokio/src/macros/mod.rs @@ -13,5 +13,14 @@ mod loom; #[macro_use] mod ready; +#[macro_use] +mod select; + #[macro_use] mod thread_local; + +cfg_macros! { + // Includes re-exports needed to implement macros + #[doc(hidden)] + pub mod support; +} diff --git a/tokio/src/macros/select.rs b/tokio/src/macros/select.rs new file mode 100644 index 000000000..00c2c6b5b --- /dev/null +++ b/tokio/src/macros/select.rs @@ -0,0 +1,873 @@ +/// Wait on multiple concurrent branches, returning when the **first** branch +/// completes, cancelling the remaining branches. +/// +/// The `select` macro accepts one or more branches with the following pattern: +/// +/// ```text +/// = (, if )? => , +/// ``` +/// +/// Additionally, the `select!` macro may include a single, optional `else` +/// branch, which evaluates if none of the other branches match their patterns: +/// +/// ```text +/// else +/// ``` +/// +/// The macro aggregates all `` expressions and runs them +/// concurrently on the **current** task. Once the **first** expression +/// completes with a value that matches its ``, the `select!` macro +/// returns the result of evaluating the completed branch's `` +/// expression. +/// +/// Additionally, each branch may include an optional `if` precondition. This +/// precondition is evaluated **before** the . If the +/// precondition returns `false`, the branch is entirely disabled. This +/// capability is useful when using `select!` within a loop. +/// +/// The complete lifecycle of a `select!` expression is as follows: +/// +/// 1. Evaluate all provded `` expressions. If the precondition +/// returns `false`, disable the branch for the remainder of the current call +/// to `select!`. Re-entering `select!` due to a loop clears the "disabled" +/// state. +/// 2. Aggregate the ``s from each branch, including the +/// disabled ones. If the branch is disabled, `` is still +/// evaluated, but the resulting future is not polled. +/// 3. Concurrently await on the results for all remaining ``s. +/// 4. Once an `` returns a value, attempt to apply the value +/// to the provided ``, if the pattern matches, evaluate `` +/// and return. If the pattern **does not** match, disable the current branch +/// and for the remainder of the current call to `select!. Continue from step 3. +/// 5. If **all** branches are disabled, evaluate the `else` expression. If none +/// is provided, panic. +/// +/// # Notes +/// +/// ### Runtime characteristics +/// +/// By running all async expressions on the current task, the expressions are +/// able to run **concurrently** but not in **parallel**. This means all +/// expressions are run on the same thread and if one branch blocks the thread, +/// all other expressions will be unable to continue. If parallelism is +/// required, spawn each async expression using [`tokio::spawn`] and pass the +/// join handle to `select!`. +/// +/// [`tokio::spawn`]: crate::spawn +/// +/// ### Avoid racy `if` preconditions +/// +/// Given that `if` preconditions are used to disable `select!` branches, some +/// caution must be used to avoid missing values. +/// +/// For example, here is **incorrect** usage of `delay` with `if`. The objective +/// is to repeatedly run an asynchronous task for up to 50 milliseconds. +/// However, there is a potential for the `delay` completion to be missed. +/// +/// ```no_run +/// use tokio::time::{self, Duration}; +/// +/// async fn some_async_work() { +/// // do work +/// } +/// +/// #[tokio::main] +/// async fn main() { +/// let mut delay = time::delay_for(Duration::from_millis(50)); +/// +/// while !delay.is_elapsed() { +/// tokio::select! { +/// _ = &mut delay, if !delay.is_elapsed() => { +/// println!("operation timed out"); +/// } +/// _ = some_async_work() => { +/// println!("operation completed"); +/// } +/// } +/// } +/// } +/// ``` +/// +/// In the above example, `delay.is_elapsed()` may return `true` even if +/// `delay.poll()` never returned `Ready`. This opens up a potential race +/// condition where `delay` expires between the `while !delay.is_elapsed()` +/// check and the call to `select!` resulting in the `some_async_work()` call to +/// run uninterrupted despite the delay having elapsed. +/// +/// One way to write the above example without the race would be: +/// +/// ``` +/// use tokio::time::{self, Duration}; +/// +/// async fn some_async_work() { +/// # time::delay_for(Duration::from_millis(10)).await; +/// // do work +/// } +/// +/// #[tokio::main] +/// async fn main() { +/// let mut delay = time::delay_for(Duration::from_millis(50)); +/// +/// loop { +/// tokio::select! { +/// _ = &mut delay => { +/// println!("operation timed out"); +/// break; +/// } +/// _ = some_async_work() => { +/// println!("operation completed"); +/// } +/// } +/// } +/// } +/// ``` +/// +/// ### Fairness +/// +/// `select!` randomly picks a branch to check first. This provides some level +/// of fairness when calling `select!` in a loop with branches that are always +/// ready. +/// +/// # Panics +/// +/// `select!` panics if all branches are disabled **and** there is no provided +/// `else` branch. A branch is disabled when the provided `if` precondition +/// returns `false` **or** when the pattern does not match the result of `. +/// +/// # Examples +/// +/// Basic select with two branches. +/// +/// ``` +/// async fn do_stuff_async() { +/// // async work +/// } +/// +/// async fn more_async_work() { +/// // more here +/// } +/// +/// #[tokio::main] +/// async fn main() { +/// tokio::select! { +/// _ = do_stuff_async() => { +/// println!("do_stuff_async() completed first") +/// } +/// _ = more_async_work() => { +/// println!("more_async_work() completed first") +/// } +/// }; +/// } +/// +/// ``` +/// +/// Basic stream selecting. +/// +/// ``` +/// use tokio::stream::{self, StreamExt}; +/// +/// #[tokio::main] +/// async fn main() { +/// let mut stream1 = stream::iter(vec![1, 2, 3]); +/// let mut stream2 = stream::iter(vec![4, 5, 6]); +/// +/// let next = tokio::select! { +/// v = stream1.next() => v.unwrap(), +/// v = stream2.next() => v.unwrap(), +/// }; +/// +/// assert!(next == 1 || next == 4); +/// } +/// ``` +/// +/// Collect the contents of two streams. In this example, we rely on pattern +/// matching and the fact that `stream::iter` is "fused", i.e. once the stream +/// is complete, all calls to `next()` return `None`. +/// +/// ``` +/// use tokio::stream::{self, StreamExt}; +/// +/// #[tokio::main] +/// async fn main() { +/// let mut stream1 = stream::iter(vec![1, 2, 3]); +/// let mut stream2 = stream::iter(vec![4, 5, 6]); +/// +/// let mut values = vec![]; +/// +/// loop { +/// tokio::select! { +/// Some(v) = stream1.next() => values.push(v), +/// Some(v) = stream2.next() => values.push(v), +/// else => break, +/// } +/// } +/// +/// values.sort(); +/// assert_eq!(&[1, 2, 3, 4, 5, 6], &values[..]); +/// } +/// ``` +/// +/// Using the same future in multiple select! expressions can be done by passing +/// a reference to the future. Doing so requires the future to be [`Unpin`]. A +/// future can be made [`Unpin`] by either using [`Box::pin`] or stack pinning. +/// +/// [`Unpin`]: std::marker::Unpin +/// [`Box::pin`]: std::boxed::Box::pin +/// +/// Here, a stream is consumed for at most 1 second. +/// +/// ``` +/// use tokio::stream::{self, StreamExt}; +/// use tokio::time::{self, Duration}; +/// +/// #[tokio::main] +/// async fn main() { +/// let mut stream = stream::iter(vec![1, 2, 3]); +/// let mut delay = time::delay_for(Duration::from_secs(1)); +/// +/// loop { +/// tokio::select! { +/// maybe_v = stream.next() => { +/// if let Some(v) = maybe_v { +/// println!("got = {}", v); +/// } else { +/// break; +/// } +/// } +/// _ = &mut delay => { +/// println!("timeout"); +/// break; +/// } +/// } +/// } +/// } +/// ``` +/// +/// Joining two values using `select!`. +/// +/// ``` +/// use tokio::sync::oneshot; +/// +/// #[tokio::main] +/// async fn main() { +/// let (tx1, mut rx1) = oneshot::channel(); +/// let (tx2, mut rx2) = oneshot::channel(); +/// +/// tokio::spawn(async move { +/// tx1.send("first").unwrap(); +/// }); +/// +/// tokio::spawn(async move { +/// tx2.send("second").unwrap(); +/// }); +/// +/// let mut a = None; +/// let mut b = None; +/// +/// while a.is_none() || b.is_none() { +/// tokio::select! { +/// v1 = (&mut rx1), if a.is_none() => a = Some(v1.unwrap()), +/// v2 = (&mut rx2), if b.is_none() => b = Some(v2.unwrap()), +/// } +/// } +/// +/// let res = (a.unwrap(), b.unwrap()); +/// +/// assert_eq!(res.0, "first"); +/// assert_eq!(res.1, "second"); +/// } +/// ``` +#[macro_export] +macro_rules! select { + // Uses a declarative macro to do **most** of the work. While it is possible + // to implement fully with a declarative macro, a procedural macro is used + // to enable improved error messages. + // + // The macro is structured as a tt-muncher. All branches are processed and + // normalized. Once the input is normalized, it is passed to the top-most + // rule. When entering the macro, `@{ }` is inserted at the front. This is + // used to collect the normalized input. + // + // The macro only recurses once per branch. This allows using `select!` + // without requiring the user to increase the recursion limit. + + // All input is normalized, now transform. + (@ { + // One `_` for each branch in the `select!` macro. Passing this to + // `count!` converts $skip to an integer. + ( $($count:tt)* ) + + // Normalized select branches. `( $skip )` is a set of `_` characters. + // There is one `_` for each select branch **before** this one. Given + // that all input futures are stored in a tuple, $skip is useful for + // generating a pattern to reference the future for the current branch. + // $skip is also used as an argument to `count!`, returning the index of + // the current select branch. + $( ( $($skip:tt)* ) $bind:pat = $fut:expr, if $c:expr => $handle:expr, )+ + + // Fallback expression used when all select branches have been disabled. + ; $else:expr + + }) => {{ + // Enter a context where stable "function-like" proc macros can be used. + // + // This module is defined within a scope and should not leak out of this + // macro. + mod util { + // Generate an enum with one variant per select branch + $crate::select_priv_declare_output_enum!( ( $($count)* ) ); + } + + // `tokio::macros::support` is a public, but doc(hidden) module + // including a re-export of all types needed by this macro. + use $crate::macros::support::Future; + use $crate::macros::support::Pin; + use $crate::macros::support::Poll::{Ready, Pending}; + + const BRANCHES: u32 = $crate::count!( $($count)* ); + + let mut disabled: util::Mask = Default::default(); + + // First, invoke all the pre-conditions. For any that return true, + // set the appropriate bit in `disabled`. + $( + if !$c { + let mask = 1 << $crate::count!( $($skip)* ); + disabled |= mask; + } + )* + + // Create a scope to separate polling from handling the output. This + // adds borrow checker flexibility when using the macro. + let mut output = { + // Safety: Nothing must be moved out of `futures`. This is to + // satisfy the requirement of `Pin::new_unchecked` called below. + let mut futures = ( $( $fut , )+ ); + + $crate::macros::support::poll_fn(|cx| { + // Track if any branch returns pending. If no branch completes + // **or** returns pending, this implies that all branches are + // disabled. + let mut is_pending = false; + + // Randomly generate a starting point. This makes `select!` a + // bit more fair and avoids always polling the first future. + let start = $crate::macros::support::thread_rng_n(BRANCHES); + + for i in 0..BRANCHES { + let branch = (start + i) % BRANCHES; + + match branch { + $( + $crate::count!( $($skip)* ) => { + // First, if the future has previously been + // disabled, do not poll it again. This is done + // by checking the associated bit in the + // `disabled` bit field. + let mask = 1 << branch; + + if disabled & mask == mask { + // The future has been disabled. + continue; + } + + // Extract the future for this branch from the + // tuple + let ( $($skip,)* fut, .. ) = &mut futures; + + // Safety: future is stored on the stack above + // and never moved. + let mut fut = unsafe { Pin::new_unchecked(fut) }; + + // Try polling it + let out = match fut.poll(cx) { + Ready(out) => out, + Pending => { + // Track that at least one future is + // still pending and continue polling. + is_pending = true; + continue; + } + }; + + // Disable the future from future polling. + disabled |= mask; + + // The future returned a value, check if matches + // the specified pattern. + #[allow(unused_variables)] + match &out { + $bind => {} + _ => continue, + } + + // The select is complete, return the value + return Ready($crate::select_variant!(util::Out, ($($skip)*))(out)); + } + )* + _ => unreachable!("reaching this means there probably is an off by one bug"), + } + } + + if is_pending { + Pending + } else { + // All branches have been disabled. + Ready(util::Out::Disabled) + } + }).await + }; + + match output { + $( + $crate::select_variant!(util::Out, ($($skip)*) ($bind)) => $handle, + )* + util::Out::Disabled => $else, + _ => unreachable!("failed to match bind"), + } + }}; + + // ==== Normalize ===== + + // These rules match a single `select!` branch and normalize it for + // processing by the first rule. + + (@ { $($t:tt)* } ) => { + // No `else` branch + $crate::select!(@{ $($t)*; unreachable!() }) + }; + (@ { $($t:tt)* } else => $else:expr $(,)?) => { + $crate::select!(@{ $($t)*; $else }) + }; + (@ { ( $($s:tt)* ) $($t:tt)* } $p:pat = $f:expr, if $c:expr => $h:block, $($r:tt)* ) => { + $crate::select!(@{ ($($s)* _) $($t)* ($($s)*) $p = $f, if $c => $h, } $($r)*) + }; + (@ { ( $($s:tt)* ) $($t:tt)* } $p:pat = $f:expr => $h:block, $($r:tt)* ) => { + $crate::select!(@{ ($($s)* _) $($t)* ($($s)*) $p = $f, if true => $h, } $($r)*) + }; + (@ { ( $($s:tt)* ) $($t:tt)* } $p:pat = $f:expr, if $c:expr => $h:block $($r:tt)* ) => { + $crate::select!(@{ ($($s)* _) $($t)* ($($s)*) $p = $f, if $c => $h, } $($r)*) + }; + (@ { ( $($s:tt)* ) $($t:tt)* } $p:pat = $f:expr => $h:block $($r:tt)* ) => { + $crate::select!(@{ ($($s)* _) $($t)* ($($s)*) $p = $f, if true => $h, } $($r)*) + }; + (@ { ( $($s:tt)* ) $($t:tt)* } $p:pat = $f:expr, if $c:expr => $h:expr ) => { + $crate::select!(@{ ($($s)* _) $($t)* ($($s)*) $p = $f, if $c => $h, }) + }; + (@ { ( $($s:tt)* ) $($t:tt)* } $p:pat = $f:expr => $h:expr ) => { + $crate::select!(@{ ($($s)* _) $($t)* ($($s)*) $p = $f, if true => $h, }) + }; + (@ { ( $($s:tt)* ) $($t:tt)* } $p:pat = $f:expr, if $c:expr => $h:expr, $($r:tt)* ) => { + $crate::select!(@{ ($($s)* _) $($t)* ($($s)*) $p = $f, if $c => $h, } $($r)*) + }; + (@ { ( $($s:tt)* ) $($t:tt)* } $p:pat = $f:expr => $h:expr, $($r:tt)* ) => { + $crate::select!(@{ ($($s)* _) $($t)* ($($s)*) $p = $f, if true => $h, } $($r)*) + }; + + // ===== Entry point ===== + + ( $p:pat = $($t:tt)* ) => { + $crate::select!(@{ () } $p = $($t)*) + }; + () => { + compile_error!("select! requires at least one branch.") + }; +} + +// And here... we manually list out matches for up to 64 branches... I'm not +// happy about it either, but this is how we manage to use a declarative macro! + +#[macro_export] +#[doc(hidden)] +macro_rules! count { + () => { + 0 + }; + (_) => { + 1 + }; + (_ _) => { + 2 + }; + (_ _ _) => { + 3 + }; + (_ _ _ _) => { + 4 + }; + (_ _ _ _ _) => { + 5 + }; + (_ _ _ _ _ _) => { + 6 + }; + (_ _ _ _ _ _ _) => { + 7 + }; + (_ _ _ _ _ _ _ _) => { + 8 + }; + (_ _ _ _ _ _ _ _ _) => { + 9 + }; + (_ _ _ _ _ _ _ _ _ _) => { + 10 + }; + (_ _ _ _ _ _ _ _ _ _ _) => { + 11 + }; + (_ _ _ _ _ _ _ _ _ _ _ _) => { + 12 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _) => { + 13 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 14 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 15 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 16 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 17 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 18 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 19 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 20 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 21 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 22 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 23 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 24 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 25 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 26 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 27 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 28 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 29 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 30 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 31 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 32 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 33 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 34 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 35 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 36 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 37 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 38 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 39 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 40 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 41 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 42 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 43 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 44 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 45 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 46 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 47 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 48 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 49 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 50 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 51 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 52 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 53 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 54 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 55 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 56 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 57 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 58 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 59 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 60 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 61 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 62 + }; + (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) => { + 63 + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! select_variant { + ($($p:ident)::*, () $($t:tt)*) => { + $($p)::*::_0 $($t)* + }; + ($($p:ident)::*, (_) $($t:tt)*) => { + $($p)::*::_1 $($t)* + }; + ($($p:ident)::*, (_ _) $($t:tt)*) => { + $($p)::*::_2 $($t)* + }; + ($($p:ident)::*, (_ _ _) $($t:tt)*) => { + $($p)::*::_3 $($t)* + }; + ($($p:ident)::*, (_ _ _ _) $($t:tt)*) => { + $($p)::*::_4 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _) $($t:tt)*) => { + $($p)::*::_5 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_6 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_7 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_8 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_9 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_10 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_11 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_12 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_13 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_14 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_15 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_16 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_17 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_18 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_19 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_20 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_21 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_22 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_23 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_24 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_25 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_26 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_27 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_28 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_29 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_30 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_31 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_32 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_33 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_34 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_35 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_36 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_37 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_38 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_39 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_40 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_41 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_42 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_43 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_44 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_45 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_46 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_47 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_48 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_49 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_50 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_51 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_52 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_53 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_54 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_55 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_56 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_57 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_58 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_59 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_60 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_61 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_62 $($t)* + }; + ($($p:ident)::*, (_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) $($t:tt)*) => { + $($p)::*::_63 $($t)* + }; +} diff --git a/tokio/src/macros/support.rs b/tokio/src/macros/support.rs new file mode 100644 index 000000000..079cb085c --- /dev/null +++ b/tokio/src/macros/support.rs @@ -0,0 +1,6 @@ +pub use crate::future::poll_fn; +pub use crate::util::thread_rng_n; + +pub use std::future::Future; +pub use std::pin::Pin; +pub use std::task::Poll; diff --git a/tokio/src/util/mod.rs b/tokio/src/util/mod.rs index 7b4a12e58..fa904c3d1 100644 --- a/tokio/src/util/mod.rs +++ b/tokio/src/util/mod.rs @@ -3,13 +3,19 @@ cfg_io_driver! { pub(crate) mod slab; } +#[cfg(any(feature = "rt-threaded", feature = "macros"))] +mod rand; + cfg_rt_threaded! { mod pad; pub(crate) use pad::CachePadded; - mod rand; pub(crate) use rand::FastRand; mod try_lock; pub(crate) use try_lock::TryLock; } + +cfg_macros! { + pub use rand::thread_rng_n; +} diff --git a/tokio/src/util/rand.rs b/tokio/src/util/rand.rs index 11617e263..101f5bb69 100644 --- a/tokio/src/util/rand.rs +++ b/tokio/src/util/rand.rs @@ -50,3 +50,16 @@ impl FastRand { s0.wrapping_add(s1) } } + +// Used by the select macro +cfg_macros! { + thread_local! { + static THREAD_RNG: FastRand = FastRand::new(crate::loom::rand::seed()); + } + + // Used by macros + #[doc(hidden)] + pub fn thread_rng_n(n: u32) -> u32 { + THREAD_RNG.with(|rng| rng.fastrand_n(n)) + } +} diff --git a/tokio/tests/macros_select.rs b/tokio/tests/macros_select.rs new file mode 100644 index 000000000..c08e816a0 --- /dev/null +++ b/tokio/tests/macros_select.rs @@ -0,0 +1,447 @@ +use tokio::sync::{mpsc, oneshot}; +use tokio::task; +use tokio_test::{assert_ok, assert_pending, assert_ready}; + +use futures::future::poll_fn; +use std::task::Poll::Ready; + +#[tokio::test] +async fn sync_one_lit_expr_comma() { + let foo = tokio::select! { + foo = async { 1 } => foo, + }; + + assert_eq!(foo, 1); +} + +#[tokio::test] +async fn nested_one() { + let foo = tokio::select! { + foo = async { 1 } => tokio::select! { + bar = async { foo } => bar, + }, + }; + + assert_eq!(foo, 1); +} + +#[tokio::test] +async fn sync_one_lit_expr_no_comma() { + let foo = tokio::select! { + foo = async { 1 } => foo + }; + + assert_eq!(foo, 1); +} + +#[tokio::test] +async fn sync_one_lit_expr_block() { + let foo = tokio::select! { + foo = async { 1 } => { foo } + }; + + assert_eq!(foo, 1); +} + +#[tokio::test] +async fn sync_one_await() { + let foo = tokio::select! { + foo = one() => foo, + }; + + assert_eq!(foo, 1); +} + +#[tokio::test] +async fn sync_one_ident() { + let one = one(); + + let foo = tokio::select! { + foo = one => foo, + }; + + assert_eq!(foo, 1); +} + +#[tokio::test] +async fn sync_two() { + use std::cell::Cell; + + let cnt = Cell::new(0); + + let res = tokio::select! { + foo = async { + cnt.set(cnt.get() + 1); + 1 + } => foo, + bar = async { + cnt.set(cnt.get() + 1); + 2 + } => bar, + }; + + assert_eq!(1, cnt.get()); + assert!(res == 1 || res == 2); +} + +#[tokio::test] +async fn drop_in_fut() { + let s = "hello".to_string(); + + let res = tokio::select! { + foo = async { + let v = one().await; + drop(s); + v + } => foo + }; + + assert_eq!(res, 1); +} + +#[tokio::test] +async fn one_ready() { + let (tx1, rx1) = oneshot::channel::(); + let (_tx2, rx2) = oneshot::channel::(); + + tx1.send(1).unwrap(); + + let v = tokio::select! { + res = rx1 => { + assert_ok!(res) + }, + _ = rx2 => unreachable!(), + }; + + assert_eq!(1, v); +} + +#[tokio::test] +async fn select_streams() { + let (tx1, mut rx1) = mpsc::unbounded_channel::(); + let (tx2, mut rx2) = mpsc::unbounded_channel::(); + + tokio::spawn(async move { + assert_ok!(tx2.send(1)); + task::yield_now().await; + + assert_ok!(tx1.send(2)); + task::yield_now().await; + + assert_ok!(tx2.send(3)); + task::yield_now().await; + + drop((tx1, tx2)); + }); + + let mut rem = true; + let mut msgs = vec![]; + + while rem { + tokio::select! { + Some(x) = rx1.recv() => { + msgs.push(x); + } + Some(y) = rx2.recv() => { + msgs.push(y); + } + else => { + rem = false; + } + } + } + + msgs.sort(); + assert_eq!(&msgs[..], &[1, 2, 3]); +} + +#[tokio::test] +async fn move_uncompleted_futures() { + let (tx1, mut rx1) = oneshot::channel::(); + let (tx2, mut rx2) = oneshot::channel::(); + + tx1.send(1).unwrap(); + tx2.send(2).unwrap(); + + let ran; + + tokio::select! { + res = &mut rx1 => { + assert_eq!(1, assert_ok!(res)); + assert_eq!(2, assert_ok!(rx2.await)); + ran = true; + }, + res = &mut rx2 => { + assert_eq!(2, assert_ok!(res)); + assert_eq!(1, assert_ok!(rx1.await)); + ran = true; + }, + } + + assert!(ran); +} + +#[tokio::test] +async fn nested() { + let res = tokio::select! { + x = async { 1 } => { + tokio::select! { + y = async { 2 } => x + y, + } + } + }; + + assert_eq!(res, 3); +} + +#[tokio::test] +async fn struct_size() { + use futures::future; + use std::mem; + + let fut = async { + let ready = future::ready(0i32); + + tokio::select! { + _ = ready => {}, + } + }; + + assert!(mem::size_of_val(&fut) <= 32); + + let fut = async { + let ready1 = future::ready(0i32); + let ready2 = future::ready(0i32); + + tokio::select! { + _ = ready1 => {}, + _ = ready2 => {}, + } + }; + + assert!(mem::size_of_val(&fut) <= 40); + + let fut = async { + let ready1 = future::ready(0i32); + let ready2 = future::ready(0i32); + let ready3 = future::ready(0i32); + + tokio::select! { + _ = ready1 => {}, + _ = ready2 => {}, + _ = ready3 => {}, + } + }; + + assert!(mem::size_of_val(&fut) <= 48); +} + +#[tokio::test] +async fn mutable_borrowing_future_with_same_borrow_in_block() { + let mut value = 234; + + tokio::select! { + _ = require_mutable(&mut value) => { }, + _ = async_noop() => { + value += 5; + }, + } + + assert!(value >= 234); +} + +#[tokio::test] +async fn mutable_borrowing_future_with_same_borrow_in_block_and_else() { + let mut value = 234; + + tokio::select! { + _ = require_mutable(&mut value) => { }, + _ = async_noop() => { + value += 5; + }, + else => { + value += 27; + }, + } + + assert!(value >= 234); +} + +#[tokio::test] +async fn future_panics_after_poll() { + use tokio_test::task; + + let (tx, rx) = oneshot::channel(); + + let mut polled = false; + + let f = poll_fn(|_| { + assert!(!polled); + polled = true; + Ready(None::<()>) + }); + + let mut f = task::spawn(async { + tokio::select! { + Some(_) = f => unreachable!(), + ret = rx => ret.unwrap(), + } + }); + + assert_pending!(f.poll()); + assert_pending!(f.poll()); + + assert_ok!(tx.send(1)); + + let res = assert_ready!(f.poll()); + assert_eq!(1, res); +} + +#[tokio::test] +async fn disable_with_if() { + use tokio_test::task; + + let f = poll_fn(|_| panic!()); + let (tx, rx) = oneshot::channel(); + + let mut f = task::spawn(async { + tokio::select! { + _ = f, if false => unreachable!(), + _ = rx => (), + } + }); + + assert_pending!(f.poll()); + + assert_ok!(tx.send(())); + assert!(f.is_woken()); + + assert_ready!(f.poll()); +} + +#[tokio::test] +async fn join_with_select() { + use tokio_test::task; + + let (tx1, mut rx1) = oneshot::channel(); + let (tx2, mut rx2) = oneshot::channel(); + + let mut f = task::spawn(async { + let mut a = None; + let mut b = None; + + while a.is_none() || b.is_none() { + tokio::select! { + v1 = &mut rx1, if a.is_none() => a = Some(assert_ok!(v1)), + v2 = &mut rx2, if b.is_none() => b = Some(assert_ok!(v2)) + } + } + + (a.unwrap(), b.unwrap()) + }); + + assert_pending!(f.poll()); + + assert_ok!(tx1.send(123)); + assert!(f.is_woken()); + assert_pending!(f.poll()); + + assert_ok!(tx2.send(456)); + assert!(f.is_woken()); + let (a, b) = assert_ready!(f.poll()); + + assert_eq!(a, 123); + assert_eq!(b, 456); +} + +#[tokio::test] +async fn use_future_in_if_condition() { + use tokio::time::{self, Duration}; + + let mut delay = time::delay_for(Duration::from_millis(50)); + + tokio::select! { + _ = &mut delay, if !delay.is_elapsed() => { + } + _ = async { 1 } => { + } + } +} + +#[tokio::test] +async fn many_branches() { + let num = tokio::select! { + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + x = async { 1 } => x, + }; + + assert_eq!(1, num); +} + +async fn one() -> usize { + 1 +} + +async fn require_mutable(_: &mut i32) {} +async fn async_noop() {}