Skip to content

Commit 38b681b

Browse files
authored
Unrolled build for rust-lang#118803
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.
2 parents 69db514 + 95a14d4 commit 38b681b

File tree

9 files changed

+801
-149
lines changed

9 files changed

+801
-149
lines changed

compiler/rustc_feature/src/unstable.rs

+3
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,9 @@ declare_features! (
516516
(unstable, macro_metavar_expr, "1.61.0", Some(83527)),
517517
/// Allows `#[marker]` on certain traits allowing overlapping implementations.
518518
(unstable, marker_trait_attr, "1.30.0", Some(29864)),
519+
/// Allows exhaustive pattern matching on types that contain uninhabited types in cases that are
520+
/// unambiguously sound.
521+
(incomplete, min_exhaustive_patterns, "CURRENT_RUSTC_VERSION", Some(119612)),
519522
/// A minimal, sound subset of specialization intended to be used by the
520523
/// standard library until the soundness issues with specialization
521524
/// are fixed.

compiler/rustc_pattern_analysis/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ pub trait TypeCx: Sized + fmt::Debug {
9595
type PatData: Clone;
9696

9797
fn is_exhaustive_patterns_feature_on(&self) -> bool;
98+
fn is_min_exhaustive_patterns_feature_on(&self) -> bool;
9899

99100
/// The number of fields for this constructor.
100101
fn ctor_arity(&self, ctor: &Constructor<Self>, ty: &Self::Ty) -> usize;

compiler/rustc_pattern_analysis/src/rustc.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,9 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
181181
// `field.ty()` doesn't normalize after substituting.
182182
let ty = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
183183
let is_visible = adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx);
184-
let is_uninhabited = cx.tcx.features().exhaustive_patterns && cx.is_uninhabited(ty);
184+
let is_uninhabited = (cx.tcx.features().exhaustive_patterns
185+
|| cx.tcx.features().min_exhaustive_patterns)
186+
&& cx.is_uninhabited(ty);
185187

186188
if is_uninhabited && (!is_visible || is_non_exhaustive) {
187189
None
@@ -863,6 +865,9 @@ impl<'p, 'tcx> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> {
863865
fn is_exhaustive_patterns_feature_on(&self) -> bool {
864866
self.tcx.features().exhaustive_patterns
865867
}
868+
fn is_min_exhaustive_patterns_feature_on(&self) -> bool {
869+
self.tcx.features().min_exhaustive_patterns
870+
}
866871

867872
fn ctor_arity(&self, ctor: &crate::constructor::Constructor<Self>, ty: &Self::Ty) -> usize {
868873
self.ctor_arity(ctor, *ty)

compiler/rustc_pattern_analysis/src/usefulness.rs

+17-9
Original file line numberDiff line numberDiff line change
@@ -548,11 +548,12 @@
548548
//! [`ValidityConstraint::specialize`].
549549
//!
550550
//! Having said all that, in practice we don't fully follow what's been presented in this section.
551-
//! Under `exhaustive_patterns`, we allow omitting empty arms even in `!known_valid` places, for
552-
//! backwards-compatibility until we have a better alternative. Without `exhaustive_patterns`, we
553-
//! mostly treat empty types as inhabited, except specifically a non-nested `!` or empty enum. In
554-
//! this specific case we also allow the empty match regardless of place validity, for
555-
//! backwards-compatibility. Hopefully we can eventually deprecate this.
551+
//! Let's call "toplevel exception" the case where the match scrutinee itself has type `!` or
552+
//! `EmptyEnum`. First, on stable rust, we require `_` patterns for empty types in all cases apart
553+
//! from the toplevel exception. The `exhaustive_patterns` and `min_exaustive_patterns` allow
554+
//! omitting patterns in the cases described above. There's a final detail: in the toplevel
555+
//! exception or with the `exhaustive_patterns` feature, we ignore place validity when checking
556+
//! whether a pattern is required for exhaustiveness. I (Nadrieril) hope to deprecate this behavior.
556557
//!
557558
//!
558559
//!
@@ -1442,10 +1443,17 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
14421443
// We treat match scrutinees of type `!` or `EmptyEnum` differently.
14431444
let is_toplevel_exception =
14441445
is_top_level && matches!(ctors_for_ty, ConstructorSet::NoConstructors);
1445-
// Whether empty patterns can be omitted for exhaustiveness.
1446-
let can_omit_empty_arms = is_toplevel_exception || mcx.tycx.is_exhaustive_patterns_feature_on();
1447-
// Whether empty patterns are counted as useful or not.
1448-
let empty_arms_are_unreachable = place_validity.is_known_valid() && can_omit_empty_arms;
1446+
// Whether empty patterns are counted as useful or not. We only warn an empty arm unreachable if
1447+
// it is guaranteed unreachable by the opsem (i.e. if the place is `known_valid`).
1448+
let empty_arms_are_unreachable = place_validity.is_known_valid()
1449+
&& (is_toplevel_exception
1450+
|| mcx.tycx.is_exhaustive_patterns_feature_on()
1451+
|| mcx.tycx.is_min_exhaustive_patterns_feature_on());
1452+
// Whether empty patterns can be omitted for exhaustiveness. We ignore place validity in the
1453+
// toplevel exception and `exhaustive_patterns` cases for backwards compatibility.
1454+
let can_omit_empty_arms = empty_arms_are_unreachable
1455+
|| is_toplevel_exception
1456+
|| mcx.tycx.is_exhaustive_patterns_feature_on();
14491457

14501458
// Analyze the constructors present in this column.
14511459
let ctors = matrix.heads().map(|p| p.ctor());

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1029,6 +1029,7 @@ symbols! {
10291029
min_const_fn,
10301030
min_const_generics,
10311031
min_const_unsafe_fn,
1032+
min_exhaustive_patterns,
10321033
min_specialization,
10331034
min_type_alias_impl_trait,
10341035
minnumf32,

0 commit comments

Comments
 (0)