-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Warning about unreachable code when combining tokio::select and async function that returns !
#2665
Comments
Hi, @RAnders00 ! I've tried to follow your lead. First, this is exactly what you wrote (I'm copying again to compare it) use std::time::Duration;
async fn one() -> ! {
loop {
tokio::time::delay_for(Duration::from_secs(1)).await;
println!("tick");
}
}
async fn two() {
tokio::time::delay_for(Duration::from_secs(3)).await;
}
#[tokio::main]
async fn main() {
tokio::select! {
_ = one() => {},
_ = two() => {}
};
println!("finished.");
} And this expands to: use std::time::Duration;
async fn one() -> ! {
loop {
tokio::time::delay_for(Duration::from_secs(1)).await;
println!("tick");
}
}
async fn two() {
tokio::time::delay_for(Duration::from_secs(3)).await;
}
#[tokio::main]
async fn main() {
tokio::select! {
_ = one() => {},
_ = two() => {}
};
println!("finished.");
} #![feature(prelude_import)]
#[prelude_import]
use std::prelude::v1::*;
#[macro_use]
extern crate std;
use std::time::Duration;
async fn one() -> ! {
loop {
tokio::time::delay_for(Duration::from_secs(1)).await;
{
::std::io::_print(::core::fmt::Arguments::new_v1(
&["tick\n"],
&match () {
() => [],
},
));
};
}
}
async fn two() {
tokio::time::delay_for(Duration::from_secs(3)).await;
}
fn main() {
tokio::runtime::Runtime::new().unwrap().block_on(async {
{
{
mod util {
pub(super) enum Out<_0, _1> {
_0(_0),
_1(_1),
Disabled,
}
pub(super) type Mask = u8;
}
use ::tokio::macros::support::Future;
use ::tokio::macros::support::Pin;
use ::tokio::macros::support::Poll::{Ready, Pending};
const BRANCHES: u32 = 2;
let mut disabled: util::Mask = Default::default();
if !true {
let mask = 1 << 0;
disabled |= mask;
}
if !true {
let mask = 1 << 1;
disabled |= mask;
}
let mut output = {
let mut futures = (one(), two());
::tokio::macros::support::poll_fn(|cx| {
let mut is_pending = false;
let start = ::tokio::macros::support::thread_rng_n(BRANCHES);
for i in 0..BRANCHES {
let branch = (start + i) % BRANCHES;
match branch {
0 => {
let mask = 1 << branch;
if disabled & mask == mask {
continue;
}
let (fut, ..) = &mut futures;
let mut fut = unsafe { Pin::new_unchecked(fut) };
let out = match fut.poll(cx) {
Ready(out) => out,
Pending => {
is_pending = true;
continue;
}
};
disabled |= mask;
#[allow(unused_variables)]
match &out {
_ => {}
_ => continue,
}
return Ready(util::Out::_0(out));
}
1 => {
let mask = 1 << branch;
if disabled & mask == mask {
continue;
}
let (_, fut, ..) = &mut futures;
let mut fut = unsafe { Pin::new_unchecked(fut) };
let out = match fut.poll(cx) {
Ready(out) => out,
Pending => {
is_pending = true;
continue;
}
};
disabled |= mask;
#[allow(unused_variables)]
match &out {
_ => {}
_ => continue,
}
return Ready(util::Out::_1(out));
}
_ => ::std::rt::begin_panic_fmt(&::core::fmt::Arguments::new_v1(
&["internal error: entered unreachable code: "],
&match (
&"reaching this means there probably is an off by one bug",
) {
(arg0,) => [::core::fmt::ArgumentV1::new(
arg0,
::core::fmt::Display::fmt,
)],
},
)),
}
}
if is_pending {
Pending
} else {
Ready(util::Out::Disabled)
}
})
.await
};
match output {
util::Out::_0(_) => {}
util::Out::_1(_) => {}
util::Out::Disabled => {
::std::rt::begin_panic("internal error: entered unreachable code")
}
_ => ::std::rt::begin_panic_fmt(&::core::fmt::Arguments::new_v1(
&["internal error: entered unreachable code: "],
&match (&"failed to match bind",) {
(arg0,) => [::core::fmt::ArgumentV1::new(
arg0,
::core::fmt::Display::fmt,
)],
},
)),
}
};
{
::std::io::_print(::core::fmt::Arguments::new_v1(
&["finished.\n"],
&match () {
() => [],
},
));
};
}
})
} And we actually get:
However, making a small adjustment the warning disappears: 7c7
< async fn one() -> ! {
---
> async fn one() { You can check it here The expanded version: ❯ cat without-exl/src/main.rs
#![feature(prelude_import)]
#[prelude_import]
use std::prelude::v1::*;
#[macro_use]
extern crate std;
use std::time::Duration;
async fn one() {
loop {
tokio::time::delay_for(Duration::from_secs(1)).await;
{
::std::io::_print(::core::fmt::Arguments::new_v1(
&["tick\n"],
&match () {
() => [],
},
));
};
}
}
async fn two() {
tokio::time::delay_for(Duration::from_secs(3)).await;
}
fn main() {
tokio::runtime::Runtime::new().unwrap().block_on(async {
{
{
mod util {
pub(super) enum Out<_0, _1> {
_0(_0),
_1(_1),
Disabled,
}
pub(super) type Mask = u8;
}
use ::tokio::macros::support::Future;
use ::tokio::macros::support::Pin;
use ::tokio::macros::support::Poll::{Ready, Pending};
const BRANCHES: u32 = 2;
let mut disabled: util::Mask = Default::default();
if !true {
let mask = 1 << 0;
disabled |= mask;
}
if !true {
let mask = 1 << 1;
disabled |= mask;
}
let mut output = {
let mut futures = (one(), two());
::tokio::macros::support::poll_fn(|cx| {
let mut is_pending = false;
let start = ::tokio::macros::support::thread_rng_n(BRANCHES);
for i in 0..BRANCHES {
let branch = (start + i) % BRANCHES;
match branch {
0 => {
let mask = 1 << branch;
if disabled & mask == mask {
continue;
}
let (fut, ..) = &mut futures;
let mut fut = unsafe { Pin::new_unchecked(fut) };
let out = match fut.poll(cx) {
Ready(out) => out,
Pending => {
is_pending = true;
continue;
}
};
disabled |= mask;
#[allow(unused_variables)]
match &out {
_ => {}
_ => continue,
}
return Ready(util::Out::_0(out));
}
1 => {
let mask = 1 << branch;
if disabled & mask == mask {
continue;
}
let (_, fut, ..) = &mut futures;
let mut fut = unsafe { Pin::new_unchecked(fut) };
let out = match fut.poll(cx) {
Ready(out) => out,
Pending => {
is_pending = true;
continue;
}
};
disabled |= mask;
#[allow(unused_variables)]
match &out {
_ => {}
_ => continue,
}
return Ready(util::Out::_1(out));
}
_ => ::std::rt::begin_panic_fmt(&::core::fmt::Arguments::new_v1(
&["internal error: entered unreachable code: "],
&match (
&"reaching this means there probably is an off by one bug",
) {
(arg0,) => [::core::fmt::ArgumentV1::new(
arg0,
::core::fmt::Display::fmt,
)],
},
)),
}
}
if is_pending {
Pending
} else {
Ready(util::Out::Disabled)
}
})
.await
};
match output {
util::Out::_0(_) => {}
util::Out::_1(_) => {}
util::Out::Disabled => {
::std::rt::begin_panic("internal error: entered unreachable code")
}
_ => ::std::rt::begin_panic_fmt(&::core::fmt::Arguments::new_v1(
&["internal error: entered unreachable code: "],
&match (&"failed to match bind",) {
(arg0,) => [::core::fmt::ArgumentV1::new(
arg0,
::core::fmt::Display::fmt,
)],
},
)),
}
};
{
::std::io::_print(::core::fmt::Arguments::new_v1(
&["finished.\n"],
&match () {
() => [],
},
));
};
}
})
} So honestly, I'm in doubt whether this is a |
Seems that the issue comes from yielding the future's output value, when being tokio/tokio/src/macros/select.rs Line 391 in 2bc6bc1
So what is happening is that the return type of Any ideas on how to move forward? @Darksonn |
I think that we just want an |
Solves tokio-rs#2665 by adding #[allow(unreachable_code)] inside a branch matching arm.
Solves tokio-rs#2665 by adding #[allow(unreachable_code)] inside a branch matching arm.
Solves tokio-rs#2665 by adding #[allow(unreachable_code)] inside a branch matching arm.
Solves tokio-rs#2665 by adding #[allow(unreachable_code)] inside a branch matching arm.
Solves #2665 by adding #[allow(unreachable_code)] inside a branch matching arm. Co-authored-by: Alice Ryhl <alice@ryhl.io>
Closing -- fixed in #2678 |
Version
v0.2.21
rust:
stable-x86_64-pc-windows-gnu
rustc 1.44.1 (c7087fe00 2020-06-17)
Platform
Windows 10 1909 64-bit
Description
I have an async function that drives an infinite loop, and I declared it
async
and its return type is!
.Consider this minimal example:
playground link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=2924dce7469017c6534d72ab407bdf07
When compiling it, I get the following compiler warning:
I think the warning is silly because this is a perfectly valid way of using
tokio::select!
, to stop an infinite async task.I would expect there to be no warning.
I dug a bit into it using
cargo expand
, and this is what the above example generated (removed/replaced some imports of std internals to make it compile):playground link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=cbaf73e335a70623c88feaa64e4aa8a9
Where the error is now (on the expanded version):
The text was updated successfully, but these errors were encountered: