mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
Correctly handle empty constructors
- `ConstructorSet` knows about both empty and nonempty constructors; - If an empty constructor is present in the column, we output it in `split().present`.
This commit is contained in:
parent
9aafc0b815
commit
2186f98f16
@ -10,6 +10,7 @@
|
||||
//! precisely here.
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//! # Constructor grouping and splitting
|
||||
//!
|
||||
//! As explained in the corresponding section in [`super::usefulness`], to make usefulness tractable
|
||||
@ -49,6 +50,7 @@
|
||||
//! [`IntRange::split`]) and slice splitting (see [`Slice::split`]).
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//! # The `Missing` constructor
|
||||
//!
|
||||
//! We detail a special case of constructor splitting that is a bit subtle. Take the following:
|
||||
@ -77,6 +79,69 @@
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//! ## Empty types, empty constructors, and the `exhaustive_patterns` feature
|
||||
//!
|
||||
//! An empty type is a type that has no valid value, like `!`, `enum Void {}`, or `Result<!, !>`.
|
||||
//! They require careful handling.
|
||||
//!
|
||||
//! First, for soundness reasons related to the possible existence of invalid values, by default we
|
||||
//! don't treat empty types as empty. We force them to be matched with wildcards. Except if the
|
||||
//! `exhaustive_patterns` feature is turned on, in which case we do treat them as empty. And also
|
||||
//! except if the type has no constructors (like `enum Void {}` but not like `Result<!, !>`), we
|
||||
//! specifically allow `match void {}` to be exhaustive. There are additionally considerations of
|
||||
//! place validity that are handled in `super::usefulness`. Yes this is a bit tricky.
|
||||
//!
|
||||
//! The second thing is that regardless of the above, it is always allowed to use all the
|
||||
//! constructors of a type. For example, all the following is ok:
|
||||
//!
|
||||
//! ```rust,ignore(example)
|
||||
//! # #![feature(never_type)]
|
||||
//! # #![feature(exhaustive_patterns)]
|
||||
//! fn foo(x: Option<!>) {
|
||||
//! match x {
|
||||
//! None => {}
|
||||
//! Some(_) => {}
|
||||
//! }
|
||||
//! }
|
||||
//! fn bar(x: &[!]) -> u32 {
|
||||
//! match x {
|
||||
//! [] => 1,
|
||||
//! [_] => 2,
|
||||
//! [_, _] => 3,
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Moreover, take the following:
|
||||
//!
|
||||
//! ```rust
|
||||
//! # #![feature(never_type)]
|
||||
//! # #![feature(exhaustive_patterns)]
|
||||
//! # let x = None::<!>;
|
||||
//! match x {
|
||||
//! None => {}
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! On a normal type, we would identify `Some` as missing and tell the user. If `x: Option<!>`
|
||||
//! however (and `exhaustive_patterns` is on), it's ok to omit `Some`. When listing the constructors
|
||||
//! of a type, we must therefore track which can be omitted.
|
||||
//!
|
||||
//! Let's call "empty" a constructor that matches no valid value for the type, like `Some` for the
|
||||
//! type `Option<!>`. What this all means is that `ConstructorSet` must know which constructors are
|
||||
//! empty. The difference between empty and nonempty constructors is that empty constructors need
|
||||
//! not be present for the match to be exhaustive.
|
||||
//!
|
||||
//! A final remark: empty constructors of arity 0 break specialization, we must avoid them. The
|
||||
//! reason is that if we specialize by them, nothing remains to witness the emptiness; the rest of
|
||||
//! the algorithm can't distinguish them from a nonempty constructor. The only known case where this
|
||||
//! could happen is the `[..]` pattern on `[!; N]` with `N > 0` so we must take care to not emit it.
|
||||
//!
|
||||
//! This is all handled by [`ConstructorSet::for_ty`] and [`ConstructorSet::split`]. The invariants
|
||||
//! of [`SplitConstructorSet`] are also of interest.
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//! ## Opaque patterns
|
||||
//!
|
||||
//! Some patterns, such as constants that are not allowed to be matched structurally, cannot be
|
||||
@ -95,7 +160,7 @@ use rustc_apfloat::ieee::{DoubleS, IeeeFloat, SingleS};
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir::RangeEnd;
|
||||
use rustc_index::Idx;
|
||||
use rustc_index::{Idx, IndexVec};
|
||||
use rustc_middle::middle::stability::EvalResult;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::mir::interpret::Scalar;
|
||||
@ -482,8 +547,12 @@ pub(super) struct Slice {
|
||||
impl Slice {
|
||||
fn new(array_len: Option<usize>, kind: SliceKind) -> Self {
|
||||
let kind = match (array_len, kind) {
|
||||
// If the middle `..` is empty, we effectively have a fixed-length pattern.
|
||||
(Some(len), VarLen(prefix, suffix)) if prefix + suffix >= len => FixedLen(len),
|
||||
// If the middle `..` has length 0, we effectively have a fixed-length pattern.
|
||||
(Some(len), VarLen(prefix, suffix)) if prefix + suffix == len => FixedLen(len),
|
||||
(Some(len), VarLen(prefix, suffix)) if prefix + suffix > len => bug!(
|
||||
"Slice pattern of length {} longer than its array length {len}",
|
||||
prefix + suffix
|
||||
),
|
||||
_ => kind,
|
||||
};
|
||||
Slice { array_len, kind }
|
||||
@ -583,17 +652,18 @@ impl Slice {
|
||||
let mut seen_fixed_lens = FxHashSet::default();
|
||||
match &mut max_slice {
|
||||
VarLen(max_prefix_len, max_suffix_len) => {
|
||||
// A length larger than any fixed-length slice encountered.
|
||||
// We start at 1 in case the subtype is empty because in that case the zero-length
|
||||
// slice must be treated separately from the rest.
|
||||
let mut fixed_len_upper_bound = 1;
|
||||
// We grow `max_slice` to be larger than all slices encountered, as described above.
|
||||
// For diagnostics, we keep the prefix and suffix lengths separate, but grow them so that
|
||||
// `L = max_prefix_len + max_suffix_len`.
|
||||
let mut max_fixed_len = 0;
|
||||
// `L` is `max_slice.arity()`. For diagnostics, we keep the prefix and suffix
|
||||
// lengths separate.
|
||||
for slice in column_slices {
|
||||
match slice.kind {
|
||||
FixedLen(len) => {
|
||||
max_fixed_len = cmp::max(max_fixed_len, len);
|
||||
if arity <= len {
|
||||
seen_fixed_lens.insert(len);
|
||||
}
|
||||
fixed_len_upper_bound = cmp::max(fixed_len_upper_bound, len + 1);
|
||||
seen_fixed_lens.insert(len);
|
||||
}
|
||||
VarLen(prefix, suffix) => {
|
||||
*max_prefix_len = cmp::max(*max_prefix_len, prefix);
|
||||
@ -602,12 +672,11 @@ impl Slice {
|
||||
}
|
||||
}
|
||||
}
|
||||
// We want `L = max(L, max_fixed_len + 1)`, modulo the fact that we keep prefix and
|
||||
// suffix separate.
|
||||
if max_fixed_len + 1 >= *max_prefix_len + *max_suffix_len {
|
||||
// The subtraction can't overflow thanks to the above check.
|
||||
// The new `max_prefix_len` is larger than its previous value.
|
||||
*max_prefix_len = max_fixed_len + 1 - *max_suffix_len;
|
||||
// If `fixed_len_upper_bound >= L`, we set `L` to `fixed_len_upper_bound`.
|
||||
if let Some(delta) =
|
||||
fixed_len_upper_bound.checked_sub(*max_prefix_len + *max_suffix_len)
|
||||
{
|
||||
*max_prefix_len += delta
|
||||
}
|
||||
|
||||
// We cap the arity of `max_slice` at the array size.
|
||||
@ -856,34 +925,46 @@ impl<'tcx> Constructor<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes the set of all constructors for a type.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(super) enum VariantVisibility {
|
||||
/// Variant that doesn't fit the other cases, i.e. most variants.
|
||||
Visible,
|
||||
/// Variant behind an unstable gate or with the `#[doc(hidden)]` attribute. It will not be
|
||||
/// mentioned in diagnostics unless the user mentioned it first.
|
||||
Hidden,
|
||||
/// Variant that matches no value. E.g. `Some::<Option<!>>` if the `exhaustive_patterns` feature
|
||||
/// is enabled. Like `Hidden`, it will not be mentioned in diagnostics unless the user mentioned
|
||||
/// it first.
|
||||
Empty,
|
||||
}
|
||||
|
||||
/// Describes the set of all constructors for a type. For details, in particular about the emptiness
|
||||
/// of constructors, see the top of the file.
|
||||
///
|
||||
/// In terms of division of responsibility, [`ConstructorSet::split`] handles all of the
|
||||
/// `exhaustive_patterns` feature.
|
||||
#[derive(Debug)]
|
||||
pub(super) enum ConstructorSet {
|
||||
/// The type has a single constructor, e.g. `&T` or a struct.
|
||||
Single,
|
||||
/// This type has the following list of constructors.
|
||||
/// Some variants are hidden, which means they won't be mentioned in diagnostics unless the user
|
||||
/// mentioned them first. We use this for variants behind an unstable gate as well as
|
||||
/// `#[doc(hidden)]` ones.
|
||||
Variants {
|
||||
visible_variants: Vec<VariantIdx>,
|
||||
hidden_variants: Vec<VariantIdx>,
|
||||
non_exhaustive: bool,
|
||||
},
|
||||
/// The type has a single constructor, e.g. `&T` or a struct. `empty` tracks whether the
|
||||
/// constructor is empty.
|
||||
Single { empty: bool },
|
||||
/// This type has the following list of constructors. If `variants` is empty and
|
||||
/// `non_exhaustive` is false, don't use this; use `NoConstructors` instead.
|
||||
Variants { variants: IndexVec<VariantIdx, VariantVisibility>, non_exhaustive: bool },
|
||||
/// Booleans.
|
||||
Bool,
|
||||
/// The type is spanned by integer values. The range or ranges give the set of allowed values.
|
||||
/// The second range is only useful for `char`.
|
||||
Integers { range_1: IntRange, range_2: Option<IntRange> },
|
||||
/// The type is matched by slices. The usize is the compile-time length of the array, if known.
|
||||
Slice(Option<usize>),
|
||||
/// The type is matched by slices whose elements are uninhabited.
|
||||
SliceOfEmpty,
|
||||
/// The type is matched by slices. `array_len` is the compile-time length of the array, if
|
||||
/// known. If `subtype_is_empty`, all constructors are empty except possibly the zero-length
|
||||
/// slice `[]`.
|
||||
Slice { array_len: Option<usize>, subtype_is_empty: bool },
|
||||
/// The constructors cannot be listed, and the type cannot be matched exhaustively. E.g. `str`,
|
||||
/// floats.
|
||||
Unlistable,
|
||||
/// The type has no inhabitants.
|
||||
Uninhabited,
|
||||
/// The type has no constructors (not even empty ones). This is `!` and empty enums.
|
||||
NoConstructors,
|
||||
}
|
||||
|
||||
/// Describes the result of analyzing the constructors in a column of a match.
|
||||
@ -913,6 +994,8 @@ pub(super) struct SplitConstructorSet<'tcx> {
|
||||
|
||||
impl ConstructorSet {
|
||||
/// Creates a set that represents all the constructors of `ty`.
|
||||
///
|
||||
/// See at the top of the file for considerations of emptiness.
|
||||
#[instrument(level = "debug", skip(cx), ret)]
|
||||
pub(super) fn for_ty<'p, 'tcx>(cx: &MatchCheckCtxt<'p, 'tcx>, ty: Ty<'tcx>) -> Self {
|
||||
let make_range = |start, end| {
|
||||
@ -924,12 +1007,6 @@ impl ConstructorSet {
|
||||
};
|
||||
// This determines the set of all possible constructors for the type `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.
|
||||
// Invariant: this is `Uninhabited` if and only if the type is uninhabited (as determined by
|
||||
// `cx.is_uninhabited()`).
|
||||
match ty.kind() {
|
||||
ty::Bool => Self::Bool,
|
||||
ty::Char => {
|
||||
@ -963,82 +1040,74 @@ impl ConstructorSet {
|
||||
};
|
||||
Self::Integers { range_1: range, range_2: None }
|
||||
}
|
||||
ty::Array(sub_ty, len) if len.try_eval_target_usize(cx.tcx, cx.param_env).is_some() => {
|
||||
let len = len.eval_target_usize(cx.tcx, cx.param_env) as usize;
|
||||
if len != 0 && cx.is_uninhabited(*sub_ty) {
|
||||
Self::Uninhabited
|
||||
} else {
|
||||
Self::Slice(Some(len))
|
||||
}
|
||||
ty::Slice(sub_ty) => {
|
||||
Self::Slice { array_len: None, subtype_is_empty: cx.is_uninhabited(*sub_ty) }
|
||||
}
|
||||
// Treat arrays of a constant but unknown length like slices.
|
||||
ty::Array(sub_ty, _) | ty::Slice(sub_ty) => {
|
||||
if cx.is_uninhabited(*sub_ty) {
|
||||
Self::SliceOfEmpty
|
||||
} else {
|
||||
Self::Slice(None)
|
||||
ty::Array(sub_ty, len) => {
|
||||
// We treat arrays of a constant but unknown length like slices.
|
||||
Self::Slice {
|
||||
array_len: len.try_eval_target_usize(cx.tcx, cx.param_env).map(|l| l as usize),
|
||||
subtype_is_empty: cx.is_uninhabited(*sub_ty),
|
||||
}
|
||||
}
|
||||
ty::Adt(def, args) if def.is_enum() => {
|
||||
// If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an
|
||||
// additional "unknown" constructor.
|
||||
// There is no point in enumerating all possible variants, because the user can't
|
||||
// actually match against them all themselves. So we always return only the fictitious
|
||||
// constructor.
|
||||
// E.g., in an example like:
|
||||
//
|
||||
// ```
|
||||
// let err: io::ErrorKind = ...;
|
||||
// match err {
|
||||
// io::ErrorKind::NotFound => {},
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// we don't want to show every possible IO error, but instead have only `_` as the
|
||||
// witness.
|
||||
let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(ty);
|
||||
|
||||
if def.variants().is_empty() && !is_declared_nonexhaustive {
|
||||
Self::Uninhabited
|
||||
Self::NoConstructors
|
||||
} else {
|
||||
let is_exhaustive_pat_feature = cx.tcx.features().exhaustive_patterns;
|
||||
let (hidden_variants, visible_variants) = 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)
|
||||
.instantiate(cx.tcx, args)
|
||||
.apply(cx.tcx, cx.param_env, cx.module)
|
||||
})
|
||||
.map(|(idx, _)| idx)
|
||||
.partition(|idx| {
|
||||
let variant_def_id = def.variant(*idx).def_id;
|
||||
// Filter variants that depend on a disabled unstable feature.
|
||||
let is_unstable = matches!(
|
||||
cx.tcx.eval_stability(variant_def_id, None, DUMMY_SP, None),
|
||||
EvalResult::Deny { .. }
|
||||
);
|
||||
// Filter foreign `#[doc(hidden)]` variants.
|
||||
let is_doc_hidden =
|
||||
cx.tcx.is_doc_hidden(variant_def_id) && !variant_def_id.is_local();
|
||||
is_unstable || is_doc_hidden
|
||||
});
|
||||
|
||||
Self::Variants {
|
||||
visible_variants,
|
||||
hidden_variants,
|
||||
non_exhaustive: is_declared_nonexhaustive,
|
||||
let mut variants =
|
||||
IndexVec::from_elem(VariantVisibility::Visible, def.variants());
|
||||
for (idx, v) in def.variants().iter_enumerated() {
|
||||
let variant_def_id = def.variant(idx).def_id;
|
||||
// Visibly uninhabited variants.
|
||||
let is_inhabited = v
|
||||
.inhabited_predicate(cx.tcx, *def)
|
||||
.instantiate(cx.tcx, args)
|
||||
.apply(cx.tcx, cx.param_env, cx.module);
|
||||
// Variants that depend on a disabled unstable feature.
|
||||
let is_unstable = matches!(
|
||||
cx.tcx.eval_stability(variant_def_id, None, DUMMY_SP, None),
|
||||
EvalResult::Deny { .. }
|
||||
);
|
||||
// Foreign `#[doc(hidden)]` variants.
|
||||
let is_doc_hidden =
|
||||
cx.tcx.is_doc_hidden(variant_def_id) && !variant_def_id.is_local();
|
||||
let visibility = if !is_inhabited {
|
||||
// FIXME: handle empty+hidden
|
||||
VariantVisibility::Empty
|
||||
} else if is_unstable || is_doc_hidden {
|
||||
VariantVisibility::Hidden
|
||||
} else {
|
||||
VariantVisibility::Visible
|
||||
};
|
||||
variants[idx] = visibility;
|
||||
}
|
||||
|
||||
Self::Variants { variants, non_exhaustive: is_declared_nonexhaustive }
|
||||
}
|
||||
}
|
||||
ty::Never => Self::Uninhabited,
|
||||
_ if cx.is_uninhabited(ty) => Self::Uninhabited,
|
||||
ty::Adt(..) | ty::Tuple(..) | ty::Ref(..) => Self::Single,
|
||||
ty::Adt(..) | ty::Tuple(..) | ty::Ref(..) => {
|
||||
Self::Single { empty: cx.is_uninhabited(ty) }
|
||||
}
|
||||
ty::Never => Self::NoConstructors,
|
||||
// This type is one for which we cannot list constructors, like `str` or `f64`.
|
||||
_ => Self::Unlistable,
|
||||
// FIXME(Nadrieril): which of these are actually allowed?
|
||||
ty::Float(_)
|
||||
| ty::Str
|
||||
| ty::Foreign(_)
|
||||
| ty::RawPtr(_)
|
||||
| ty::FnDef(_, _)
|
||||
| ty::FnPtr(_)
|
||||
| ty::Dynamic(_, _, _)
|
||||
| ty::Closure(_, _)
|
||||
| ty::Coroutine(_, _, _)
|
||||
| ty::CoroutineWitness(_, _)
|
||||
| ty::Alias(_, _)
|
||||
| ty::Param(_)
|
||||
| ty::Bound(_, _)
|
||||
| ty::Placeholder(_)
|
||||
| ty::Infer(_)
|
||||
| ty::Error(_) => Self::Unlistable,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1056,6 +1125,9 @@ impl ConstructorSet {
|
||||
'tcx: 'a,
|
||||
{
|
||||
let mut present: SmallVec<[_; 1]> = SmallVec::new();
|
||||
// Empty constructors found missing.
|
||||
let mut missing_empty = Vec::new();
|
||||
// Nonempty constructors found missing.
|
||||
let mut missing = Vec::new();
|
||||
// Constructors in `ctors`, except wildcards and opaques.
|
||||
let mut seen = Vec::new();
|
||||
@ -1068,38 +1140,36 @@ impl ConstructorSet {
|
||||
}
|
||||
|
||||
match self {
|
||||
ConstructorSet::Single => {
|
||||
if seen.is_empty() {
|
||||
missing.push(Single);
|
||||
} else {
|
||||
ConstructorSet::Single { empty } => {
|
||||
if !seen.is_empty() {
|
||||
present.push(Single);
|
||||
} else if *empty {
|
||||
missing_empty.push(Single);
|
||||
} else {
|
||||
missing.push(Single);
|
||||
}
|
||||
}
|
||||
ConstructorSet::Variants { visible_variants, hidden_variants, non_exhaustive } => {
|
||||
ConstructorSet::Variants { variants, non_exhaustive } => {
|
||||
let seen_set: FxHashSet<_> = seen.iter().map(|c| c.as_variant().unwrap()).collect();
|
||||
let mut skipped_a_hidden_variant = false;
|
||||
|
||||
for variant in visible_variants {
|
||||
let ctor = Variant(*variant);
|
||||
if seen_set.contains(variant) {
|
||||
for (idx, visibility) in variants.iter_enumerated() {
|
||||
let ctor = Variant(idx);
|
||||
if seen_set.contains(&idx) {
|
||||
present.push(ctor);
|
||||
} else {
|
||||
missing.push(ctor);
|
||||
// We only put visible variants directly into `missing`.
|
||||
match visibility {
|
||||
VariantVisibility::Visible => missing.push(ctor),
|
||||
VariantVisibility::Hidden => skipped_a_hidden_variant = true,
|
||||
VariantVisibility::Empty => missing_empty.push(ctor),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for variant in hidden_variants {
|
||||
let ctor = Variant(*variant);
|
||||
if seen_set.contains(variant) {
|
||||
present.push(ctor);
|
||||
} else {
|
||||
skipped_a_hidden_variant = true;
|
||||
}
|
||||
}
|
||||
if skipped_a_hidden_variant {
|
||||
missing.push(Hidden);
|
||||
}
|
||||
|
||||
if *non_exhaustive {
|
||||
missing.push(NonExhaustive);
|
||||
}
|
||||
@ -1143,33 +1213,22 @@ impl ConstructorSet {
|
||||
}
|
||||
}
|
||||
}
|
||||
&ConstructorSet::Slice(array_len) => {
|
||||
ConstructorSet::Slice { array_len, subtype_is_empty } => {
|
||||
let seen_slices = seen.iter().map(|c| c.as_slice().unwrap());
|
||||
let base_slice = Slice::new(array_len, VarLen(0, 0));
|
||||
for (seen, splitted_slice) in base_slice.split(seen_slices) {
|
||||
let ctor = Slice(splitted_slice);
|
||||
match seen {
|
||||
Presence::Unseen => missing.push(ctor),
|
||||
Presence::Seen => present.push(ctor),
|
||||
}
|
||||
}
|
||||
}
|
||||
ConstructorSet::SliceOfEmpty => {
|
||||
// This one is tricky because even though there's only one possible value of this
|
||||
// type (namely `[]`), slice patterns of all lengths are allowed, they're just
|
||||
// unreachable if length != 0.
|
||||
// We still gather the seen constructors in `present`, but the only slice that can
|
||||
// go in `missing` is `[]`.
|
||||
let seen_slices = seen.iter().map(|c| c.as_slice().unwrap());
|
||||
let base_slice = Slice::new(None, VarLen(0, 0));
|
||||
let base_slice = Slice::new(*array_len, VarLen(0, 0));
|
||||
for (seen, splitted_slice) in base_slice.split(seen_slices) {
|
||||
let ctor = Slice(splitted_slice);
|
||||
match seen {
|
||||
Presence::Seen => present.push(ctor),
|
||||
Presence::Unseen if splitted_slice.arity() == 0 => {
|
||||
missing.push(Slice(Slice::new(None, FixedLen(0))))
|
||||
Presence::Unseen => {
|
||||
if *subtype_is_empty && splitted_slice.arity() != 0 {
|
||||
// We have subpatterns of an empty type, so the constructor is
|
||||
// empty.
|
||||
missing_empty.push(ctor);
|
||||
} else {
|
||||
missing.push(ctor);
|
||||
}
|
||||
}
|
||||
Presence::Unseen => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1179,17 +1238,16 @@ impl ConstructorSet {
|
||||
present.extend(seen);
|
||||
missing.push(NonExhaustive);
|
||||
}
|
||||
// If `exhaustive_patterns` is disabled and our scrutinee is an empty 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.
|
||||
ConstructorSet::Uninhabited
|
||||
if !pcx.cx.tcx.features().exhaustive_patterns && !pcx.is_top_level =>
|
||||
{
|
||||
missing.push(NonExhaustive);
|
||||
ConstructorSet::NoConstructors => {
|
||||
if !pcx.is_top_level {
|
||||
missing_empty.push(NonExhaustive);
|
||||
}
|
||||
}
|
||||
ConstructorSet::Uninhabited => {}
|
||||
}
|
||||
|
||||
if !pcx.cx.tcx.features().exhaustive_patterns {
|
||||
missing.extend(missing_empty);
|
||||
}
|
||||
SplitConstructorSet { present, missing }
|
||||
}
|
||||
}
|
||||
@ -1263,7 +1321,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
|
||||
// `field.ty()` doesn't normalize after substituting.
|
||||
let ty = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
|
||||
let is_visible = adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx);
|
||||
let is_uninhabited = cx.is_uninhabited(ty);
|
||||
let is_uninhabited = cx.tcx.features().exhaustive_patterns && cx.is_uninhabited(ty);
|
||||
|
||||
if is_uninhabited && (!is_visible || is_non_exhaustive) {
|
||||
None
|
||||
|
@ -596,11 +596,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]`.
|
||||
|
Loading…
Reference in New Issue
Block a user