diff options
| author | Matthias Krüger <matthias.krueger@famsik.de> | 2024-08-01 00:50:12 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-08-01 00:50:12 +0200 |
| commit | ac67d1005042fea5495520d5ed9076ddaf0061b4 (patch) | |
| tree | 6cc33021dcd9ec4d38ba35a06416e495a2172181 /tests | |
| parent | c4ee4118548f9025483dbba50c21ce8419adfa94 (diff) | |
| parent | 74754b878621b42097de33f9579d7630fa704627 (diff) | |
| download | rust-ac67d1005042fea5495520d5ed9076ddaf0061b4.tar.gz rust-ac67d1005042fea5495520d5ed9076ddaf0061b4.zip | |
Rollup merge of #128443 - compiler-errors:async-unreachable, r=fmease
Properly mark loop as diverging if it has no breaks
Due to specifics about the desugaring of the `.await` operator, HIR typeck doesn't recognize that `.await`ing an `impl Future<Output = !>` will diverge in the same way as calling a `fn() -> !`.
This is because the await operator desugars to approximately:
```rust
loop {
match future.poll(...) {
Poll::Ready(x) => break x,
Poll::Pending => {}
}
}
```
We know that the value of `x` is `!`, however since `break` is a coercion site, we coerce `!` to some `?0` (the type of the loop expression). Then since the type of the `loop {...}` expression is `?0`, we will not detect the loop as diverging like we do with other expressions that evaluate to `!`:
https://github.com/rust-lang/rust/blob/0b5eb7ba7bd796fb39c8bb6acd9ef6c140f28b65/compiler/rustc_hir_typeck/src/expr.rs#L240-L243
We can technically fix this in two ways:
1. Make coercion of loop exprs more eagerly result in a type of `!` when the only break expressions have type `!`.
2. Make loops understand that all of that if they have only diverging break values, then the loop diverges as well.
(1.) likely has negative effects on inference, and seems like a weird special case to drill into coercion. However, it turns out that (2.) is very easy to implement, we already record whether a loop has any break expressions, and when we do so, we actually skip over any break expressions with diverging values!:
https://github.com/rust-lang/rust/blob/0b5eb7ba7bd796fb39c8bb6acd9ef6c140f28b65/compiler/rustc_hir_typeck/src/expr.rs#L713-L716
Thus, we can consider the loop as diverging if we see that it has no breaks, which is the change implemented in this PR.
This is not usually a problem in regular code for two reasons:
1. In regular code, we already mark `break diverging()` as unreachable if `diverging()` is unreachable. We don't do this for `.await`, since we suppress unreachable errors within `.await` (#64930). Un-suppressing this code will result in spurious unreachable expression errors pointing to internal await machinery.
3. In loops that truly have no breaks (e.g. `loop {}`), we already evaluate the type of the loop to `!`, so this special case is kinda moot. This only affects loops that have `break`s with values of type `!`.
Thus, this seems like a change that may affect more code than just `.await`, but it likely does not in meaningful ways; if it does, it's certainly correct to apply.
Fixes #128434
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/ui/async-await/unreachable-lint-2.rs | 15 | ||||
| -rw-r--r-- | tests/ui/async-await/unreachable-lint-2.stderr | 17 |
2 files changed, 32 insertions, 0 deletions
diff --git a/tests/ui/async-await/unreachable-lint-2.rs b/tests/ui/async-await/unreachable-lint-2.rs new file mode 100644 index 00000000000..137cb32481b --- /dev/null +++ b/tests/ui/async-await/unreachable-lint-2.rs @@ -0,0 +1,15 @@ +//@ edition:2018 + +#![deny(unreachable_code)] + +async fn foo() { + endless().await; + println!("this is unreachable!"); + //~^ ERROR unreachable statement +} + +async fn endless() -> ! { + loop {} +} + +fn main() { } diff --git a/tests/ui/async-await/unreachable-lint-2.stderr b/tests/ui/async-await/unreachable-lint-2.stderr new file mode 100644 index 00000000000..cbebc9951f3 --- /dev/null +++ b/tests/ui/async-await/unreachable-lint-2.stderr @@ -0,0 +1,17 @@ +error: unreachable statement + --> $DIR/unreachable-lint-2.rs:7:5 + | +LL | endless().await; + | ----- any code following this expression is unreachable +LL | println!("this is unreachable!"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unreachable statement + | +note: the lint level is defined here + --> $DIR/unreachable-lint-2.rs:3:9 + | +LL | #![deny(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + |
