-
Notifications
You must be signed in to change notification settings - Fork 13.2k
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
unreachable_patterns
fires on match arm that can't be removed.
#129352
Comments
cc @Nadrieril |
The way to fix this would be to specify an uninhabited type which isn't never type (and thus doesn't experience never type fallback). i.e. doing an I think the only way to fix the warning is to specify the type of the futures, either directly: fn ascribe_future<R>(f: impl Future<Output = R>) -> impl Future<Output = R> { f }
let a = ascribe_future::<Infallible>(async { loop {} });
let b = ascribe_future::<Infallible>(async { loop {} }); or in the async fn select<A: Future<Output = RA>, B: Future<Output = RB>, RA, RB>(_: A, _: B) -> Result<A::Output, B::Output> {
todo!()
}
match select::<_, _, Infallible, Infallible>(a, b).await {} Neither is particularly nice and the interaction with the old never type fallback is very unfortunate, but yeah :( |
Well that's fun :D Yeah, to mitigate we'd have to make pattern exhaustiveness influence type inference I think, which... yeah. |
The code above can be fixed to not warn with: //@ edition: 2021
use core::future::Future;
async fn select<A: Future, B: Future>(_: A, _: B) -> Result<A::Output, B::Output> {
loop {}
}
pub async fn f() -> ! {
let a = async { loop {} };
let b = async { loop {} };
match select(a, b).await {
_ => unreachable!(), //[2021]~ <--- No warning.
}
} This does warn in Rust 2024 as one might expect and as is desirable. |
This comment was marked as resolved.
This comment was marked as resolved.
it's undesirable to have to write |
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This is the code where I ran into this originally, if you're curious. The two futures are supposed to never return. I could've simply written |
Thanks. OK, I see now what you were trying to enforce there. |
Reflecting on this, there are I think two separate issues:
It seems kind of an accident that this approach to the static assertion works at all without warnings. Consider, e.g., that this spiritually-identical code warns on stable Rust: fn select<A, B>(_: impl FnOnce() -> A, _: impl FnOnce() -> B) -> Result<A, B> {
todo!()
}
pub fn f() -> ! {
let a = || loop {};
let b = || loop {};
match select(a, b) {
// ^^^^^ WARN unreachable expression
Ok(x) | Err(x) => x,
}
} That adding in an That warning would push most people to instead write: pub fn f() -> ! {
let a = || loop {};
let b = || loop {};
_ = select(a, b);
unreachable!()
} ...that of course loses the static assertion you want to express. At which point, I'd probably just express that static assertion directly: fn assert_output<T>(_: &impl FnOnce() -> T) {}
pub fn f() -> ! {
let a = || loop {};
let b = || loop {};
assert_output::<Infallible>(&a); //~ <---
assert_output::<Infallible>(&b); //~ <---
_ = select(a, b);
unreachable!()
} We can apply that same transformation to the original example: async fn select<A: Future, B: Future>(_: A, _: B) -> Result<A::Output, B::Output> {
todo!()
}
fn assert_output<T>(_: &impl Future<Output = T>) {}
pub async fn f() -> ! {
let a = async { loop {} };
let b = async { loop {} };
assert_output::<Infallible>(&a);
assert_output::<Infallible>(&b);
_ = select(a, b).await;
unreachable!()
} This approach still works even if the match isn't dead, e.g., this is warning-free in all editions: pub fn f() -> ! {
let a = || loop {};
let b = || ();
assert_output::<Infallible>(&a);
match select(a, b)() {
Err(()) => todo!(),
}
} |
The warning is similarly annoying in cases like this:
Of course, here the |
We'll be reverting the lint change for 1.82.0 and deciding how to proceed further once that's done (likely with a sublint for the empty pattern case) |
The warning happening in beta will be rolled back (for now) according to rust-lang/rust#129352 (comment) Until this change propagates to a beta release, we can live with the warnings.
playground
warns with
unreachable_patterns
on both armsbut removing either doesn't actually compile
So this code seems "unfixable" with the new lint.
What's happening is the match arm is unreachable, but it still has the "side effect" of making the async block be inferred to
Future<Output=!>
instead ofFuture<Output=()>
. So, if you remove it the future now gets inferred to return()
, and the arm is not unreachable anymore.In edition 2024 it Just Works if you write
match select(a, b).await {}
, because the fallback is!
instead of()
.This seems a bit hard to mitigate in Edition 2021, it seems more like an unfortunate combination of the lint and the inference fallback more than a bug. I'm opening the issue anyway to share it, just in case anyone has an idea.
The text was updated successfully, but these errors were encountered: