Skip to content

Commit 357d53c

Browse files
committed
Introduce Constructor::NonExhaustive
It counts as an extra constructor for types that are not allowed to be matched exhaustively.
1 parent eb99c73 commit 357d53c

File tree

1 file changed

+58
-57
lines changed

1 file changed

+58
-57
lines changed

src/librustc_mir/hair/pattern/_match.rs

Lines changed: 58 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -590,13 +590,16 @@ enum Constructor<'tcx> {
590590
FixedLenSlice(u64),
591591
/// Slice patterns. Captures any array constructor of `length >= i + j`.
592592
VarLenSlice(u64, u64),
593+
/// Fake extra constructor for enums that aren't allowed to be matched exhaustively.
594+
NonExhaustive,
593595
}
594596

595597
// Ignore spans when comparing, they don't carry semantic information as they are only for lints.
596598
impl<'tcx> std::cmp::PartialEq for Constructor<'tcx> {
597599
fn eq(&self, other: &Self) -> bool {
598600
match (self, other) {
599601
(Constructor::Single, Constructor::Single) => true,
602+
(Constructor::NonExhaustive, Constructor::NonExhaustive) => true,
600603
(Constructor::Variant(a), Constructor::Variant(b)) => a == b,
601604
(Constructor::ConstantValue(a, _), Constructor::ConstantValue(b, _)) => a == b,
602605
(
@@ -771,6 +774,8 @@ impl<'tcx> Constructor<'tcx> {
771774
// ranges have been omitted.
772775
remaining_ctors
773776
}
777+
// This constructor is never covered by anything else
778+
NonExhaustive => vec![NonExhaustive],
774779
}
775780
}
776781

@@ -842,7 +847,7 @@ impl<'tcx> Constructor<'tcx> {
842847
}
843848
_ => bug!("bad slice pattern {:?} {:?}", self, ty),
844849
},
845-
ConstantValue(..) | ConstantRange(..) => vec![],
850+
ConstantValue(..) | ConstantRange(..) | NonExhaustive => vec![],
846851
}
847852
}
848853

@@ -865,7 +870,7 @@ impl<'tcx> Constructor<'tcx> {
865870
},
866871
FixedLenSlice(length) => *length,
867872
VarLenSlice(prefix, suffix) => prefix + suffix,
868-
ConstantValue(..) | ConstantRange(..) => 0,
873+
ConstantValue(..) | ConstantRange(..) | NonExhaustive => 0,
869874
}
870875
}
871876

@@ -932,6 +937,7 @@ impl<'tcx> Constructor<'tcx> {
932937
hi: ty::Const::from_bits(cx.tcx, hi, ty::ParamEnv::empty().and(ty)),
933938
end,
934939
}),
940+
NonExhaustive => PatKind::Wild,
935941
};
936942

937943
Pat { ty, span: DUMMY_SP, kind: Box::new(pat) }
@@ -1193,6 +1199,36 @@ fn all_constructors<'a, 'tcx>(
11931199
}
11941200
}
11951201
};
1202+
1203+
// FIXME: currently the only way I know of something can
1204+
// be a privately-empty enum is when the exhaustive_patterns
1205+
// feature flag is not present, so this is only
1206+
// needed for that case.
1207+
let is_privately_empty = ctors.is_empty() && !cx.is_uninhabited(pcx.ty);
1208+
let is_declared_nonexhaustive = cx.is_non_exhaustive_enum(pcx.ty) && !cx.is_local(pcx.ty);
1209+
let is_non_exhaustive = is_privately_empty
1210+
|| is_declared_nonexhaustive
1211+
|| (pcx.ty.is_ptr_sized_integral() && !cx.tcx.features().precise_pointer_size_matching);
1212+
if is_non_exhaustive {
1213+
// If our scrutinee is *privately* an empty enum, we must treat it as though it had an
1214+
// "unknown" constructor (in that case, all other patterns obviously can't be variants) to
1215+
// avoid exposing its emptyness. See the `match_privately_empty` test for details.
1216+
//
1217+
// If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an additionnal
1218+
// "unknown" constructor. However there is no point in enumerating all possible variants,
1219+
// because the user can't actually match against them themselves. So we return only the
1220+
// fictitious constructor.
1221+
// E.g., in an example like:
1222+
// ```
1223+
// let err: io::ErrorKind = ...;
1224+
// match err {
1225+
// io::ErrorKind::NotFound => {},
1226+
// }
1227+
// ```
1228+
// we don't want to show every possible IO error, but instead have only `_` as the witness.
1229+
return vec![NonExhaustive];
1230+
}
1231+
11961232
ctors
11971233
}
11981234

@@ -1591,48 +1627,22 @@ pub fn is_useful<'p, 'a, 'tcx>(
15911627
let all_ctors = all_constructors(cx, pcx);
15921628
debug!("all_ctors = {:#?}", all_ctors);
15931629

