mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-14 02:49:40 +00:00
![]() Stabilize `min_exhaustive_patterns` ## Stabilisation report I propose we stabilize the [`min_exhaustive_patterns`](https://github.com/rust-lang/rust/issues/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`](https://github.com/rust-lang/rust/issues/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 https://github.com/rust-lang/nomicon/pull/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 |
||
---|---|---|
.. | ||
rustc | ||
rustc_abi | ||
rustc_arena | ||
rustc_ast | ||
rustc_ast_ir | ||
rustc_ast_lowering | ||
rustc_ast_passes | ||
rustc_ast_pretty | ||
rustc_attr | ||
rustc_baked_icu_data | ||
rustc_borrowck | ||
rustc_builtin_macros | ||
rustc_codegen_cranelift | ||
rustc_codegen_gcc | ||
rustc_codegen_llvm | ||
rustc_codegen_ssa | ||
rustc_const_eval | ||
rustc_data_structures | ||
rustc_driver | ||
rustc_driver_impl | ||
rustc_error_codes | ||
rustc_error_messages | ||
rustc_errors | ||
rustc_expand | ||
rustc_feature | ||
rustc_fluent_macro | ||
rustc_fs_util | ||
rustc_graphviz | ||
rustc_hir | ||
rustc_hir_analysis | ||
rustc_hir_pretty | ||
rustc_hir_typeck | ||
rustc_incremental | ||
rustc_index | ||
rustc_index_macros | ||
rustc_infer | ||
rustc_interface | ||
rustc_lexer | ||
rustc_lint | ||
rustc_lint_defs | ||
rustc_llvm | ||
rustc_log | ||
rustc_macros | ||
rustc_metadata | ||
rustc_middle | ||
rustc_mir_build | ||
rustc_mir_dataflow | ||
rustc_mir_transform | ||
rustc_monomorphize | ||
rustc_next_trait_solver | ||
rustc_parse | ||
rustc_parse_format | ||
rustc_passes | ||
rustc_pattern_analysis | ||
rustc_privacy | ||
rustc_query_impl | ||
rustc_query_system | ||
rustc_resolve | ||
rustc_sanitizers | ||
rustc_serialize | ||
rustc_session | ||
rustc_smir | ||
rustc_span | ||
rustc_symbol_mangling | ||
rustc_target | ||
rustc_trait_selection | ||
rustc_traits | ||
rustc_transmute | ||
rustc_ty_utils | ||
rustc_type_ir | ||
rustc_type_ir_macros | ||
stable_mir |