Stabilize min_exhaustive_patterns
by Nadrieril · Pull Request #122792 · rust-lang/rust (original) (raw)
Stabilisation report
I propose we stabilize the min_exhaustive_patterns language feature.
With this feature, patterns of empty types are considered unreachable when matched by-value. This allows:
enum Void {} fn foo() -> Result<u32, Void>;
fn main() { let Ok(x) = foo(); // also match foo() { Ok(x) => ..., } }
This is a subset of the long-unstable exhaustive_patterns feature. That feature is blocked because omitting empty patterns is tricky when not matched by-value. This PR stabilizes the by-value case, which is not tricky.
The not-by-value cases (behind references, pointers, and unions) stay as they are today, e.g.
enum Void {} fn foo() -> Result<u32, &Void>;
fn main() {
let Ok(x) = foo(); // ERROR: missing Err(_)
}
The consequence on existing code is some extra "unreachable pattern" warnings. This is fully backwards-compatible.
Comparison with today's rust
This proposal only affects match checking of empty types (i.e. types with no valid values). Non-empty types behave the same with or without this feature. Note that everything below is phrased in terms of match
but applies equallly to if let
and other pattern-matching expressions.
To be precise, a visibly empty type is:
- an enum with no variants;
- the never type
!
; - a struct with a visible field of a visibly empty type (and no #[non_exhaustive] annotation);
- a tuple where one of the types is visibly empty;
- en enum with all variants visibly empty (and no
#[non_exhaustive]
annotation); - a
[T; N]
withN != 0
andT
visibly empty; - all other types are nonempty.
(An extra change was proposed below: that we ignore #[non_exhaustive] for structs since adding fields cannot turn an empty struct into a non-empty one)
For normal types, exhaustiveness checking requires that we list all variants (or use a wildcard). For empty types it's more subtle: in some cases we require a _
pattern even though there are no valid values that can match it. This is where the difference lies regarding this feature.
Today's rust
Under today's rust, a _
is required for all empty types, except specifically: if the matched expression is of type !
(the never type) or EmptyEnum
(where EmptyEnum
is an enum with no variants), then the _
is not required.
let foo: Result<u32, !> = ...; match foo { Ok(x) => ..., Err() => ..., // required } let foo: Result<u32, &!> = ...; match foo { Ok(x) => ..., Err() => ..., // required } let foo: &! = ...; match foo { _ => ..., // required } fn blah(foo: (u32, !)) { match foo { _ => ..., // required } } unsafe { let ptr: *const ! = ...; match *ptr {} // allowed let ptr: *const (u32, !) = ...; match *ptr { (x, ) => { ... } // required } let ptr: *const Result<u32, !> = ...; match *ptr { Ok(x) => { ... } Err() => { ... } // required } }
After this PR
After this PR, a pattern of an empty type can be omitted if (and only if):
- the match scrutinee expression has type
!
orEmptyEnum
(like before); - or the empty type is matched by value (that's the new behavior).
In all other cases, a _
is required to match on an empty type.
let foo: Result<u32, !> = ...;
match foo {
Ok(x) => ..., // Err
not required
}
let foo: Result<u32, &!> = ...;
match foo {
Ok(x) => ...,
Err(_) => ..., // required because !
is under a dereference
}
let foo: &! = ...;
match foo {
_ => ..., // required because !
is under a dereference
}
fn blah(foo: (u32, !)) {
match foo {} // allowed
}
unsafe {
let ptr: *const ! = ...;
match *ptr {} // allowed
let ptr: *const (u32, !) = ...;
match *ptr {
(x, ) => { ... } // required because the matched place is under a (pointer) dereference
}
let ptr: *const Result<u32, !> = ...;
match *ptr {
Ok(x) => { ... }
Err() => { ... } // required because the matched place is under a (pointer) dereference
}
}
Documentation
The reference does not say anything specific about exhaustiveness checking, hence there is nothing to update there. The nomicon does, I opened rust-lang/nomicon#445 to reflect the changes.
Tests
The relevant tests are in tests/ui/pattern/usefulness/empty-types.rs
.
Unresolved Questions
None that I know of.
try-job: dist-aarch64-apple