diff --git a/tests-build/tests/fail/macros_join.rs b/tests-build/tests/fail/macros_join.rs new file mode 100644 index 00000000000..7301149201f --- /dev/null +++ b/tests-build/tests/fail/macros_join.rs @@ -0,0 +1,42 @@ +use tests_build::tokio; + +#[tokio::main] +async fn main() { + // do not leak `RotatorSelect` + let _ = tokio::join!(async { + fn foo(_: impl RotatorSelect) {} + }); + + // do not leak `std::task::Poll::Pending` + let _ = tokio::join!(async { Pending }); + + // do not leak `std::task::Poll::Ready` + let _ = tokio::join!(async { Ready(0) }); + + // do not leak `std::future::Future` + let _ = tokio::join!(async { + struct MyFuture; + + impl Future for MyFuture { + type Output = (); + + fn poll( + self: std::pin::Pin<&mut Self>, + _cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + todo!() + } + } + }); + + // do not leak `std::pin::Pin` + let _ = tokio::join!(async { + let mut x = 5; + let _ = Pin::new(&mut x); + }); + + // do not leak `std::future::poll_fn` + let _ = tokio::join!(async { + let _ = poll_fn(|_cx| todo!()); + }); +} diff --git a/tests-build/tests/fail/macros_join.stderr b/tests-build/tests/fail/macros_join.stderr new file mode 100644 index 00000000000..45ce61e03c9 --- /dev/null +++ b/tests-build/tests/fail/macros_join.stderr @@ -0,0 +1,60 @@ +error[E0405]: cannot find trait `RotatorSelect` in this scope + --> tests/fail/macros_join.rs:7:24 + | +7 | fn foo(_: impl RotatorSelect) {} + | ^^^^^^^^^^^^^ not found in this scope + +error[E0425]: cannot find value `Pending` in this scope + --> tests/fail/macros_join.rs:11:34 + | +11 | let _ = tokio::join!(async { Pending }); + | ^^^^^^^ not found in this scope + | +help: consider importing this unit variant + | + 1 + use std::task::Poll::Pending; + | + +error[E0425]: cannot find function, tuple struct or tuple variant `Ready` in this scope + --> tests/fail/macros_join.rs:14:34 + | +14 | let _ = tokio::join!(async { Ready(0) }); + | ^^^^^ not found in this scope + | +help: consider importing this tuple variant + | + 1 + use std::task::Poll::Ready; + | + +error[E0405]: cannot find trait `Future` in this scope + --> tests/fail/macros_join.rs:20:14 + | +20 | impl Future for MyFuture { + | ^^^^^^ not found in this scope + | +help: consider importing this trait + | + 1 + use std::future::Future; + | + +error[E0433]: failed to resolve: use of undeclared type `Pin` + --> tests/fail/macros_join.rs:35:17 + | +35 | let _ = Pin::new(&mut x); + | ^^^ use of undeclared type `Pin` + | +help: consider importing this struct + | + 1 + use std::pin::Pin; + | + +error[E0425]: cannot find function `poll_fn` in this scope + --> tests/fail/macros_join.rs:40:17 + | +40 | let _ = poll_fn(|_cx| todo!()); + | ^^^^^^^ not found in this scope + | +help: consider importing this function + | + 1 + use std::future::poll_fn; + | diff --git a/tests-build/tests/fail/macros_try_join.rs b/tests-build/tests/fail/macros_try_join.rs new file mode 100644 index 00000000000..cf7f79fce77 --- /dev/null +++ b/tests-build/tests/fail/macros_try_join.rs @@ -0,0 +1,42 @@ +use tests_build::tokio; + +#[tokio::main] +async fn main() { + // do not leak `RotatorSelect` + let _ = tokio::try_join!(async { + fn foo(_: impl RotatorSelect) {} + }); + + // do not leak `std::task::Poll::Pending` + let _ = tokio::try_join!(async { Pending }); + + // do not leak `std::task::Poll::Ready` + let _ = tokio::try_join!(async { Ready(0) }); + + // do not leak `std::future::Future` + let _ = tokio::try_join!(async { + struct MyFuture; + + impl Future for MyFuture { + type Output = (); + + fn poll( + self: std::pin::Pin<&mut Self>, + _cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + todo!() + } + } + }); + + // do not leak `std::pin::Pin` + let _ = tokio::try_join!(async { + let mut x = 5; + let _ = Pin::new(&mut x); + }); + + // do not leak `std::future::poll_fn` + let _ = tokio::try_join!(async { + let _ = poll_fn(|_cx| todo!()); + }); +} diff --git a/tests-build/tests/fail/macros_try_join.stderr b/tests-build/tests/fail/macros_try_join.stderr new file mode 100644 index 00000000000..ac3ae6e3698 --- /dev/null +++ b/tests-build/tests/fail/macros_try_join.stderr @@ -0,0 +1,60 @@ +error[E0405]: cannot find trait `RotatorSelect` in this scope + --> tests/fail/macros_try_join.rs:7:24 + | +7 | fn foo(_: impl RotatorSelect) {} + | ^^^^^^^^^^^^^ not found in this scope + +error[E0425]: cannot find value `Pending` in this scope + --> tests/fail/macros_try_join.rs:11:38 + | +11 | let _ = tokio::try_join!(async { Pending }); + | ^^^^^^^ not found in this scope + | +help: consider importing this unit variant + | + 1 + use std::task::Poll::Pending; + | + +error[E0425]: cannot find function, tuple struct or tuple variant `Ready` in this scope + --> tests/fail/macros_try_join.rs:14:38 + | +14 | let _ = tokio::try_join!(async { Ready(0) }); + | ^^^^^ not found in this scope + | +help: consider importing this tuple variant + | + 1 + use std::task::Poll::Ready; + | + +error[E0405]: cannot find trait `Future` in this scope + --> tests/fail/macros_try_join.rs:20:14 + | +20 | impl Future for MyFuture { + | ^^^^^^ not found in this scope + | +help: consider importing this trait + | + 1 + use std::future::Future; + | + +error[E0433]: failed to resolve: use of undeclared type `Pin` + --> tests/fail/macros_try_join.rs:35:17 + | +35 | let _ = Pin::new(&mut x); + | ^^^ use of undeclared type `Pin` + | +help: consider importing this struct + | + 1 + use std::pin::Pin; + | + +error[E0425]: cannot find function `poll_fn` in this scope + --> tests/fail/macros_try_join.rs:40:17 + | +40 | let _ = poll_fn(|_cx| todo!()); + | ^^^^^^^ not found in this scope + | +help: consider importing this function + | + 1 + use std::future::poll_fn; + | diff --git a/tests-build/tests/macros.rs b/tests-build/tests/macros.rs index 8de08125d30..7e7e6943f95 100644 --- a/tests-build/tests/macros.rs +++ b/tests-build/tests/macros.rs @@ -18,6 +18,12 @@ fn compile_fail_full() { #[cfg(feature = "full")] t.compile_fail("tests/fail/macros_dead_code.rs"); + #[cfg(feature = "full")] + t.compile_fail("tests/fail/macros_join.rs"); + + #[cfg(feature = "full")] + t.compile_fail("tests/fail/macros_try_join.rs"); + #[cfg(feature = "full")] t.compile_fail("tests/fail/macros_type_mismatch.rs"); diff --git a/tokio/src/macros/join.rs b/tokio/src/macros/join.rs index 1a62a979fb3..bcf7ca27bdb 100644 --- a/tokio/src/macros/join.rs +++ b/tokio/src/macros/join.rs @@ -129,16 +129,13 @@ doc! {macro_rules! join { $( ( $($skip:tt)* ) $e:expr, )* }) => {{ - use $crate::macros::support::{maybe_done, poll_fn, Future, Pin, RotatorSelect}; - use $crate::macros::support::Poll::{Ready, Pending}; - // Safety: nothing must be moved out of `futures`. This is to satisfy // the requirement of `Pin::new_unchecked` called below. // // We can't use the `pin!` macro for this because `futures` is a tuple // and the standard library provides no way to pin-project to the fields // of a tuple. - let mut futures = ( $( maybe_done($e), )* ); + let mut futures = ( $( $crate::macros::support::maybe_done($e), )* ); // This assignment makes sure that the `poll_fn` closure only has a // reference to the futures, instead of taking ownership of them. This @@ -149,9 +146,9 @@ doc! {macro_rules! join { // Each time the future created by poll_fn is polled, if not using biased mode, // a different future is polled first to ensure every future passed to join! // can make progress even if one of the futures consumes the whole budget. - let mut rotator = <$rotator_select as RotatorSelect>::Rotator::<{$($total)*}>::default(); + let mut rotator = <$rotator_select as $crate::macros::support::RotatorSelect>::Rotator::<{$($total)*}>::default(); - poll_fn(move |cx| { + $crate::macros::support::poll_fn(move |cx| { const COUNT: u32 = $($total)*; let mut is_pending = false; @@ -176,10 +173,10 @@ doc! {macro_rules! join { // Safety: future is stored on the stack above // and never moved. - let mut fut = unsafe { Pin::new_unchecked(fut) }; + let mut fut = unsafe { $crate::macros::support::Pin::new_unchecked(fut) }; // Try polling - if fut.poll(cx).is_pending() { + if $crate::macros::support::Future::poll(fut.as_mut(), cx).is_pending() { is_pending = true; } } else { @@ -190,15 +187,15 @@ doc! {macro_rules! join { } if is_pending { - Pending + $crate::macros::support::Poll::Pending } else { - Ready(($({ + $crate::macros::support::Poll::Ready(($({ // 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) }; + let mut fut = unsafe { $crate::macros::support::Pin::new_unchecked(fut) }; fut.take_output().expect("expected completed future") },)*)) diff --git a/tokio/src/macros/try_join.rs b/tokio/src/macros/try_join.rs index 04f4825cdc0..9d5d7d75c0d 100644 --- a/tokio/src/macros/try_join.rs +++ b/tokio/src/macros/try_join.rs @@ -179,16 +179,13 @@ doc! {macro_rules! try_join { $( ( $($skip:tt)* ) $e:expr, )* }) => {{ - use $crate::macros::support::{maybe_done, poll_fn, Future, Pin, RotatorSelect}; - use $crate::macros::support::Poll::{Ready, Pending}; - // Safety: nothing must be moved out of `futures`. This is to satisfy // the requirement of `Pin::new_unchecked` called below. // // We can't use the `pin!` macro for this because `futures` is a tuple // and the standard library provides no way to pin-project to the fields // of a tuple. - let mut futures = ( $( maybe_done($e), )* ); + let mut futures = ( $( $crate::macros::support::maybe_done($e), )* ); // This assignment makes sure that the `poll_fn` closure only has a // reference to the futures, instead of taking ownership of them. This @@ -199,9 +196,9 @@ doc! {macro_rules! try_join { // Each time the future created by poll_fn is polled, if not using biased mode, // a different future is polled first to ensure every future passed to try_join! // can make progress even if one of the futures consumes the whole budget. - let mut rotator = <$rotator_select as RotatorSelect>::Rotator::<{$($total)*}>::default(); + let mut rotator = <$rotator_select as $crate::macros::support::RotatorSelect>::Rotator::<{$($total)*}>::default(); - poll_fn(move |cx| { + $crate::macros::support::poll_fn(move |cx| { const COUNT: u32 = $($total)*; let mut is_pending = false; @@ -226,13 +223,13 @@ doc! {macro_rules! try_join { // Safety: future is stored on the stack above // and never moved. - let mut fut = unsafe { Pin::new_unchecked(fut) }; + let mut fut = unsafe { $crate::macros::support::Pin::new_unchecked(fut) }; // Try polling - if fut.as_mut().poll(cx).is_pending() { + if $crate::macros::support::Future::poll(fut.as_mut(), cx).is_pending() { is_pending = true; } else if fut.as_mut().output_mut().expect("expected completed future").is_err() { - return Ready(Err(fut.take_output().expect("expected completed future").err().unwrap())) + return $crate::macros::support::Poll::Ready(Err(fut.take_output().expect("expected completed future").err().unwrap())) } } else { // Future skipped, one less future to skip in the next iteration @@ -242,15 +239,15 @@ doc! {macro_rules! try_join { } if is_pending { - Pending + $crate::macros::support::Poll::Pending } else { - Ready(Ok(($({ + $crate::macros::support::Poll::Ready(Ok(($({ // 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) }; + let mut fut = unsafe { $crate::macros::support::Pin::new_unchecked(fut) }; fut .take_output()