Skip to content
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

Stabilise exhaustive patterns feature #110105

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/accepted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ declare_features! (
(accepted, dyn_trait, "1.27.0", Some(44662), None),
/// Allows integer match exhaustiveness checking (RFC 2591).
(accepted, exhaustive_integer_patterns, "1.33.0", Some(50907), None),
/// Allows exhaustive pattern matching on types that contain uninhabited types.
(accepted, exhaustive_patterns, "CURRENT_RUSTC_VERSION", Some(51085), None),
/// Allows explicit generic arguments specification with `impl Trait` present.
(accepted, explicit_generic_args_with_impl_trait, "1.63.0", Some(83701), None),
/// Allows arbitrary expressions in key-value attributes at parse time.
Expand Down
2 changes: 0 additions & 2 deletions compiler/rustc_feature/src/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,8 +393,6 @@ declare_features! (
(incomplete, dyn_star, "1.65.0", Some(102425), None),
/// Allows `X..Y` patterns.
(active, exclusive_range_pattern, "1.11.0", Some(37854), None),
/// Allows exhaustive pattern matching on types that contain uninhabited types.
(active, exhaustive_patterns, "1.13.0", Some(51085), None),
/// Allows explicit tail calls via `become` expression.
(incomplete, explicit_tail_calls, "CURRENT_RUSTC_VERSION", Some(112788), None),
/// Allows using `efiapi`, `sysv64` and `win64` as calling convention
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
#![feature(box_patterns)]
#![feature(core_intrinsics)]
#![feature(discriminant_kind)]
#![feature(exhaustive_patterns)]
#![cfg_attr(bootstrap, feature(exhaustive_patterns))]
#![feature(generators)]
#![feature(get_mut_unchecked)]
#![feature(if_let_guard)]
Expand Down
8 changes: 3 additions & 5 deletions compiler/rustc_mir_build/src/build/matches/simplify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,11 +262,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
PatKind::Variant { adt_def, substs, variant_index, ref subpatterns } => {
let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
i == variant_index || {
self.tcx.features().exhaustive_patterns
&& !v
.inhabited_predicate(self.tcx, adt_def)
.subst(self.tcx, substs)
.apply_ignore_module(self.tcx, self.param_env)
!v.inhabited_predicate(self.tcx, adt_def)
.subst(self.tcx, substs)
.apply_ignore_module(self.tcx, self.param_env)
}
}) && (adt_def.did().is_local()
|| !adt_def.is_variant_list_non_exhaustive());
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_mir_build/src/thir/pattern/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,8 +499,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
// Emit an extra note if the first uncovered witness would be uninhabited
// if we disregard visibility.
let witness_1_is_privately_uninhabited =
if cx.tcx.features().exhaustive_patterns
&& let Some(witness_1) = witnesses.get(0)
if let Some(witness_1) = witnesses.get(0)
&& let ty::Adt(adt, substs) = witness_1.ty().kind()
&& adt.is_enum()
&& let Constructor::Variant(variant_index) = witness_1.ctor()
Expand Down
36 changes: 9 additions & 27 deletions compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -902,9 +902,8 @@ impl<'tcx> SplitWildcard<'tcx> {
// This determines the set of all possible constructors for the type `pcx.ty`. For numbers,
// arrays and slices we use ranges and variable-length slices when appropriate.
//
// If the `exhaustive_patterns` feature is enabled, we make sure to omit constructors that
// are statically impossible. E.g., for `Option<!>`, we do not include `Some(_)` in the
// returned list of constructors.
// We omit constructors that are statically impossible. E.g., for `Option<!>`, we do not
// include `Some(_)` in the returned list of constructors.
// Invariant: this is empty if and only if the type is uninhabited (as determined by
// `cx.is_uninhabited()`).
let all_ctors = match pcx.ty.kind() {
Expand Down Expand Up @@ -941,32 +940,21 @@ impl<'tcx> SplitWildcard<'tcx> {
// witness.
let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(pcx.ty);

let is_exhaustive_pat_feature = cx.tcx.features().exhaustive_patterns;

// If `exhaustive_patterns` is disabled and our scrutinee is an empty enum, we treat it
// as though it had an "unknown" constructor to avoid exposing its emptiness. The
// exception is if the pattern is at the top level, because we want empty matches to be
// considered exhaustive.
let is_secretly_empty =
def.variants().is_empty() && !is_exhaustive_pat_feature && !pcx.is_top_level;

let mut ctors: SmallVec<[_; 1]> = def
.variants()
.iter_enumerated()
.filter(|(_, v)| {
// If `exhaustive_patterns` is enabled, we exclude variants known to be
// uninhabited.
!is_exhaustive_pat_feature
|| v.inhabited_predicate(cx.tcx, *def).subst(cx.tcx, substs).apply(
cx.tcx,
cx.param_env,
cx.module,
)
// exclude variants known to be uninhabited.
v.inhabited_predicate(cx.tcx, *def).subst(cx.tcx, substs).apply(
cx.tcx,
cx.param_env,
cx.module,
)
})
.map(|(idx, _)| Variant(idx))
.collect();

if is_secretly_empty || is_declared_nonexhaustive {
if is_declared_nonexhaustive {
ctors.push(NonExhaustive);
}
ctors
Expand Down Expand Up @@ -998,12 +986,6 @@ impl<'tcx> SplitWildcard<'tcx> {
let max = size.truncate(u128::MAX);
smallvec![make_range(0, max)]
}
// If `exhaustive_patterns` is disabled and our scrutinee is the never type, we cannot
// expose its emptiness. The exception is if the pattern is at the top level, because we
// want empty matches to be considered exhaustive.
ty::Never if !cx.tcx.features().exhaustive_patterns && !pcx.is_top_level => {
smallvec![NonExhaustive]
}
ty::Never => smallvec![],
_ if cx.is_uninhabited(pcx.ty) => smallvec![],
ty::Adt(..) | ty::Tuple(..) | ty::Ref(..) => smallvec![Single],
Expand Down
6 changes: 1 addition & 5 deletions compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,11 +340,7 @@ pub(crate) struct MatchCheckCtxt<'p, 'tcx> {

impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
pub(super) fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool {
if self.tcx.features().exhaustive_patterns {
!ty.is_inhabited_from(self.tcx, self.module, self.param_env)
} else {
false
}
!ty.is_inhabited_from(self.tcx, self.module, self.param_env)
}

/// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_target/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(assert_matches)]
#![feature(associated_type_bounds)]
#![feature(exhaustive_patterns)]
#![cfg_attr(bootstrap, feature(exhaustive_patterns))]
#![feature(iter_intersperse)]
#![feature(min_specialization)]
#![feature(never_type)]
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_traits/src/chalk/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,7 @@ impl<'tcx> LowerInto<'tcx, Region<'tcx>> for &chalk_ir::Lifetime<RustInterner<'t
),
chalk_ir::LifetimeData::Static => tcx.lifetimes.re_static,
chalk_ir::LifetimeData::Erased => tcx.lifetimes.re_erased,
#[cfg(bootstrap)]
chalk_ir::LifetimeData::Phantom(void, _) => match *void {},
}
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_transmute/src/layout/nfa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ where
pub(crate) fn from_tree(tree: Tree<!, R>) -> Result<Self, Uninhabited> {
Ok(match tree {
Tree::Byte(b) => Self::from_byte(b),
#[cfg(bootstrap)]
Tree::Def(..) => unreachable!(),
Tree::Ref(r) => Self::from_ref(r),
Tree::Alt(alts) => {
Expand Down
2 changes: 2 additions & 0 deletions library/alloc/src/collections/vec_deque/into_iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
{
match self.try_fold(init, |b, item| Ok::<B, !>(f(b, item))) {
Ok(b) => b,
#[cfg(bootstrap)]
Err(e) => match e {},
}
}
Expand Down Expand Up @@ -241,6 +242,7 @@ impl<T, A: Allocator> DoubleEndedIterator for IntoIter<T, A> {
{
match self.try_rfold(init, |b, item| Ok::<B, !>(f(b, item))) {
Ok(b) => b,
#[cfg(bootstrap)]
Err(e) => match e {},
}
}
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@
//
// Language features:
// tidy-alphabetical-start
#![cfg_attr(bootstrap, feature(exhaustive_patterns))]
#![feature(abi_unadjusted)]
#![feature(adt_const_params)]
#![feature(allow_internal_unsafe)]
Expand All @@ -206,7 +207,6 @@
#![feature(doc_cfg)]
#![feature(doc_cfg_hide)]
#![feature(doc_notable_trait)]
#![feature(exhaustive_patterns)]
#![feature(extern_types)]
#![feature(fundamental)]
#![feature(generic_arg_infer)]
Expand Down
9 changes: 4 additions & 5 deletions library/core/src/primitive_docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,15 +130,14 @@ mod prim_bool {}
/// [`Result<String, !>`] which we can unpack like this:
///
/// ```
/// #![feature(exhaustive_patterns)]
/// # #![cfg_attr(bootstrap, feature(exhaustive_patterns))]
/// use std::str::FromStr;
/// let Ok(s) = String::from_str("hello");
/// ```
///
/// Since the [`Err`] variant contains a `!`, it can never occur. If the `exhaustive_patterns`
/// feature is present this means we can exhaustively match on [`Result<T, !>`] by just taking the
/// [`Ok`] variant. This illustrates another behaviour of `!` - it can be used to "delete" certain
/// enum variants from generic types like `Result`.
/// Since the [`Err`] variant contains a `!`, it can never occur. This means we can exhaustively
/// match on [`Result<T, !>`] by just taking the [`Ok`] variant. This illustrates another behaviour
/// of `!` - it can be used to "delete" certain enum variants from generic types like `Result`.
///
/// ## Infinite loops
///
Expand Down
2 changes: 1 addition & 1 deletion library/std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@
//
// Language features:
// tidy-alphabetical-start
#![cfg_attr(bootstrap, feature(exhaustive_patterns))]
#![feature(alloc_error_handler)]
#![feature(allocator_internals)]
#![feature(allow_internal_unsafe)]
Expand All @@ -254,7 +255,6 @@
#![feature(doc_masked)]
#![feature(doc_notable_trait)]
#![feature(dropck_eyepatch)]
#![feature(exhaustive_patterns)]
#![feature(if_let_guard)]
#![feature(intra_doc_pointers)]
#![feature(lang_items)]
Expand Down
9 changes: 4 additions & 5 deletions library/std/src/primitive_docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,15 +130,14 @@ mod prim_bool {}
/// [`Result<String, !>`] which we can unpack like this:
///
/// ```
/// #![feature(exhaustive_patterns)]
/// # #![cfg_attr(bootstrap, feature(exhaustive_patterns))]
/// use std::str::FromStr;
/// let Ok(s) = String::from_str("hello");
/// ```
///
/// Since the [`Err`] variant contains a `!`, it can never occur. If the `exhaustive_patterns`
/// feature is present this means we can exhaustively match on [`Result<T, !>`] by just taking the
/// [`Ok`] variant. This illustrates another behaviour of `!` - it can be used to "delete" certain
/// enum variants from generic types like `Result`.
/// Since the [`Err`] variant contains a `!`, it can never occur. This means we can exhaustively
/// match on [`Result<T, !>`] by just taking the [`Ok`] variant. This illustrates another behaviour
/// of `!` - it can be used to "delete" certain enum variants from generic types like `Result`.
///
/// ## Infinite loops
///
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//@run-rustfix
#![feature(exhaustive_patterns, never_type)]
#![feature(never_type)]
#![allow(dead_code, unreachable_code, unused_variables)]
#![allow(clippy::let_and_return)]

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//@run-rustfix
#![feature(exhaustive_patterns, never_type)]
#![feature(never_type)]
#![allow(dead_code, unreachable_code, unused_variables)]
#![allow(clippy::let_and_return)]

Expand Down
1 change: 1 addition & 0 deletions src/tools/clippy/tests/ui/single_match_else.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//@aux-build: proc_macros.rs
#![warn(clippy::single_match_else)]
#![allow(unused, clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)]
#![allow(irrefutable_let_patterns)] // The fixed code may introduce some cases.
extern crate proc_macros;
use proc_macros::with_span;

Expand Down
1 change: 1 addition & 0 deletions src/tools/clippy/tests/ui/single_match_else.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//@aux-build: proc_macros.rs
#![warn(clippy::single_match_else)]
#![allow(unused, clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)]
#![allow(irrefutable_let_patterns)] // The fixed code may introduce some cases.
extern crate proc_macros;
use proc_macros::with_span;

Expand Down
18 changes: 9 additions & 9 deletions src/tools/clippy/tests/ui/single_match_else.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
--> $DIR/single_match_else.rs:17:13
--> $DIR/single_match_else.rs:18:13
|
LL | let _ = match ExprNode::Butterflies {
| _____________^
Expand All @@ -21,7 +21,7 @@ LL ~ };
|

error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
--> $DIR/single_match_else.rs:82:5
--> $DIR/single_match_else.rs:83:5
|
LL | / match Some(1) {
LL | | Some(a) => println!("${:?}", a),
Expand All @@ -41,7 +41,7 @@ LL + }
|

error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
--> $DIR/single_match_else.rs:91:5
--> $DIR/single_match_else.rs:92:5
|
LL | / match Some(1) {
LL | | Some(a) => println!("${:?}", a),
Expand All @@ -61,7 +61,7 @@ LL + }
|

error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
--> $DIR/single_match_else.rs:101:5
--> $DIR/single_match_else.rs:102:5
|
LL | / match Result::<i32, Infallible>::Ok(1) {
LL | | Ok(a) => println!("${:?}", a),
Expand All @@ -81,7 +81,7 @@ LL + }
|

error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
--> $DIR/single_match_else.rs:110:5
--> $DIR/single_match_else.rs:111:5
|
LL | / match Cow::from("moo") {
LL | | Cow::Owned(a) => println!("${:?}", a),
Expand All @@ -101,7 +101,7 @@ LL + }
|

error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
--> $DIR/single_match_else.rs:120:5
--> $DIR/single_match_else.rs:121:5
|
LL | / match bar {
LL | | Some(v) => unsafe {
Expand All @@ -124,7 +124,7 @@ LL + }
|

error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
--> $DIR/single_match_else.rs:131:5
--> $DIR/single_match_else.rs:132:5
|
LL | / match bar {
LL | | Some(v) => {
Expand All @@ -148,7 +148,7 @@ LL + } }
|

error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
--> $DIR/single_match_else.rs:143:5
--> $DIR/single_match_else.rs:144:5
|
LL | / match bar {
LL | | Some(v) => unsafe {
Expand All @@ -172,7 +172,7 @@ LL + } }
|

error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
--> $DIR/single_match_else.rs:155:5
--> $DIR/single_match_else.rs:156:5
|
LL | / match bar {
LL | | #[rustfmt::skip]
Expand Down
1 change: 1 addition & 0 deletions src/tools/miri/src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,7 @@ pub fn eval_entry<'tcx>(
let res = match res {
Err(res) => res,
// `Ok` can never happen
#[cfg(bootstrap)]
Ok(never) => match never {},
};

Expand Down
2 changes: 2 additions & 0 deletions src/tools/miri/src/shims/unix/dlsym.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
match dlsym {
Dlsym::Android(dlsym) =>
android::EvalContextExt::call_dlsym(this, dlsym, args, dest, ret),
#[cfg(bootstrap)]
Dlsym::FreeBsd(dlsym) =>
freebsd::EvalContextExt::call_dlsym(this, dlsym, args, dest, ret),
#[cfg(bootstrap)]
Dlsym::Linux(dlsym) => linux::EvalContextExt::call_dlsym(this, dlsym, args, dest, ret),
Dlsym::MacOs(dlsym) => macos::EvalContextExt::call_dlsym(this, dlsym, args, dest, ret),
}
Expand Down
1 change: 1 addition & 0 deletions src/tools/miri/tests/pass/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ fn more_discriminant_overflow() {
V4,
}

#[allow(unreachable_patterns)]
if let E1::V2 { .. } = (E1::V1 { f: true }) {
unreachable!()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@
//! or-patterns; instead we just try the alternatives one-by-one. For details on splitting
//! wildcards, see [`SplitWildcard`]; for integer ranges, see [`SplitIntRange`].

// This uses uninhabited types to encode statically unused variants.
#![allow(unreachable_patterns)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drive by comment, but the fact that changing rustc requires atomically changing rust-analyzer doesn't feel right to me. Ideally, this change is done by ra devs in ra repo when ra upgrades to the new rust version.

Perhaps rust-lang/rust build system should build rust-analyzer with --cap-lints allow? From rustc point of view, rust-analyzer pretty much is a dependency which might not have upgraded to the latest compiler yet.


use std::{
cell::Cell,
cmp::{max, min},
Expand Down
1 change: 1 addition & 0 deletions src/tools/rust-analyzer/crates/hir-ty/src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1620,6 +1620,7 @@ impl HirDisplay for LifetimeData {
}
LifetimeData::Static => write!(f, "'static"),
LifetimeData::Erased => Ok(()),
#[allow(unreachable_patterns)]
LifetimeData::Phantom(_, _) => Ok(()),
}
}
Expand Down
Loading
Loading