pub static NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE: &'static Lint
Expand description
The never_type_fallback_flowing_into_unsafe
lint detects cases where never type fallback
affects unsafe function calls.
§Never type fallback
When the compiler sees a value of type !
it implicitly inserts a coercion (if possible),
to allow type check to infer any type:
// this
let x: u8 = panic!();
// is (essentially) turned by the compiler into
let x: u8 = absurd(panic!());
// where absurd is a function with the following signature
// (it's sound, because `!` always marks unreachable code):
fn absurd<T>(never: !) -> T { ... }
While it’s convenient to be able to use non-diverging code in one of the branches (like
if a { b } else { return }
) this could lead to compilation errors:
// this
{ panic!() };
// gets turned into this
{ absurd(panic!()) }; // error: can't infer the type of `absurd`
To prevent such errors, compiler remembers where it inserted absurd
calls, and if it
can’t infer their type, it sets the type to fallback. { absurd::<Fallback>(panic!()) };
.
This is what is known as “never type fallback”.
§Example
#![deny(never_type_fallback_flowing_into_unsafe)]
fn main() {
if true {
// return has type `!` which, is some cases, causes never type fallback
return
} else {
// `zeroed` is an unsafe function, which returns an unbounded type
unsafe { std::mem::zeroed() }
};
// depending on the fallback, `zeroed` may create `()` (which is completely sound),
// or `!` (which is instant undefined behavior)
}
{{produces}}
§Explanation
Due to historic reasons never type fallback was ()
, meaning that !
got spontaneously
coerced to ()
. There are plans to change that, but they may make the code such as above
unsound. Instead of depending on the fallback, you should specify the type explicitly:
if true {
return
} else {
// type is explicitly specified, fallback can't hurt us no more
unsafe { std::mem::zeroed::<()>() }
};