Skip to content

Commit f1dfbbb

Browse files
WaffleLapkinwaffle.lapkin@gmail.com
authored andcommitted
bless ui tests
1 parent 2744060 commit f1dfbbb

25 files changed

+393
-251
lines changed

src/doc/edition-guide

Submodule edition-guide deleted from e2ed891
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
# Never type fallback change
2+
3+
## Summary
4+
5+
- Never type (`!`) to any type ("never-to-any") coercions fall back to never type (`!`) rather than to unit type (`()`).
6+
- The `never_type_fallback_flowing_into_unsafe` lint is now `deny` by default.
7+
8+
<!-- [`never_type_fallback_flowing_into_unsafe`]: ../../rustc/lints/listing/error-by-default.html#never-type-fallback-flowing-into-unsafe -->
9+
10+
## Details
11+
12+
When the compiler sees a value of type `!` (never) in a [coercion site][], it implicitly inserts a coercion to allow the type checker to infer any type:
13+
14+
```rust,should_panic
15+
# #![feature(never_type)]
16+
// This:
17+
let x: u8 = panic!();
18+
19+
// ...is (essentially) turned by the compiler into:
20+
let x: u8 = absurd(panic!());
21+
22+
// ...where `absurd` is the following function
23+
// (it's sound because `!` always marks unreachable code):
24+
fn absurd<T>(x: !) -> T { x }
25+
```
26+
27+
This can lead to compilation errors if the type cannot be inferred:
28+
29+
```rust,compile_fail,E0282
30+
# #![feature(never_type)]
31+
# fn absurd<T>(x: !) -> T { x }
32+
// This:
33+
{ panic!() };
34+
35+
// ...gets turned into this:
36+
{ absurd(panic!()) }; //~ ERROR can't infer the type of `absurd`
37+
```
38+
39+
To prevent such errors, the compiler remembers where it inserted `absurd` calls, and if it can't infer the type, it uses the fallback type instead:
40+
41+
```rust,should_panic
42+
# #![feature(never_type)]
43+
# fn absurd<T>(x: !) -> T { x }
44+
type Fallback = /* An arbitrarily selected type! */ !;
45+
{ absurd::<Fallback>(panic!()) }
46+
```
47+
48+
This is what is known as "never type fallback".
49+
50+
Historically, the fallback type has been `()` (unit). This caused `!` to spontaneously coerce to `()` even when the compiler would not infer `()` without the fallback. That was confusing and has prevented the stabilization of the `!` type.
51+
52+
In the 2024 edition, the fallback type is now `!`. (We plan to make this change across all editions at a later date.) This makes things work more intuitively. Now when you pass `!` and there is no reason to coerce it to something else, it is kept as `!`.
53+
54+
In some cases your code might depend on the fallback type being `()`, so this can cause compilation errors or changes in behavior.
55+
56+
[coercion site]: ../../reference/type-coercions.html#coercion-sites
57+
58+
### `never_type_fallback_flowing_into_unsafe`
59+
60+
The default level of the `never_type_fallback_flowing_into_unsafe` lint has been raised from `warn` to `deny` in the 2024 Edition. This lint helps detect a particular interaction with the fallback to `!` and `unsafe` code which may lead to undefined behavior. See the link for a complete description.
61+
62+
## Migration
63+
64+
There is no automatic fix, but there is automatic detection of code that will be broken by the edition change. While still on a previous edition you will see warnings if your code will be broken.
65+
66+
The fix is to specify the type explicitly so that the fallback type is not used. Unfortunately, it might not be trivial to see which type needs to be specified.
67+
68+
One of the most common patterns broken by this change is using `f()?;` where `f` is generic over the `Ok`-part of the return type:
69+
70+
```rust
71+
# #![allow(dependency_on_unit_never_type_fallback)]
72+
# fn outer<T>(x: T) -> Result<T, ()> {
73+
fn f<T: Default>() -> Result<T, ()> {
74+
Ok(T::default())
75+
}
76+
77+
f()?;
78+
# Ok(x)
79+
# }
80+
```
81+
82+
You might think that, in this example, type `T` can't be inferred. However, due to the current desugaring of the `?` operator, it was inferred as `()`, and it will now be inferred as `!`.
83+
84+
To fix the issue you need to specify the `T` type explicitly:
85+
86+
```rust,edition2024
87+
# #![deny(dependency_on_unit_never_type_fallback)]
88+
# fn outer<T>(x: T) -> Result<T, ()> {
89+
# fn f<T: Default>() -> Result<T, ()> {
90+
# Ok(T::default())
91+
# }
92+
f::<()>()?;
93+
// ...or:
94+
() = f()?;
95+
# Ok(x)
96+
# }
97+
```
98+
99+
Another relatively common case is panicking in a closure:
100+
101+
```rust,should_panic
102+
# #![allow(dependency_on_unit_never_type_fallback)]
103+
trait Unit {}
104+
impl Unit for () {}
105+
106+
fn run<R: Unit>(f: impl FnOnce() -> R) {
107+
f();
108+
}
109+
110+
run(|| panic!());
111+
```
112+
113+
Previously `!` from the `panic!` coerced to `()` which implements `Unit`. However now the `!` is kept as `!` so this code fails because `!` doesn't implement `Unit`. To fix this you can specify the return type of the closure:
114+
115+
```rust,edition2024,should_panic
116+
# #![deny(dependency_on_unit_never_type_fallback)]
117+
# trait Unit {}
118+
# impl Unit for () {}
119+
#
120+
# fn run<R: Unit>(f: impl FnOnce() -> R) {
121+
# f();
122+
# }
123+
run(|| -> () { panic!() });
124+
```
125+
126+
A similar case to that of `f()?` can be seen when using a `!`-typed expression in one branch and a function with an unconstrained return type in the other:
127+
128+
```rust
129+
# #![allow(dependency_on_unit_never_type_fallback)]
130+
if true {
131+
Default::default()
132+
} else {
133+
return
134+
};
135+
```
136+
137+
Previously `()` was inferred as the return type of `Default::default()` because `!` from `return` was spuriously coerced to `()`. Now, `!` will be inferred instead causing this code to not compile because `!` does not implement `Default`.
138+
139+
Again, this can be fixed by specifying the type explicitly:
140+
141+
```rust,edition2024
142+
# #![deny(dependency_on_unit_never_type_fallback)]
143+
() = if true {
144+
Default::default()
145+
} else {
146+
return
147+
};
148+
149+
// ...or:
150+
151+
if true {
152+
<() as Default>::default()
153+
} else {
154+
return
155+
};
156+
```

