about summary refs log tree commit diff
path: root/compiler/rustc_codegen_llvm/src
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2024-08-01 00:50:12 +0200
committerGitHub <noreply@github.com>2024-08-01 00:50:12 +0200
commitac67d1005042fea5495520d5ed9076ddaf0061b4 (patch)
tree6cc33021dcd9ec4d38ba35a06416e495a2172181 /compiler/rustc_codegen_llvm/src
parentc4ee4118548f9025483dbba50c21ce8419adfa94 (diff)
parent74754b878621b42097de33f9579d7630fa704627 (diff)
downloadrust-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 'compiler/rustc_codegen_llvm/src')
0 files changed, 0 insertions, 0 deletions