1594-
let is_privately_empty = all_ctors.is_empty() && !cx.is_uninhabited(pcx.ty);
1595-
let is_declared_nonexhaustive = cx.is_non_exhaustive_enum(pcx.ty) && !cx.is_local(pcx.ty);
1596-
15971630
// `missing_ctors` is the set of constructors from the same type as the
15981631
// first column of `matrix` that are matched only by wildcard patterns
15991632
// from the first column.
16001633
//
16011634
// Therefore, if there is some pattern that is unmatched by `matrix`,
16021635
// it will still be unmatched if the first constructor is replaced by
16031636
// any of the constructors in `missing_ctors`
1604-
//
1605-
// However, if our scrutinee is *privately* an empty enum, we
1606-
// must treat it as though it had an "unknown" constructor (in
1607-
// that case, all other patterns obviously can't be variants)
1608-
// to avoid exposing its emptyness. See the `match_privately_empty`
1609-
// test for details.
1610-
//
1611-
// FIXME: currently the only way I know of something can
1612-
// be a privately-empty enum is when the exhaustive_patterns
1613-
// feature flag is not present, so this is only
1614-
// needed for that case.
1615-
1616-
// Missing constructors are those that are not matched by any
1617-
// non-wildcard patterns in the current column. To determine if
1618-
// the set is empty, we can check that `.peek().is_none()`, so
1619-
// we only fully construct them on-demand, because they're rarely used and can be big.
1620-
let missing_ctors = MissingConstructors::new(cx.tcx, cx.param_env, all_ctors, used_ctors);
16211637

1622-
debug!(
1623-
"missing_ctors.empty()={:#?} is_privately_empty={:#?} is_declared_nonexhaustive={:#?}",
1624-
missing_ctors.is_empty(),
1625-
is_privately_empty,
1626-
is_declared_nonexhaustive
1627-
);
1638+
// Missing constructors are those that are not matched by any non-wildcard patterns in the
1639+
// current column. We only fully construct them on-demand, because they're rarely used and
1640+
// can be big.
1641+
let missing_ctors = MissingConstructors::new(cx.tcx, cx.param_env, all_ctors, used_ctors);
16281642

1629-
// For privately empty and non-exhaustive enums, we work as if there were an "extra"
1630-
// `_` constructor for the type, so we can never match over all constructors.
1631-
let is_non_exhaustive = is_privately_empty
1632-
|| is_declared_nonexhaustive
1633-
|| (pcx.ty.is_ptr_sized_integral() && !cx.tcx.features().precise_pointer_size_matching);
1643+
debug!("missing_ctors.empty()={:#?}", missing_ctors.is_empty(),);
16341644

1635-
if missing_ctors.is_empty() && !is_non_exhaustive {
1645+
if missing_ctors.is_empty() {
16361646
let (all_ctors, _) = missing_ctors.into_inner();
16371647
split_grouped_constructors(cx.tcx, cx.param_env, pcx, all_ctors, matrix, DUMMY_SP, None)
16381648
.into_iter()
@@ -1661,26 +1671,9 @@ pub fn is_useful<'p, 'a, 'tcx>(
16611671
//
16621672
// we can report 3 witnesses: `S`, `E`, and `W`.
16631673
//
1664-
// However, there are 2 cases where we don't want
1674+
// However, there is a case where we don't want
16651675
// to do this and instead report a single `_` witness:
1666-
//
1667-
// 1) If the user is matching against a non-exhaustive
1668-
// enum, there is no point in enumerating all possible
1669-
// variants, because the user can't actually match
1670-
// against them themselves, e.g., in an example like:
1671-
// ```
1672-
// let err: io::ErrorKind = ...;
1673-
// match err {
1674-
// io::ErrorKind::NotFound => {},
1675-
// }
1676-
// ```
1677-
// we don't want to show every possible IO error,
1678-
// but instead have `_` as the witness (this is
1679-
// actually *required* if the user specified *all*
1680-
// IO errors, but is probably what we want in every
1681-
// case).
1682-
//
1683-
// 2) If the user didn't actually specify a constructor
1676+
// if the user didn't actually specify a constructor
16841677
// in this arm, e.g., in
16851678
// ```
16861679
// let x: (Direction, Direction, bool) = ...;
@@ -1690,7 +1683,7 @@ pub fn is_useful<'p, 'a, 'tcx>(
16901683
// `(<direction-1>, <direction-2>, true)` - we are
16911684
// satisfied with `(_, _, true)`. In this case,
16921685
// `used_ctors` is empty.
1693-
if is_non_exhaustive || missing_ctors.all_ctors_are_missing() {
1686+
if missing_ctors.all_ctors_are_missing() {
16941687
// All constructors are unused. Add a wild pattern
16951688
// rather than each individual constructor.
16961689
usefulness.apply_wildcard(pcx.ty)
@@ -2217,13 +2210,21 @@ fn patterns_for_variant<'p, 'a: 'p, 'tcx>(
22172210
/// fields filled with wild patterns.
22182211
fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>(
22192212
cx: &mut MatchCheckCtxt<'a, 'tcx>,
2220-
pat: &'q Pat<'tcx>,
2213+
mut pat: &'q Pat<'tcx>,
22212214
constructor: &Constructor<'tcx>,
22222215
ctor_wild_subpatterns: &[&'p Pat<'tcx>],
22232216
) -> Option<PatStack<'p, 'tcx>> {
2217+
while let PatKind::AscribeUserType { ref subpattern, .. } = *pat.kind {
2218+
pat = subpattern;
2219+
}
2220+
2221+
if let NonExhaustive = constructor {
2222+
// Only a wildcard pattern can match the special extra constructor
2223+
return if pat.is_wildcard() { Some(PatStack::default()) } else { None };
2224+
}
2225+
22242226
let result = match *pat.kind {
2225-
PatKind::AscribeUserType { ref subpattern, .. } => PatStack::from_pattern(subpattern)
2226-
.specialize_constructor(cx, constructor, ctor_wild_subpatterns),
2227+
PatKind::AscribeUserType { .. } => bug!(), // Handled above
22272228

22282229
PatKind::Binding { .. } | PatKind::Wild => {
22292230
Some(PatStack::from_slice(ctor_wild_subpatterns))

0 commit comments

Comments
 (0)