rustc
, never-type
, bug
()
67225An empty type is something that never exists in the runtime as there is no way to create one and therefore, such type in code gives us a static gurantee that a piece of code will never be executed.
use std::process::exit;
fn main() {
let x: ! = exit(0);
// `x` is in scope, everything below
// here is dead code.
let y: String = match x {
// no match cases as `Never` has
// no variants
};
// we can still use `y` though
println!("Our string is: {}", y);
// we can use `x` to diverge
x
}
An HIR node is divergent if it does not exit normally, instead it return
s / break
s / panic
s leaving behind dead code.
In rustc, this is tracked using the enum Diverges.
There are different variants of it:
Maybe
- Some of the cases diverge, while others require further analysis to determine.Always
- Certainly known to diverge, so next sibling and parent are unreachable.WarnedAlways
- Same as Always
with a reachability warning always emitted.Further explanations given by Niko:
So, what would a never returning node result in ?
Ans: !
type.
Similarly, !
is also a type that can be assigned. Therefore, sometimes the compiler creates an inference variable ?X
to represent the type to which !
is being coersed.
e.g.
let x: u32 = return ...
will type-check, because the type of return will be ?X
and ?X = u32
is a valid result.
The problem is that we generally require all inference variables to have a defined value.
Which is why let x = None
won't type-check on its own and would require Option<?T>
.
What is ?T
?
Ans: Its a constraint.
But it would be annoying if we required to constrain the divergent variables as it should not matter what !
is coersed to.
Therefore,
let x = return 22;
shouldn't fail compilation if we can't figure out the type of x
. Therefore, today, we assign ?X
as the type of x
and at the end of type-checking, when we still couldn't resolve ?X
, we fallback to ()
as the type of x
.
The fallback type in this case should be !
instead of ()
.
We start out by detecting cases where that's a problem.
fn unconstrained_return<T>() -> Result<T, String> {
let ffi: fn() -> T = transmute(some_pointer);
Ok(ffi())
}
fn foo() {
match unconstrained_return::<_>() {
Ok(x) => x, // `x` has type `_`, which is unconstrained
Err(s) => panic!(s), // … except for unifying with the type of `panic!()`
// so that both `match` arms have the same type.
// Therefore `_` resolves to `!` and we "return" an `Ok(!)` value.
};
}
Walk through what goes wrong:
T
gets specified as _
– _
means "make fresh inference variable", let's call it ?X
unconstrained_return::<?X>()
is Result<?X, String>
x
in Ok(x)
is ?X
Ok(x) => x
is ?X
s
in Err(s)
is String
Err(s) => panic!(s)
is !
!
we create a fresh variable ?V
?V
as divergingM
such that both match arms can be coerced to that type M
?M
where ?X
can be coerced to ?M
?V
can be coerced to ?M
?X = ?V = ?M
?X
and ?V
are both unconstrained
?X
?V
falls back to !
, so effectively ?V
becomes !
?X
is unified, it becomes !
Where is this implemented?
fallback_if_possible
!
Slightly bigger than the set of unified variables
fn foo<T, U>() where T: Eq<U> { }
trait Eq<A> { }
impl Eq<!> for ! { }
diverging fallback
.
diverging fallback
.HIR_ID
for expressions where flag was set to warnedAlways
on entry.warn_if_unreachable
hir_id
to some set roughly on this line if self.diverges()
is set to WarnedAlways
Example of case where we would NOT issue a warning but is still UB:
// the bet is that an unconstrained return value like `T` here signals data
// that is never used, e.g. this fn could return `Ok(())`, but in this case
// that is not true:
fn unconstrained_return<T>() -> Result<(), T> {
let ffi: fn() -> T = transmute(some_pointer);
ffi(); // produces a value of type T
}
fn foo() {
match unconstrained_return::<_>() { // _ is ?X
Ok(_) => Ok(()), // type of `Ok` is `Result<(), ?A>`
Err(s) => Err(panic!(s)), // type of `Err(_)` is `Result<?B, ?V>` where `?V` is diverging
}; // type of expression is `Result<(), ?V>`
}
or
or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up
Syntax | Example | Reference | |
---|---|---|---|
# Header | Header | 基本排版 | |
- Unordered List |
|
||
1. Ordered List |
|
||
- [ ] Todo List |
|
||
> Blockquote | Blockquote |
||
**Bold font** | Bold font | ||
*Italics font* | Italics font | ||
~~Strikethrough~~ | |||
19^th^ | 19th | ||
H~2~O | H2O | ||
++Inserted text++ | Inserted text | ||
==Marked text== | Marked text | ||
[link text](https:// "title") | Link | ||
 | Image | ||
`Code` | Code |
在筆記中貼入程式碼 | |
```javascript var i = 0; ``` |
|
||
:smile: | ![]() |
Emoji list | |
{%youtube youtube_id %} | Externals | ||
$L^aT_eX$ | LaTeX | ||
:::info This is a alert area. ::: |
This is a alert area. |
On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?
Please give us some advice and help us improve HackMD.
Do you want to remove this version name and description?
Syncing