pub static NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE: &'static LintExpand 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
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::<()>() }
};