tests/ui/editions/never-type-fallback-breaking.e2021.fixed

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33
//@[e2021] edition: 2021
44
//@[e2024] edition: 2024
55
//
6-
//@[e2021] run-pass
76
//@[e2021] run-rustfix
8-
//@[e2024] check-fail
97

108
fn main() {
119
m();
@@ -16,8 +14,8 @@ fn main() {
1614
}
1715

1816
fn m() {
19-
//[e2021]~^ WARN this function depends on never type fallback being `()`
20-
//[e2021]~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions!
17+
//[e2021]~^ error: this function depends on never type fallback being `()`
18+
//[e2021]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions!
2119
let x: () = match true {
2220
true => Default::default(),
2321
//[e2024]~^ error: the trait bound `!: Default` is not satisfied
@@ -28,8 +26,8 @@ fn m() {
2826
}
2927

3028
fn q() -> Option<()> {
31-
//[e2021]~^ WARN this function depends on never type fallback being `()`
32-
//[e2021]~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions!
29+
//[e2021]~^ error: this function depends on never type fallback being `()`
30+
//[e2021]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions!
3331
fn deserialize<T: Default>() -> Option<T> {
3432
Some(T::default())
3533
}
@@ -45,8 +43,8 @@ fn help<'a: 'a, T: Into<()>, U>(_: U) -> Result<T, ()> {
4543
Err(())
4644
}
4745
fn meow() -> Result<(), ()> {
48-
//[e2021]~^ WARN this function depends on never type fallback being `()`
49-
//[e2021]~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions!
46+
//[e2021]~^ error: this function depends on never type fallback being `()`
47+
//[e2021]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions!
5048
help::<(), _>(1)?;
5149
//[e2024]~^ error: the trait bound `(): From<!>` is not satisfied
5250
Ok(())
@@ -57,8 +55,8 @@ pub fn takes_apit<T>(_y: impl Fn() -> T) -> Result<T, ()> {
5755
}
5856

5957
pub fn fallback_return() -> Result<(), ()> {
60-
//[e2021]~^ WARN this function depends on never type fallback being `()`
61-
//[e2021]~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions!
58+
//[e2021]~^ error: this function depends on never type fallback being `()`
59+
//[e2021]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions!
6260
takes_apit::<()>(|| Default::default())?;
6361
//[e2024]~^ error: the trait bound `!: Default` is not satisfied
6462
Ok(())
@@ -71,8 +69,8 @@ fn mk<T>() -> Result<T, ()> {
7169
fn takes_apit2(_x: impl Default) {}
7270

7371
fn fully_apit() -> Result<(), ()> {
74-
//[e2021]~^ WARN this function depends on never type fallback being `()`
75-
//[e2021]~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions!
72+
//[e2021]~^ error: this function depends on never type fallback being `()`
73+
//[e2021]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions!
7674
takes_apit2(mk::<()>()?);
7775
//[e2024]~^ error: the trait bound `!: Default` is not satisfied
7876
Ok(())

0 commit comments

Comments
 (0)