-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tracking Issue for min_exhaustive_patterns
#119612
Comments
Hi T-lang! I would like to move forward with this experiment. Summary of the featureThis feature allows users to omit match arms on empty types when the empty type is matched by-value. This is a guaranteed-sound subset of the enum Void {}
let x: Result<T, Void> = foo();
match x { // ok
Ok(y) => ...,
}
let Ok(y) = x; // ok If Because it's a well-behaved and useful subset, I'd like to move to stabilize it soon. We can think about the rest of ProposalIf T-lang approves, I will:
|
For the triage meeting I prepared step 1 and 2 here: #120742 It appears that the vast majority of in-tree use cases of |
@rfcbot fcp merge This covers the majority of cases I've encountered myself. I'd be very excited to see this subset move forward. Let's do it! |
Team member @nikomatsakis has proposed to merge this. The next step is review by the rest of the tagged team members: No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! cc @rust-lang/lang-advisors: FCP proposed for lang, please feel free to register concerns. |
This sounds great. By-value-only covers the two places that I immediately think of for this:
So I'd be quite happy to start with that and worry about the rest afterwards. @rfcbot reviewed |
Sorry, it's too late for me to be looking at things, apparently, if I can't even hit the right button 🤦 Restarting the FCP from #119612 (comment) |
Team member @scottmcm has proposed to merge this. The next step is review by the rest of the tagged team members: No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! cc @rust-lang/lang-advisors: FCP proposed for lang, please feel free to register concerns. |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
How do we treat a |
Good point: for both |
@rustbot labels -I-lang-nominated This is going into FCP, so we can unnominate. Thanks to @Nadrieril for pushing this forward. |
…ttmcm Use `min_exhaustive_patterns` in core & std [`min_exhaustive_patterns`](rust-lang#119612) provides a subset of the functionality of [`exhaustive_patterns`](rust-lang#51085) which is likely to be stabilized much earlier than the full feature. The subset covers all the compiler and std use cases. `compiler/` [already uses it](rust-lang@9dd6eda); this PR switches `std` over.
…ttmcm Use `min_exhaustive_patterns` in core & std [`min_exhaustive_patterns`](rust-lang#119612) provides a subset of the functionality of [`exhaustive_patterns`](rust-lang#51085) which is likely to be stabilized much earlier than the full feature. The subset covers all the compiler and std use cases. `compiler/` [already uses it](rust-lang@9dd6eda); this PR switches `std` over.
Rollup merge of rust-lang#122255 - Nadrieril:min_exh_pats-libs, r=scottmcm Use `min_exhaustive_patterns` in core & std [`min_exhaustive_patterns`](rust-lang#119612) provides a subset of the functionality of [`exhaustive_patterns`](rust-lang#51085) which is likely to be stabilized much earlier than the full feature. The subset covers all the compiler and std use cases. `compiler/` [already uses it](rust-lang@9dd6eda); this PR switches `std` over.
I've added this to this week's Call for Testing section in This Week in Rust Edit: looks like I don't have permission to remove the |
Thank you! I've removed the label for you. |
Stabilization PR: #122792 |
… r=fee1-dead Stabilize `min_exhaustive_patterns` ## Stabilisation report I propose we stabilize the [`min_exhaustive_patterns`](rust-lang#119612) language feature. With this feature, patterns of empty types are considered unreachable when matched by-value. This allows: ```rust 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`](rust-lang#51085) 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. ```rust 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]` with `N != 0` and `T` 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. ```rust 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 `!` or `EmptyEnum` (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. ```rust 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> Stabilize `min_exhaustive_patterns` ## Stabilisation report I propose we stabilize the [`min_exhaustive_patterns`](rust-lang#119612) language feature. With this feature, patterns of empty types are considered unreachable when matched by-value. This allows: ```rust 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`](rust-lang#51085) 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. ```rust 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]` with `N != 0` and `T` 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. ```rust 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 `!` or `EmptyEnum` (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. ```rust 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.
…=fee1-dead Stabilize `min_exhaustive_patterns` ## Stabilisation report I propose we stabilize the [`min_exhaustive_patterns`](rust-lang#119612) language feature. With this feature, patterns of empty types are considered unreachable when matched by-value. This allows: ```rust 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`](rust-lang#51085) 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. ```rust 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]` with `N != 0` and `T` 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. ```rust 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 `!` or `EmptyEnum` (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. ```rust 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> Stabilize `min_exhaustive_patterns` ## Stabilisation report I propose we stabilize the [`min_exhaustive_patterns`](rust-lang#119612) language feature. With this feature, patterns of empty types are considered unreachable when matched by-value. This allows: ```rust 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`](rust-lang#51085) 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. ```rust 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]` with `N != 0` and `T` 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. ```rust 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 `!` or `EmptyEnum` (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. ```rust 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
…=fee1-dead Stabilize `min_exhaustive_patterns` ## Stabilisation report I propose we stabilize the [`min_exhaustive_patterns`](rust-lang#119612) language feature. With this feature, patterns of empty types are considered unreachable when matched by-value. This allows: ```rust 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`](rust-lang#51085) 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. ```rust 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]` with `N != 0` and `T` 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. ```rust 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 `!` or `EmptyEnum` (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. ```rust 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
This is now stabilized! |
Stabilize `min_exhaustive_patterns` ## Stabilisation report I propose we stabilize the [`min_exhaustive_patterns`](rust-lang/rust#119612) language feature. With this feature, patterns of empty types are considered unreachable when matched by-value. This allows: ```rust 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`](rust-lang/rust#51085) 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. ```rust 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]` with `N != 0` and `T` 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. ```rust 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 `!` or `EmptyEnum` (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. ```rust 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
Stabilize `min_exhaustive_patterns` ## Stabilisation report I propose we stabilize the [`min_exhaustive_patterns`](rust-lang/rust#119612) language feature. With this feature, patterns of empty types are considered unreachable when matched by-value. This allows: ```rust 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`](rust-lang/rust#51085) 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. ```rust 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]` with `N != 0` and `T` 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. ```rust 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 `!` or `EmptyEnum` (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. ```rust 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
This is a tracking issue for the experimental "minimal exhaustive patterns" feature (as per the T-lang experimental feature process).
The feature gate for the issue is
#![feature(min_exhaustive_patterns)]
.Proposal
Pattern-matching on empty types is tricky around unsafe code. For that reason, current stable rust conservatively requires arms for empty types in all but the simplest case. It has long been the intention to allow omitting empty arms when it's safe to do so. The
exhaustive_patterns
feature allows the omission of all empty arms, but hasn't been stabilized because that was deemed dangerous around unsafe code.This feature aims to stabilize an uncontroversial subset of
exhaustive_patterns
. Namely: when we're matching on data by-value, then we allow empty arms to be omitted. E.g.:In other cases (behind references, pointers or unions) we keep stable behavior i.e. we require arms for the empty cases. This is because these cases may be reachable without UB (either now or in the future, until T-opsem decides otherwise).
Note:
never_patterns
will make these remaining cases more convenient to handle. The two features nicely complement each other.Comparison with stable 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 toif let
and other pattern-matching expressions.To be precise, an empty type is:
!
;#[non_exhaustive]
annotation);#[non_exhaustive]
annotation).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 regardingmin_exhaustive_patterns
.Stable rust
Under stable rust, a
_
is required for all empty types, except specifically: if the matched expression is of type!
(the never type) orEmptyEnum
(whereEmptyEnum
is an enum with no variants), then the_
is not required.min_exhaustive_patterns
With
min_exhaustive_patterns
, a pattern of an empty type can be omitted if (and only if):!
orEmptyEnum
;In all other cases, a
_
is required to match on an empty type.About tracking issues
Tracking issues are used to record the overall progress of implementation.
They are also used as hubs connecting to other relevant issues, e.g., bugs or open design questions.
A tracking issue is however not meant for large scale discussion, questions, or bug reports about a feature.
Instead, open a dedicated issue for the specific matter and add the relevant feature gate label.
Steps
Unresolved Questions
Implementation history
min_exhaustive_patterns
feature gate #118803min_exhaustive_patterns
matchexhaustive_patterns
better #120775min_exhaustive_patterns
as complete #120742min_exhaustive_patterns
in core & std #122255min_exhaustive_patterns
#122792The text was updated successfully, but these errors were encountered: