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

Stabilize min_exhaustive_patterns #122792

Merged
merged 5 commits into from
Aug 10, 2024
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,7 @@ pub enum E2<X> {
V4,
}

#[allow(unreachable_patterns)]
fn check_niche_behavior() {
if let E1::V2 { .. } = (E1::V1 { f: true }) {
intrinsics::abort();
Copy link
Member

Choose a reason for hiding this comment

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

Does the MIR for this function keep both matches? If not, this wouldn't test what it is intended to test anymore.

Copy link
Member Author

@Nadrieril Nadrieril Jul 27, 2024

Choose a reason for hiding this comment

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

Yes: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=b88ecbaaeea020ab1055a977170749a0. MIR lowering is only affected in the case of an enum with one non-empty variant and a bunch of empty variants.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@ pub enum E2<X> {
V4,
}

#[allow(unreachable_patterns)]
fn check_niche_behavior () {
if let E1::V2 { .. } = (E1::V1 { f: true }) {
intrinsics::abort();
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_errors/src/diagnostic_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ macro_rules! into_diag_arg_for_number {
impl IntoDiagArg for $ty {
fn into_diag_arg(self) -> DiagArgValue {
// Convert to a string if it won't fit into `Number`.
#[allow(irrefutable_let_patterns)]
if let Ok(n) = TryInto::<i32>::try_into(self) {
DiagArgValue::Number(n)
} else {
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/accepted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,8 @@ declare_features! (
(accepted, min_const_generics, "1.51.0", Some(74878)),
/// Allows calling `const unsafe fn` inside `unsafe` blocks in `const fn` functions.
(accepted, min_const_unsafe_fn, "1.33.0", Some(55607)),
/// Allows exhaustive pattern matching on uninhabited types when matched by value.
(accepted, min_exhaustive_patterns, "CURRENT_RUSTC_VERSION", Some(119612)),
/// Allows using `Self` and associated types in struct expressions and patterns.
(accepted, more_struct_aliases, "1.16.0", Some(37544)),
/// Allows using the MOVBE target feature.
Expand Down
3 changes: 0 additions & 3 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -519,9 +519,6 @@ declare_features! (
(unstable, macro_metavar_expr_concat, "1.81.0", Some(124225)),
/// Allows `#[marker]` on certain traits allowing overlapping implementations.
(unstable, marker_trait_attr, "1.30.0", Some(29864)),
/// Allows exhaustive pattern matching on types that contain uninhabited types in cases that are
/// unambiguously sound.
(unstable, min_exhaustive_patterns, "1.77.0", Some(119612)),
/// A minimal, sound subset of specialization intended to be used by the
/// standard library until the soundness issues with specialization
/// are fixed.
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 @@ -28,6 +28,7 @@
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::potential_query_instability)]
#![allow(rustc::untranslatable_diagnostic)]
#![cfg_attr(bootstrap, feature(min_exhaustive_patterns))]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(allocator_api)]
Expand All @@ -48,7 +49,6 @@
#![feature(iter_from_coroutine)]
#![feature(let_chains)]
#![feature(macro_metavar_expr)]
#![feature(min_exhaustive_patterns)]
#![feature(min_specialization)]
#![feature(negative_impls)]
#![feature(never_type)]
Expand Down
13 changes: 5 additions & 8 deletions compiler/rustc_mir_build/src/build/matches/match_pair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,14 +208,11 @@ impl<'pat, 'tcx> MatchPairTree<'pat, 'tcx> {
subpairs = cx.field_match_pairs(downcast_place, subpatterns);

let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
i == variant_index || {
(cx.tcx.features().exhaustive_patterns
|| cx.tcx.features().min_exhaustive_patterns)
&& !v
.inhabited_predicate(cx.tcx, adt_def)
.instantiate(cx.tcx, args)
.apply_ignore_module(cx.tcx, cx.param_env)
}
i == variant_index
|| !v
.inhabited_predicate(cx.tcx, adt_def)
.instantiate(cx.tcx, args)
.apply_ignore_module(cx.tcx, cx.param_env)
}) && (adt_def.did().is_local()
|| !adt_def.is_variant_list_non_exhaustive());
if irrefutable {
Expand Down
6 changes: 2 additions & 4 deletions compiler/rustc_mir_build/src/thir/pattern/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -695,9 +695,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 (self.tcx.features().exhaustive_patterns
|| self.tcx.features().min_exhaustive_patterns)
&& let Some(witness_1) = witnesses.get(0)
let witness_1_is_privately_uninhabited = if let Some(witness_1) = witnesses.get(0)
&& let ty::Adt(adt, args) = witness_1.ty().kind()
&& adt.is_enum()
&& let Constructor::Variant(variant_index) = witness_1.ctor()
Expand Down Expand Up @@ -1059,7 +1057,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
err.note("`&str` cannot be matched exhaustively, so a wildcard `_` is necessary");
} else if cx.is_foreign_non_exhaustive_enum(ty) {
err.note(format!("`{ty}` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively"));
} else if cx.is_uninhabited(ty.inner()) && cx.tcx.features().min_exhaustive_patterns {
} else if cx.is_uninhabited(ty.inner()) {
// The type is uninhabited yet there is a witness: we must be in the `MaybeInvalid`
// case.
err.note(format!("`{ty}` is uninhabited but is not being matched by value, so a wildcard `_` is required"));
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_pattern_analysis/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ pub trait PatCx: Sized + fmt::Debug {
type PatData: Clone;

fn is_exhaustive_patterns_feature_on(&self) -> bool;
fn is_min_exhaustive_patterns_feature_on(&self) -> bool;

/// The number of fields for this constructor.
fn ctor_arity(&self, ctor: &Constructor<Self>, ty: &Self::Ty) -> usize;
Expand Down
7 changes: 1 addition & 6 deletions compiler/rustc_pattern_analysis/src/rustc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,9 +237,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
let tys = cx.variant_sub_tys(ty, variant).map(|(field, ty)| {
let is_visible =
adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx);
let is_uninhabited = (cx.tcx.features().exhaustive_patterns
|| cx.tcx.features().min_exhaustive_patterns)
&& cx.is_uninhabited(*ty);
let is_uninhabited = cx.is_uninhabited(*ty);
let skip = is_uninhabited && (!is_visible || is_non_exhaustive);
(ty, PrivateUninhabitedField(skip))
});
Expand Down Expand Up @@ -925,9 +923,6 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
fn is_exhaustive_patterns_feature_on(&self) -> bool {
self.tcx.features().exhaustive_patterns
}
fn is_min_exhaustive_patterns_feature_on(&self) -> bool {
self.tcx.features().min_exhaustive_patterns
}

fn ctor_arity(&self, ctor: &crate::constructor::Constructor<Self>, ty: &Self::Ty) -> usize {
self.ctor_arity(ctor, *ty)
Expand Down
19 changes: 7 additions & 12 deletions compiler/rustc_pattern_analysis/src/usefulness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -543,13 +543,11 @@
//! recurse into subpatterns. That second part is done through [`PlaceValidity`], most notably
//! [`PlaceValidity::specialize`].
//!
//! Having said all that, in practice we don't fully follow what's been presented in this section.
//! Let's call "toplevel exception" the case where the match scrutinee itself has type `!` or
//! `EmptyEnum`. First, on stable rust, we require `_` patterns for empty types in all cases apart
//! from the toplevel exception. The `exhaustive_patterns` and `min_exaustive_patterns` allow
//! omitting patterns in the cases described above. There's a final detail: in the toplevel
//! exception or with the `exhaustive_patterns` feature, we ignore place validity when checking
//! whether a pattern is required for exhaustiveness. I (Nadrieril) hope to deprecate this behavior.
//! Having said all that, we don't fully follow what's been presented in this section. For
//! backwards-compatibility, we ignore place validity when checking whether a pattern is required
//! for exhaustiveness in two cases: when the `exhaustive_patterns` feature gate is on, or when the
//! match scrutinee itself has type `!` or `EmptyEnum`. I (Nadrieril) hope to deprecate this
//! exception.
//!
//!
//!
Expand Down Expand Up @@ -953,13 +951,10 @@ impl<Cx: PatCx> PlaceInfo<Cx> {
self.is_scrutinee && matches!(ctors_for_ty, ConstructorSet::NoConstructors);
// Whether empty patterns are counted as useful or not. We only warn an empty arm unreachable if
// it is guaranteed unreachable by the opsem (i.e. if the place is `known_valid`).
let empty_arms_are_unreachable = self.validity.is_known_valid()
&& (is_toplevel_exception
|| cx.is_exhaustive_patterns_feature_on()
|| cx.is_min_exhaustive_patterns_feature_on());
let empty_arms_are_unreachable = self.validity.is_known_valid();
// Whether empty patterns can be omitted for exhaustiveness. We ignore place validity in the
// toplevel exception and `exhaustive_patterns` cases for backwards compatibility.
let can_omit_empty_arms = empty_arms_are_unreachable
let can_omit_empty_arms = self.validity.is_known_valid()
fee1-dead marked this conversation as resolved.
Show resolved Hide resolved
|| is_toplevel_exception
|| cx.is_exhaustive_patterns_feature_on();

Expand Down
4 changes: 0 additions & 4 deletions compiler/rustc_pattern_analysis/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,6 @@ impl PatCx for Cx {
false
}

fn is_min_exhaustive_patterns_feature_on(&self) -> bool {
true
}

fn ctor_arity(&self, ctor: &Constructor<Self>, ty: &Self::Ty) -> usize {
ty.sub_tys(ctor).len()
}
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 @@ -9,12 +9,12 @@

// tidy-alphabetical-start
#![allow(internal_features)]
#![cfg_attr(bootstrap, feature(min_exhaustive_patterns))]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(assert_matches)]
#![feature(iter_intersperse)]
#![feature(let_chains)]
#![feature(min_exhaustive_patterns)]
#![feature(rustc_attrs)]
#![feature(rustdoc_internals)]
// tidy-alphabetical-end
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 @@ -87,6 +87,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 compiler/rustc_type_ir/src/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ pub trait TypeFoldable<I: Interner>: TypeVisitable<I> {
fn fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self {
match self.try_fold_with(folder) {
Ok(t) => t,
#[cfg(bootstrap)]
Err(e) => match e {},
}
}
Expand All @@ -115,6 +116,7 @@ pub trait TypeSuperFoldable<I: Interner>: TypeFoldable<I> {
fn super_fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self {
match self.try_super_fold_with(folder) {
Ok(t) => t,
#[cfg(bootstrap)]
Err(e) => match e {},
}
}
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 @@ -121,6 +121,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 @@ -242,6 +243,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 @@ -192,6 +192,7 @@
//
// Language features:
// tidy-alphabetical-start
#![cfg_attr(bootstrap, feature(min_exhaustive_patterns))]
#![feature(abi_unadjusted)]
#![feature(adt_const_params)]
#![feature(allow_internal_unsafe)]
Expand Down Expand Up @@ -225,7 +226,6 @@
#![feature(link_llvm_intrinsics)]
#![feature(macro_metavar_expr)]
#![feature(marker_trait_attr)]
#![feature(min_exhaustive_patterns)]
#![feature(min_specialization)]
#![feature(multiple_supertrait_upcastable)]
#![feature(must_not_suspend)]
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 @@ -272,6 +272,7 @@
//
// Language features:
// tidy-alphabetical-start
#![cfg_attr(bootstrap, feature(min_exhaustive_patterns))]
#![feature(alloc_error_handler)]
#![feature(allocator_internals)]
#![feature(allow_internal_unsafe)]
Expand Down Expand Up @@ -299,7 +300,6 @@
#![feature(link_cfg)]
#![feature(linkage)]
#![feature(macro_metavar_expr_concat)]
#![feature(min_exhaustive_patterns)]
#![feature(min_specialization)]
#![feature(must_not_suspend)]
#![feature(needs_panic_runtime)]
Expand Down
1 change: 1 addition & 0 deletions src/tools/clippy/clippy_utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2931,6 +2931,7 @@ pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> ExprU
moved_before_use,
same_ctxt,
},
#[allow(unreachable_patterns)]
Some(ControlFlow::Break(_)) => unreachable!("type of node is ControlFlow<!>"),
None => ExprUseCtxt {
node: Node::Crate(cx.tcx.hir().root_module()),
Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/tests/ui/single_match_else.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ fn main() {

// lint here
use std::convert::Infallible;
if let Ok(a) = Result::<i32, Infallible>::Ok(1) { println!("${:?}", a) } else {
if let Ok(a) = Result::<i32, &Infallible>::Ok(1) { println!("${:?}", a) } else {
println!("else block");
return;
}
Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/tests/ui/single_match_else.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ fn main() {

// lint here
use std::convert::Infallible;
match Result::<i32, Infallible>::Ok(1) {
match Result::<i32, &Infallible>::Ok(1) {
Ok(a) => println!("${:?}", a),
Err(_) => {
println!("else block");
Expand Down
4 changes: 2 additions & 2 deletions src/tools/clippy/tests/ui/single_match_else.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ LL + }
error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
--> tests/ui/single_match_else.rs:101:5
|
LL | / match Result::<i32, Infallible>::Ok(1) {
LL | / match Result::<i32, &Infallible>::Ok(1) {
LL | | Ok(a) => println!("${:?}", a),
LL | | Err(_) => {
LL | | println!("else block");
Expand All @@ -75,7 +75,7 @@ LL | | }
|
help: try
|
LL ~ if let Ok(a) = Result::<i32, Infallible>::Ok(1) { println!("${:?}", a) } else {
LL ~ if let Ok(a) = Result::<i32, &Infallible>::Ok(1) { println!("${:?}", a) } else {
LL + println!("else block");
LL + return;
LL + }
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 @@ -463,6 +463,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
1 change: 1 addition & 0 deletions src/tools/miri/tests/pass/async-fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ async fn hello_world() {
}

// This example comes from https://github.com/rust-lang/rust/issues/115145
#[allow(unreachable_patterns)]
async fn uninhabited_variant() {
async fn unreachable(_: Never) {}

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 @@ -43,6 +43,7 @@ fn discriminant_overflow() {
}
}

#[allow(unreachable_patterns)]
fn more_discriminant_overflow() {
pub enum Infallible {}

Expand Down
1 change: 1 addition & 0 deletions src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ macro_rules! from_bytes {
($ty:tt, $value:expr) => {
($ty::from_le_bytes(match ($value).try_into() {
Ok(it) => it,
#[allow(unreachable_patterns)]
Err(_) => return Err(MirEvalError::InternalError("mismatched size".into())),
}))
};
Expand Down
1 change: 1 addition & 0 deletions src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1161,6 +1161,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
ProjectionElem::OpaqueCast(it) => {
ProjectionElem::OpaqueCast(it)
}
#[allow(unreachable_patterns)]
ProjectionElem::Index(it) => match it {},
})
.collect(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@ fn opt1(_1: &Result<u32, Void>) -> &u32 {

bb0: {
PlaceMention(_1);
_2 = discriminant((*_1));
switchInt(move _2) -> [0: bb2, 1: bb3, otherwise: bb1];
falseEdge -> [real: bb4, imaginary: bb1];
}

bb1: {
FakeRead(ForMatchedPlace(None), _1);
unreachable;
_2 = discriminant((*_1));
switchInt(move _2) -> [1: bb3, otherwise: bb2];
}

bb2: {
falseEdge -> [real: bb4, imaginary: bb3];
FakeRead(ForMatchedPlace(None), _1);
unreachable;
}

bb3: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,10 @@ fn opt2(_1: &Result<u32, Void>) -> &u32 {

bb0: {
PlaceMention(_1);
_2 = discriminant((*_1));
switchInt(move _2) -> [0: bb2, 1: bb3, otherwise: bb1];
}

bb1: {
FakeRead(ForMatchedPlace(None), _1);
unreachable;
}

bb2: {
StorageLive(_3);
_3 = &(((*_1) as Ok).0: u32);
_0 = &(*_3);
StorageDead(_3);
return;
}

bb3: {
FakeRead(ForMatchedPlace(None), (((*_1) as Err).0: Void));
unreachable;
}
}
Loading
Loading