Stabilize min_exhaustive_patterns
This commit is contained in:
parent
19469cb536
commit
cd40769c02
@ -267,6 +267,8 @@ macro_rules! declare_features {
|
|||||||
(accepted, min_const_generics, "1.51.0", Some(74878)),
|
(accepted, min_const_generics, "1.51.0", Some(74878)),
|
||||||
/// Allows calling `const unsafe fn` inside `unsafe` blocks in `const fn` functions.
|
/// Allows calling `const unsafe fn` inside `unsafe` blocks in `const fn` functions.
|
||||||
(accepted, min_const_unsafe_fn, "1.33.0", Some(55607)),
|
(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.
|
/// Allows using `Self` and associated types in struct expressions and patterns.
|
||||||
(accepted, more_struct_aliases, "1.16.0", Some(37544)),
|
(accepted, more_struct_aliases, "1.16.0", Some(37544)),
|
||||||
/// Allows using the MOVBE target feature.
|
/// Allows using the MOVBE target feature.
|
||||||
|
@ -519,9 +519,6 @@ pub fn internal(&self, feature: Symbol) -> bool {
|
|||||||
(unstable, macro_metavar_expr_concat, "1.81.0", Some(124225)),
|
(unstable, macro_metavar_expr_concat, "1.81.0", Some(124225)),
|
||||||
/// Allows `#[marker]` on certain traits allowing overlapping implementations.
|
/// Allows `#[marker]` on certain traits allowing overlapping implementations.
|
||||||
(unstable, marker_trait_attr, "1.30.0", Some(29864)),
|
(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
|
/// A minimal, sound subset of specialization intended to be used by the
|
||||||
/// standard library until the soundness issues with specialization
|
/// standard library until the soundness issues with specialization
|
||||||
/// are fixed.
|
/// are fixed.
|
||||||
|
@ -208,14 +208,11 @@ pub(in crate::build) fn for_pattern(
|
|||||||
subpairs = cx.field_match_pairs(downcast_place, subpatterns);
|
subpairs = cx.field_match_pairs(downcast_place, subpatterns);
|
||||||
|
|
||||||
let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
|
let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
|
||||||
i == variant_index || {
|
i == variant_index
|
||||||
(cx.tcx.features().exhaustive_patterns
|
|| !v
|
||||||
|| cx.tcx.features().min_exhaustive_patterns)
|
|
||||||
&& !v
|
|
||||||
.inhabited_predicate(cx.tcx, adt_def)
|
.inhabited_predicate(cx.tcx, adt_def)
|
||||||
.instantiate(cx.tcx, args)
|
.instantiate(cx.tcx, args)
|
||||||
.apply_ignore_module(cx.tcx, cx.param_env)
|
.apply_ignore_module(cx.tcx, cx.param_env)
|
||||||
}
|
|
||||||
}) && (adt_def.did().is_local()
|
}) && (adt_def.did().is_local()
|
||||||
|| !adt_def.is_variant_list_non_exhaustive());
|
|| !adt_def.is_variant_list_non_exhaustive());
|
||||||
if irrefutable {
|
if irrefutable {
|
||||||
|
@ -695,9 +695,7 @@ fn check_binding_is_irrefutable(
|
|||||||
|
|
||||||
// Emit an extra note if the first uncovered witness would be uninhabited
|
// Emit an extra note if the first uncovered witness would be uninhabited
|
||||||
// if we disregard visibility.
|
// if we disregard visibility.
|
||||||
let witness_1_is_privately_uninhabited = if (self.tcx.features().exhaustive_patterns
|
let witness_1_is_privately_uninhabited = if let Some(witness_1) = witnesses.get(0)
|
||||||
|| self.tcx.features().min_exhaustive_patterns)
|
|
||||||
&& let Some(witness_1) = witnesses.get(0)
|
|
||||||
&& let ty::Adt(adt, args) = witness_1.ty().kind()
|
&& let ty::Adt(adt, args) = witness_1.ty().kind()
|
||||||
&& adt.is_enum()
|
&& adt.is_enum()
|
||||||
&& let Constructor::Variant(variant_index) = witness_1.ctor()
|
&& let Constructor::Variant(variant_index) = witness_1.ctor()
|
||||||
@ -1059,7 +1057,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
|
|||||||
err.note("`&str` cannot be matched exhaustively, so a wildcard `_` is necessary");
|
err.note("`&str` cannot be matched exhaustively, so a wildcard `_` is necessary");
|
||||||
} else if cx.is_foreign_non_exhaustive_enum(ty) {
|
} 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"));
|
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`
|
// The type is uninhabited yet there is a witness: we must be in the `MaybeInvalid`
|
||||||
// case.
|
// case.
|
||||||
err.note(format!("`{ty}` is uninhabited but is not being matched by value, so a wildcard `_` is required"));
|
err.note(format!("`{ty}` is uninhabited but is not being matched by value, so a wildcard `_` is required"));
|
||||||
|
@ -54,7 +54,6 @@ pub trait PatCx: Sized + fmt::Debug {
|
|||||||
type PatData: Clone;
|
type PatData: Clone;
|
||||||
|
|
||||||
fn is_exhaustive_patterns_feature_on(&self) -> bool;
|
fn is_exhaustive_patterns_feature_on(&self) -> bool;
|
||||||
fn is_min_exhaustive_patterns_feature_on(&self) -> bool;
|
|
||||||
|
|
||||||
/// The number of fields for this constructor.
|
/// The number of fields for this constructor.
|
||||||
fn ctor_arity(&self, ctor: &Constructor<Self>, ty: &Self::Ty) -> usize;
|
fn ctor_arity(&self, ctor: &Constructor<Self>, ty: &Self::Ty) -> usize;
|
||||||
|
@ -237,9 +237,7 @@ fn reveal_and_alloc<'a, 'tcx>(
|
|||||||
let tys = cx.variant_sub_tys(ty, variant).map(|(field, ty)| {
|
let tys = cx.variant_sub_tys(ty, variant).map(|(field, ty)| {
|
||||||
let is_visible =
|
let is_visible =
|
||||||
adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx);
|
adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx);
|
||||||
let is_uninhabited = (cx.tcx.features().exhaustive_patterns
|
let is_uninhabited = cx.is_uninhabited(*ty);
|
||||||
|| cx.tcx.features().min_exhaustive_patterns)
|
|
||||||
&& cx.is_uninhabited(*ty);
|
|
||||||
let skip = is_uninhabited && (!is_visible || is_non_exhaustive);
|
let skip = is_uninhabited && (!is_visible || is_non_exhaustive);
|
||||||
(ty, PrivateUninhabitedField(skip))
|
(ty, PrivateUninhabitedField(skip))
|
||||||
});
|
});
|
||||||
@ -925,9 +923,6 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
|
|||||||
fn is_exhaustive_patterns_feature_on(&self) -> bool {
|
fn is_exhaustive_patterns_feature_on(&self) -> bool {
|
||||||
self.tcx.features().exhaustive_patterns
|
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 {
|
fn ctor_arity(&self, ctor: &crate::constructor::Constructor<Self>, ty: &Self::Ty) -> usize {
|
||||||
self.ctor_arity(ctor, *ty)
|
self.ctor_arity(ctor, *ty)
|
||||||
|
@ -543,13 +543,11 @@
|
|||||||
//! recurse into subpatterns. That second part is done through [`PlaceValidity`], most notably
|
//! recurse into subpatterns. That second part is done through [`PlaceValidity`], most notably
|
||||||
//! [`PlaceValidity::specialize`].
|
//! [`PlaceValidity::specialize`].
|
||||||
//!
|
//!
|
||||||
//! Having said all that, in practice we don't fully follow what's been presented in this section.
|
//! Having said all that, we don't fully follow what's been presented in this section. For
|
||||||
//! Let's call "toplevel exception" the case where the match scrutinee itself has type `!` or
|
//! backwards-compatibility, we ignore place validity when checking whether a pattern is required
|
||||||
//! `EmptyEnum`. First, on stable rust, we require `_` patterns for empty types in all cases apart
|
//! for exhaustiveness in two cases: when the `exhaustive_patterns` feature gate is on, or when the
|
||||||
//! from the toplevel exception. The `exhaustive_patterns` and `min_exaustive_patterns` allow
|
//! match scrutinee itself has type `!` or `EmptyEnum`. I (Nadrieril) hope to deprecate this
|
||||||
//! omitting patterns in the cases described above. There's a final detail: in the toplevel
|
//! exception.
|
||||||
//! 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.
|
|
||||||
//!
|
//!
|
||||||
//!
|
//!
|
||||||
//!
|
//!
|
||||||
@ -953,13 +951,10 @@ fn split_column_ctors<'a>(
|
|||||||
self.is_scrutinee && matches!(ctors_for_ty, ConstructorSet::NoConstructors);
|
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
|
// 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`).
|
// 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()
|
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());
|
|
||||||
// Whether empty patterns can be omitted for exhaustiveness. We ignore place validity in the
|
// Whether empty patterns can be omitted for exhaustiveness. We ignore place validity in the
|
||||||
// toplevel exception and `exhaustive_patterns` cases for backwards compatibility.
|
// 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()
|
||||||
|| is_toplevel_exception
|
|| is_toplevel_exception
|
||||||
|| cx.is_exhaustive_patterns_feature_on();
|
|| cx.is_exhaustive_patterns_feature_on();
|
||||||
|
|
||||||
|
@ -152,10 +152,6 @@ fn is_exhaustive_patterns_feature_on(&self) -> bool {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_min_exhaustive_patterns_feature_on(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ctor_arity(&self, ctor: &Constructor<Self>, ty: &Self::Ty) -> usize {
|
fn ctor_arity(&self, ctor: &Constructor<Self>, ty: &Self::Ty) -> usize {
|
||||||
ty.sub_tys(ctor).len()
|
ty.sub_tys(ctor).len()
|
||||||
}
|
}
|
||||||
|
@ -121,6 +121,7 @@ fn fold<B, F>(mut self, init: B, mut f: F) -> B
|
|||||||
{
|
{
|
||||||
match self.try_fold(init, |b, item| Ok::<B, !>(f(b, item))) {
|
match self.try_fold(init, |b, item| Ok::<B, !>(f(b, item))) {
|
||||||
Ok(b) => b,
|
Ok(b) => b,
|
||||||
|
#[cfg(bootstrap)]
|
||||||
Err(e) => match e {},
|
Err(e) => match e {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -242,6 +243,7 @@ fn rfold<B, F>(mut self, init: B, mut f: F) -> B
|
|||||||
{
|
{
|
||||||
match self.try_rfold(init, |b, item| Ok::<B, !>(f(b, item))) {
|
match self.try_rfold(init, |b, item| Ok::<B, !>(f(b, item))) {
|
||||||
Ok(b) => b,
|
Ok(b) => b,
|
||||||
|
#[cfg(bootstrap)]
|
||||||
Err(e) => match e {},
|
Err(e) => match e {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
//@ revisions: normal min_exh_pats exhaustive_patterns never_pats
|
//@ revisions: normal min_exh_pats exhaustive_patterns never_pats
|
||||||
// gate-test-min_exhaustive_patterns
|
|
||||||
//
|
//
|
||||||
// This tests correct handling of empty types in exhaustiveness checking.
|
// This tests correct handling of empty types in exhaustiveness checking.
|
||||||
//
|
//
|
||||||
|
Loading…
Reference in New Issue
Block a user