-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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 RFC 1872: exhaustive_patterns
feature
#51085
Comments
…lexcrichton Ensure every unstable language feature has a tracking issue. Filled in the missing numbers: * `abi_ptx` → #38788 * `generators` → #43122 * `global_allocator` → #27389 Reused existing tracking issues because they were decomposed from a larger feature * `*_target_feature` → #44839 (reusing the old `target_feature` number) * `proc_macros_*` → #38356 (reusing the to-be-stabilized `proc_macros` number) Filed new issues * `exhaustive_patterns` → #51085 * `pattern_parentheses` → #51087 * `wasm_custom_section` and `wasm_import_module` → #51088
The lack of this feature has unintuitive consequences (e.g. #55123). I haven't seen any problems arising from it either. cc @rust-lang/lang: could this be put forward for stabilisation? |
@varkor I am mildly reluctant until we have agreed upon the story around "never patterns" -- but I suppose given a sufficiently conservative definition of "uninhabited pattern" I'd be all right with it. Do you think you could write-up a "stabilization report" that includes examples of the tests we have and also documents the limits of what sorts of things would be considered uninhabited in stable code? (That said, I think "never patterns" gives us a path forward that addresses my concerns around aggressive uninhabited definitions, by just turning the "ambiguous" cases into lints in the presence of unsafe code. I'd be happier though if we had an RFC on the topic.) |
The I would expect that this issue will resolve itself if there is a consensus to go with references never being uninhabited (ref #54125 (comment)), as the reference handling is what I believe makes this expensive (not looking through references both reduces the amount of types we need to look at, and allows us to drop the expensive hashmap and nested hashsets used to avoid unbounded recursion). If the consensus goes in the other direction, then this code should probably be optimized a bit prior to stabilization of the feature. |
cc #51221, using this feature in |
It is now the case that "references never are uninhabited", at least as far as |
I suspect this requires some investigation. (Perhaps it would be sufficient to open a PR enabling the feature and do a perf run.) |
…mpiler-errors Exhaustiveness: Reveal empty opaques in depth Follow-up to rust-lang#116821. As noted [there](rust-lang#116821 (comment)), the current implementation doesn't detect emptiness of opaques when the opaque is nested inside a type. This doesn't matter for stable behavior (which ignores nested empty types anyway) but does matter for the [`exhaustive_patterns`](https://github.com/rust-lang/rust/issues/51085)/[`min_exhaustive_patterns`](https://github.com/rust-lang/rust/pull/118803) features. This PR fixes this behavior by adding `InhabitedPredicate::apply_reveal_opaque` that considers opaque types when determining inhabitedness. r? `@compiler-errors`
I'm proposing a (hopefully uncontroversial) subset of this feature under the |
… r=compiler-errors Add the `min_exhaustive_patterns` feature gate ## Motivation 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`](rust-lang#51085) feature allows the omission of all empty arms, but hasn't been stabilized because that was deemed dangerous around unsafe code. ## Proposal This feature aims to stabilize an uncontroversial subset of exhaustive_patterns. Namely: when `min_exhaustive_patterns` is enabled and the data we're matching on is guaranteed to be valid by rust's operational semantics, then we allow empty arms to be omitted. E.g.: ```rust let x: Result<T, !> = foo(); match x { // ok Ok(y) => ..., } let Ok(y) = x; // ok ``` If the place is not guaranteed to hold valid data (namely ptr dereferences, ref dereferences (conservatively) and union field accesses), then we keep stable behavior i.e. we (usually) require arms for the empty cases. ```rust unsafe { let ptr: *const Result<u32, !> = ...; match *ptr { Ok(x) => { ... } Err(_) => { ... } // still required } } let foo: Result<u32, &!> = ...; match foo { Ok(x) => { ... } Err(&_) => { ... } // still required because of the dereference } unsafe { let ptr: *const ! = ...; match *ptr {} // already allowed on stable } ``` Note that we conservatively consider that a valid reference can point to invalid data, hence we don't allow arms of type `&!` and similar cases to be omitted. This could eventually change depending on [opsem decisions](rust-lang/unsafe-code-guidelines#413). Whenever opsem is undecided on a case, we conservatively keep today's stable behavior. I proposed this behavior in the [`never_patterns`](rust-lang#118155) feature gate but it makes sense on its own and could be stabilized more quickly. The two proposals nicely complement each other. ## Unresolved Questions Part of the question is whether this requires an RFC. I'd argue this doesn't need one since there is no design question beyond the intent to omit unreachable patterns, but I'm aware the problem can be framed in ways that require design (I'm thinking of the [original never patterns proposal](https://smallcultfollowing.com/babysteps/blog/2018/08/13/never-patterns-exhaustive-matching-and-uninhabited-types-oh-my/), which would frame this behavior as "auto-nevering" happening). EDIT: I initially proposed a future-compatibility lint as part of this feature, I don't anymore.
… r=compiler-errors Add the `min_exhaustive_patterns` feature gate ## Motivation 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`](rust-lang#51085) feature allows the omission of all empty arms, but hasn't been stabilized because that was deemed dangerous around unsafe code. ## Proposal This feature aims to stabilize an uncontroversial subset of exhaustive_patterns. Namely: when `min_exhaustive_patterns` is enabled and the data we're matching on is guaranteed to be valid by rust's operational semantics, then we allow empty arms to be omitted. E.g.: ```rust let x: Result<T, !> = foo(); match x { // ok Ok(y) => ..., } let Ok(y) = x; // ok ``` If the place is not guaranteed to hold valid data (namely ptr dereferences, ref dereferences (conservatively) and union field accesses), then we keep stable behavior i.e. we (usually) require arms for the empty cases. ```rust unsafe { let ptr: *const Result<u32, !> = ...; match *ptr { Ok(x) => { ... } Err(_) => { ... } // still required } } let foo: Result<u32, &!> = ...; match foo { Ok(x) => { ... } Err(&_) => { ... } // still required because of the dereference } unsafe { let ptr: *const ! = ...; match *ptr {} // already allowed on stable } ``` Note that we conservatively consider that a valid reference can point to invalid data, hence we don't allow arms of type `&!` and similar cases to be omitted. This could eventually change depending on [opsem decisions](rust-lang/unsafe-code-guidelines#413). Whenever opsem is undecided on a case, we conservatively keep today's stable behavior. I proposed this behavior in the [`never_patterns`](rust-lang#118155) feature gate but it makes sense on its own and could be stabilized more quickly. The two proposals nicely complement each other. ## Unresolved Questions Part of the question is whether this requires an RFC. I'd argue this doesn't need one since there is no design question beyond the intent to omit unreachable patterns, but I'm aware the problem can be framed in ways that require design (I'm thinking of the [original never patterns proposal](https://smallcultfollowing.com/babysteps/blog/2018/08/13/never-patterns-exhaustive-matching-and-uninhabited-types-oh-my/), which would frame this behavior as "auto-nevering" happening). EDIT: I initially proposed a future-compatibility lint as part of this feature, I don't anymore.
… r=compiler-errors Add the `min_exhaustive_patterns` feature gate ## Motivation 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`](rust-lang#51085) feature allows the omission of all empty arms, but hasn't been stabilized because that was deemed dangerous around unsafe code. ## Proposal This feature aims to stabilize an uncontroversial subset of exhaustive_patterns. Namely: when `min_exhaustive_patterns` is enabled and the data we're matching on is guaranteed to be valid by rust's operational semantics, then we allow empty arms to be omitted. E.g.: ```rust let x: Result<T, !> = foo(); match x { // ok Ok(y) => ..., } let Ok(y) = x; // ok ``` If the place is not guaranteed to hold valid data (namely ptr dereferences, ref dereferences (conservatively) and union field accesses), then we keep stable behavior i.e. we (usually) require arms for the empty cases. ```rust unsafe { let ptr: *const Result<u32, !> = ...; match *ptr { Ok(x) => { ... } Err(_) => { ... } // still required } } let foo: Result<u32, &!> = ...; match foo { Ok(x) => { ... } Err(&_) => { ... } // still required because of the dereference } unsafe { let ptr: *const ! = ...; match *ptr {} // already allowed on stable } ``` Note that we conservatively consider that a valid reference can point to invalid data, hence we don't allow arms of type `&!` and similar cases to be omitted. This could eventually change depending on [opsem decisions](rust-lang/unsafe-code-guidelines#413). Whenever opsem is undecided on a case, we conservatively keep today's stable behavior. I proposed this behavior in the [`never_patterns`](rust-lang#118155) feature gate but it makes sense on its own and could be stabilized more quickly. The two proposals nicely complement each other. ## Unresolved Questions Part of the question is whether this requires an RFC. I'd argue this doesn't need one since there is no design question beyond the intent to omit unreachable patterns, but I'm aware the problem can be framed in ways that require design (I'm thinking of the [original never patterns proposal](https://smallcultfollowing.com/babysteps/blog/2018/08/13/never-patterns-exhaustive-matching-and-uninhabited-types-oh-my/), which would frame this behavior as "auto-nevering" happening). EDIT: I initially proposed a future-compatibility lint as part of this feature, I don't anymore.
Rollup merge of rust-lang#118803 - Nadrieril:min-exhaustive-patterns, r=compiler-errors Add the `min_exhaustive_patterns` feature gate ## Motivation 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`](rust-lang#51085) feature allows the omission of all empty arms, but hasn't been stabilized because that was deemed dangerous around unsafe code. ## Proposal This feature aims to stabilize an uncontroversial subset of exhaustive_patterns. Namely: when `min_exhaustive_patterns` is enabled and the data we're matching on is guaranteed to be valid by rust's operational semantics, then we allow empty arms to be omitted. E.g.: ```rust let x: Result<T, !> = foo(); match x { // ok Ok(y) => ..., } let Ok(y) = x; // ok ``` If the place is not guaranteed to hold valid data (namely ptr dereferences, ref dereferences (conservatively) and union field accesses), then we keep stable behavior i.e. we (usually) require arms for the empty cases. ```rust unsafe { let ptr: *const Result<u32, !> = ...; match *ptr { Ok(x) => { ... } Err(_) => { ... } // still required } } let foo: Result<u32, &!> = ...; match foo { Ok(x) => { ... } Err(&_) => { ... } // still required because of the dereference } unsafe { let ptr: *const ! = ...; match *ptr {} // already allowed on stable } ``` Note that we conservatively consider that a valid reference can point to invalid data, hence we don't allow arms of type `&!` and similar cases to be omitted. This could eventually change depending on [opsem decisions](rust-lang/unsafe-code-guidelines#413). Whenever opsem is undecided on a case, we conservatively keep today's stable behavior. I proposed this behavior in the [`never_patterns`](rust-lang#118155) feature gate but it makes sense on its own and could be stabilized more quickly. The two proposals nicely complement each other. ## Unresolved Questions Part of the question is whether this requires an RFC. I'd argue this doesn't need one since there is no design question beyond the intent to omit unreachable patterns, but I'm aware the problem can be framed in ways that require design (I'm thinking of the [original never patterns proposal](https://smallcultfollowing.com/babysteps/blog/2018/08/13/never-patterns-exhaustive-matching-and-uninhabited-types-oh-my/), which would frame this behavior as "auto-nevering" happening). EDIT: I initially proposed a future-compatibility lint as part of this feature, I don't anymore.
Call for TestingHi all! The
Testing the featureTo test the feature, enable enum Void {}
fn foo() -> Result<String, Void> { ... }
fn bar() {
let Ok(s) = foo();
// or
match foo() {
Ok(s) => ...,
}
} If you already use FeedbackIf you find a bug or the compiler crashes, please file an issue and tag me. If you the feature is not doing what you think it should be doing, have a look at the feature description first, and file an issue if you found a case that doesn't match this description. |
…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'm proposing to stabilize the |
… 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
|
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
Is this code supposed to work in the future? struct A(Box<A>);
fn f(x: A) -> ! {
match x {}
// Or this:
match x {
!,
}
}
|
No, I am not aware of any plans trying to reason about cyclic types. |
This tracks the
exhaustive_patterns
feature which allows uninhabited variant to be omitted(bug report: #12609; relevant RFC: rust-lang/rfcs#1872).
never_type
in Stabilise feature(never_type). Introduce feature(exhaustive_patterns) #47630)The text was updated successfully, but these errors were encountered: