rust/tests/ui/pattern/usefulness/empty-types.rs

682 lines
20 KiB
Rust

//@ revisions: normal min_exh_pats exhaustive_patterns never_pats
// gate-test-min_exhaustive_patterns
//
// This tests correct handling of empty types in exhaustiveness checking.
//
// Most of the subtlety of this file happens in scrutinee places which are not required to hold
// valid data, namely dereferences and union field accesses. In these cases, empty arms can
// generally not be omitted, except with `exhaustive_patterns` which ignores this..
#![feature(never_type)]
// This feature is useful to avoid `!` falling back to `()` all the time.
#![feature(never_type_fallback)]
#![cfg_attr(exhaustive_patterns, feature(exhaustive_patterns))]
#![cfg_attr(min_exh_pats, feature(min_exhaustive_patterns))]
#![cfg_attr(never_pats, feature(never_patterns))]
//[never_pats]~^ WARN the feature `never_patterns` is incomplete
#![allow(dead_code, unreachable_code)]
#![deny(unreachable_patterns)]
#[derive(Copy, Clone)]
enum Void {}
/// A bunch of never situations that can't be normally constructed.
#[derive(Copy, Clone)]
struct NeverBundle {
never: !,
void: Void,
tuple_never: (!, !),
tuple_half_never: (u32, !),
array_3_never: [!; 3],
result_never: Result<!, !>,
}
/// A simplified `MaybeUninit` to test union field accesses.
#[derive(Copy, Clone)]
union Uninit<T: Copy> {
value: T,
uninit: (),
}
impl<T: Copy> Uninit<T> {
fn new() -> Self {
Self { uninit: () }
}
}
// Simple cases of omitting empty arms, all with known_valid scrutinees.
fn basic(x: NeverBundle) {
let never: ! = x.never;
match never {}
match never {
_ => {} //~ ERROR unreachable pattern
}
match never {
_x => {} //~ ERROR unreachable pattern
}
let ref_never: &! = &x.never;
match ref_never {}
//~^ ERROR non-empty
match ref_never {
// useful, reachable
_ => {}
}
match ref_never {
// useful, reachable
&_ => {}
}
let tuple_half_never: (u32, !) = x.tuple_half_never;
match tuple_half_never {}
//[normal,never_pats]~^ ERROR non-empty
match tuple_half_never {
(_, _) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
}
let tuple_never: (!, !) = x.tuple_never;
match tuple_never {}
//[normal,never_pats]~^ ERROR non-empty
match tuple_never {
_ => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
}
match tuple_never {
(_, _) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
}
match tuple_never.0 {}
match tuple_never.0 {
_ => {} //~ ERROR unreachable pattern
}
let res_u32_never: Result<u32, !> = Ok(0);
match res_u32_never {}
//~^ ERROR non-exhaustive
match res_u32_never {
//[normal,never_pats]~^ ERROR non-exhaustive
Ok(_) => {}
}
match res_u32_never {
Ok(_) => {}
Err(_) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
}
match res_u32_never {
//~^ ERROR non-exhaustive
Ok(0) => {}
Err(_) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
}
let Ok(_x) = res_u32_never;
//[normal,never_pats]~^ ERROR refutable
let Ok(_x) = res_u32_never.as_ref();
//~^ ERROR refutable
// Non-obvious difference: here there's an implicit dereference in the patterns, which makes the
// inner place !known_valid. `exhaustive_patterns` ignores this.
let Ok(_x) = &res_u32_never;
//[normal,min_exh_pats,never_pats]~^ ERROR refutable
let result_never: Result<!, !> = x.result_never;
match result_never {}
//[normal,never_pats]~^ ERROR non-exhaustive
match result_never {
_ => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
}
match result_never {
//[normal,never_pats]~^ ERROR non-exhaustive
Ok(_) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
}
match result_never {
Ok(_) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
_ => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
}
match result_never {
Ok(_) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
Err(_) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
}
}
// Check for a few cases that `Void` and `!` are treated the same.
fn void_same_as_never(x: NeverBundle) {
unsafe {
match x.void {}
match x.void {
_ => {} //~ ERROR unreachable pattern
}
match x.void {
_ if false => {} //~ ERROR unreachable pattern
}
let opt_void: Option<Void> = None;
match opt_void {
//[normal,never_pats]~^ ERROR non-exhaustive
None => {}
}
match opt_void {
None => {}
Some(_) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
}
match opt_void {
None => {}
_ => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
}
let ref_void: &Void = &x.void;
match *ref_void {}
match *ref_void {
_ => {}
}
let ref_opt_void: &Option<Void> = &None;
match *ref_opt_void {
//[normal,min_exh_pats,never_pats]~^ ERROR non-exhaustive
None => {}
}
match *ref_opt_void {
None => {}
Some(_) => {}
}
match *ref_opt_void {
None => {}
_ => {}
}
match *ref_opt_void {
None => {}
_a => {}
}
let union_void = Uninit::<Void>::new();
match union_void.value {}
match union_void.value {
_ => {}
}
let ptr_void: *const Void = std::ptr::null();
match *ptr_void {}
match *ptr_void {
_ => {}
}
}
}
// Test if we correctly determine validity from the scrutinee expression.
fn invalid_scrutinees(x: NeverBundle) {
let ptr_never: *const ! = std::ptr::null();
let never: ! = x.never;
let ref_never: &! = &never;
struct NestedNeverBundle(NeverBundle);
let nested_x = NestedNeverBundle(x);
// These should be considered known_valid and warn unreachable.
unsafe {
// A plain `!` value must be valid.
match never {}
match never {
_ => {} //~ ERROR unreachable pattern
}
// A block forces a copy.
match { *ptr_never } {}
match { *ptr_never } {
_ => {} //~ ERROR unreachable pattern
}
// This field access is not a dereference.
match x.never {}
match x.never {
_ => {} //~ ERROR unreachable pattern
}
// This nested field access is not a dereference.
match nested_x.0.never {}
match nested_x.0.never {
_ => {} //~ ERROR unreachable pattern
}
// Indexing is like a field access. This one does not access behind a reference.
let array_3_never: [!; 3] = x.array_3_never;
match array_3_never[0] {}
match array_3_never[0] {
_ => {} //~ ERROR unreachable pattern
}
}
// These should be considered !known_valid and not warn unreachable.
unsafe {
// A pointer may point to a place with an invalid value.
match *ptr_never {}
match *ptr_never {
_ => {}
}
// A reference may point to a place with an invalid value.
match *ref_never {}
match *ref_never {
_ => {}
}
// This field access is a dereference.
let ref_x: &NeverBundle = &x;
match ref_x.never {}
match ref_x.never {
_ => {}
}
// This nested field access is a dereference.
let nested_ref_x: &NestedNeverBundle = &nested_x;
match nested_ref_x.0.never {}
match nested_ref_x.0.never {
_ => {}
}
// A cast does not load.
match (*ptr_never as Void) {}
match (*ptr_never as Void) {
_ => {}
}
// A union field may contain invalid data.
let union_never = Uninit::<!>::new();
match union_never.value {}
match union_never.value {
_ => {}
}
// Indexing is like a field access. This one accesses behind a reference.
let slice_never: &[!] = &[];
match slice_never[0] {}
match slice_never[0] {
_ => {}
}
}
}
// Test we correctly track validity as we dig into patterns. Validity changes when we go under a
// dereference or a union field access, and it otherwise preserved.
fn nested_validity_tracking(bundle: NeverBundle) {
let never: ! = bundle.never;
let ref_never: &! = &never;
let tuple_never: (!, !) = bundle.tuple_never;
let result_never: Result<!, !> = bundle.result_never;
let union_never = Uninit::<!>::new();
// These should be considered known_valid and warn unreachable.
match never {
_ => {} //~ ERROR unreachable pattern
}
match tuple_never {
(_, _) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
}
match result_never {
Ok(_) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
Err(_) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
}
// These should be considered !known_valid and not warn unreachable.
match ref_never {
&_ => {}
}
match union_never {
Uninit { value: _ } => {}
}
}
// Test we don't allow empty matches on empty types if the scrutinee is `!known_valid`.
fn invalid_empty_match(bundle: NeverBundle) {
// We allow these two for backwards-compability.
let x: &! = &bundle.never;
match *x {}
let x: &Void = &bundle.void;
match *x {}
let x: &(u32, !) = &bundle.tuple_half_never;
match *x {} //[normal,min_exh_pats,never_pats]~ ERROR non-exhaustive
let x: &(!, !) = &bundle.tuple_never;
match *x {} //[normal,min_exh_pats,never_pats]~ ERROR non-exhaustive
let x: &Result<!, !> = &bundle.result_never;
match *x {} //[normal,min_exh_pats,never_pats]~ ERROR non-exhaustive
let x: &[!; 3] = &bundle.array_3_never;
match *x {} //[normal,min_exh_pats,never_pats]~ ERROR non-exhaustive
}
fn arrays_and_slices(x: NeverBundle) {
let slice_never: &[!] = &[];
match slice_never {}
//~^ ERROR non-empty
match slice_never {
//[normal,min_exh_pats,never_pats]~^ ERROR not covered
[] => {}
}
match slice_never {
[] => {}
[_] => {}
[_, _, ..] => {}
}
match slice_never {
//[normal,min_exh_pats]~^ ERROR `&[]`, `&[_]` and `&[_, _]` not covered
//[exhaustive_patterns]~^^ ERROR `&[]` not covered
//[never_pats]~^^^ ERROR `&[]`, `&[!]` and `&[!, !]` not covered
[_, _, _, ..] => {}
}
match slice_never {
[] => {}
_ => {}
}
match slice_never {
[] => {}
_x => {}
}
match slice_never {
//[normal,min_exh_pats]~^ ERROR `&[]` and `&[_, ..]` not covered
//[exhaustive_patterns]~^^ ERROR `&[]` not covered
//[never_pats]~^^^ ERROR `&[]` and `&[!, ..]` not covered
&[..] if false => {}
}
match *slice_never {}
//~^ ERROR non-empty
match *slice_never {
_ => {}
}
let array_3_never: [!; 3] = x.array_3_never;
match array_3_never {}
//[normal,never_pats]~^ ERROR non-empty
match array_3_never {
_ => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
}
match array_3_never {
[_, _, _] => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
}
match array_3_never {
[_, ..] => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
}
let ref_array_3_never: &[!; 3] = &array_3_never;
match ref_array_3_never {
// useful, reachable
&[_, _, _] => {}
}
match ref_array_3_never {
// useful, !reachable
&[_x, _, _] => {}
}
let array_0_never: [!; 0] = [];
match array_0_never {}
//~^ ERROR non-empty
match array_0_never {
[] => {}
}
match array_0_never {
[] => {}
_ => {} //~ ERROR unreachable pattern
}
match array_0_never {
//~^ ERROR `[]` not covered
[..] if false => {}
}
}
// The difference between `_` and `_a` patterns is that `_a` loads the value. In case of an empty
// type, this asserts validity of the value, and thus the binding is unreachable. We don't yet
// distinguish these cases since we don't lint "unreachable" on `useful && !reachable` arms.
// Once/if never patterns are a thing, we can warn that the `_a` cases should be never patterns.
fn bindings(x: NeverBundle) {
let opt_never: Option<!> = None;
let ref_never: &! = &x.never;
let ref_opt_never: &Option<!> = &None;
// On a known_valid place.
match opt_never {
None => {}
// !useful, !reachable
Some(_) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
}
match opt_never {
None => {}
// !useful, !reachable
Some(_a) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
}
match opt_never {
None => {}
// !useful, !reachable
_ => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
}
match opt_never {
None => {}
// !useful, !reachable
_a => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
}
// The scrutinee is known_valid, but under the `&` isn't anymore.
match ref_never {
// useful, reachable
_ => {}
}
match ref_never {
// useful, reachable
&_ => {}
}
match ref_never {
// useful, reachable
_a => {}
}
match ref_never {
// useful, !reachable
&_a => {}
}
match ref_opt_never {
//[normal,min_exh_pats,never_pats]~^ ERROR non-exhaustive
&None => {}
}
match ref_opt_never {
&None => {}
// useful, reachable
_ => {}
}
match ref_opt_never {
&None => {}
// useful, reachable
_a => {}
}
match ref_opt_never {
&None => {}
// useful, reachable
&_ => {}
}
match ref_opt_never {
&None => {}
// useful, !reachable
&_a => {}
}
// On a !known_valid place.
match *ref_never {}
match *ref_never {
// useful, reachable
_ => {}
}
match *ref_never {
// useful, !reachable
_a => {}
}
// This is equivalent to `match ref_never { _a => {} }`. In other words, it asserts validity of
// `ref_never` but says nothing of the data at `*ref_never`.
match *ref_never {
// useful, reachable
ref _a => {}
}
match *ref_opt_never {
//[normal,min_exh_pats,never_pats]~^ ERROR non-exhaustive
None => {}
}
match *ref_opt_never {
None => {}
// useful, reachable
Some(_) => {}
}
match *ref_opt_never {
None => {}
// useful, !reachable
Some(_a) => {}
}
match *ref_opt_never {
None => {}
// useful, reachable
_ => {}
}
match *ref_opt_never {
None => {}
// useful, !reachable
_a => {}
}
match *ref_opt_never {
None => {}
// useful, !reachable
_a @ Some(_) => {}
}
// This is equivalent to `match ref_opt_never { None => {}, _a => {} }`. In other words, it
// asserts validity of `ref_opt_never` but says nothing of the data at `*ref_opt_never`.
match *ref_opt_never {
None => {}
// useful, reachable
ref _a => {}
}
match *ref_opt_never {
None => {}
// useful, reachable
ref _a @ Some(_) => {}
}
match *ref_opt_never {
None => {}
// useful, !reachable
ref _a @ Some(_b) => {}
}
let ref_res_never: &Result<!, !> = &x.result_never;
match *ref_res_never {
//[normal,min_exh_pats,never_pats]~^ ERROR non-exhaustive
// useful, reachable
Ok(_) => {}
}
match *ref_res_never {
// useful, reachable
Ok(_) => {}
// useful, reachable
_ => {}
}
match *ref_res_never {
//[normal,min_exh_pats,never_pats]~^ ERROR non-exhaustive
// useful, !reachable
Ok(_a) => {}
}
match *ref_res_never {
// useful, !reachable
Ok(_a) => {}
// useful, reachable
_ => {}
}
match *ref_res_never {
// useful, !reachable
Ok(_a) => {}
// useful, reachable
Err(_) => {}
}
let ref_tuple_half_never: &(u32, !) = &x.tuple_half_never;
match *ref_tuple_half_never {}
//[normal,min_exh_pats,never_pats]~^ ERROR non-empty
match *ref_tuple_half_never {
// useful, reachable
(_, _) => {}
}
match *ref_tuple_half_never {
// useful, reachable
(_x, _) => {}
}
match *ref_tuple_half_never {
// useful, !reachable
(_, _x) => {}
}
match *ref_tuple_half_never {
// useful, !reachable
(0, _x) => {}
// useful, reachable
(1.., _) => {}
}
}
// When we execute the condition for a guard we loads from all bindings. This asserts validity at
// all places with bindings. Surprisingly this can make subsequent arms unreachable. We choose to
// not detect this in exhaustiveness because this is rather subtle. With never patterns, we would
// recommend using a never pattern instead.
fn guards_and_validity(x: NeverBundle) {
let never: ! = x.never;
let ref_never: &! = &never;
// Basic guard behavior when known_valid.
match never {}
match never {
_ => {} //~ ERROR unreachable pattern
}
match never {
_x => {} //~ ERROR unreachable pattern
}
match never {
_ if false => {} //~ ERROR unreachable pattern
}
match never {
_x if false => {} //~ ERROR unreachable pattern
}
// If the pattern under the guard doesn't load, all is normal.
match *ref_never {
// useful, reachable
_ if false => {}
// useful, reachable
_ => {}
}
// Now the madness commences. The guard caused a load of the value thus asserting validity. So
// there's no invalid value for `_` to catch. So the second pattern is unreachable despite the
// guard not being taken.
match *ref_never {
// useful, !reachable
_a if false => {}
// !useful, !reachable
_ => {}
}
// The above still applies to the implicit `_` pattern used for exhaustiveness.
match *ref_never {
// useful, !reachable
_a if false => {}
}
match ref_never {
//[normal,min_exh_pats,never_pats]~^ ERROR non-exhaustive
// useful, !reachable
&_a if false => {}
}
// Same but with subpatterns.
let ref_result_never: &Result<!, !> = &x.result_never;
match *ref_result_never {
// useful, !reachable
Ok(_x) if false => {}
// !useful, !reachable
Ok(_) => {}
// useful, !reachable
Err(_) => {}
}
match *ref_result_never {
//[normal,min_exh_pats]~^ ERROR `Ok(_)` not covered
//[never_pats]~^^ ERROR `Ok(!)` not covered
// useful, reachable
Ok(_) if false => {}
// useful, reachable
Err(_) => {}
}
let ref_tuple_never: &(!, !) = &x.tuple_never;
match *ref_tuple_never {
// useful, !reachable
(_, _x) if false => {}
// !useful, !reachable
(_, _) => {}
}
}
fn diagnostics_subtlety(x: NeverBundle) {
// Regression test for diagnostics: don't report `Some(Ok(_))` and `Some(Err(_))`.
let x: &Option<Result<!, !>> = &None;
match *x {
//[normal,min_exh_pats]~^ ERROR `Some(_)` not covered
//[never_pats]~^^ ERROR `Some(!)` not covered
None => {}
}
}
fn main() {}