diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index b19ddf7a77d..fbf073d9423 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -590,6 +590,8 @@ enum Constructor<'tcx> { FixedLenSlice(u64), /// Slice patterns. Captures any array constructor of `length >= i + j`. VarLenSlice(u64, u64), + /// Fake extra constructor for enums that aren't allowed to be matched exhaustively. + NonExhaustive, } // Ignore spans when comparing, they don't carry semantic information as they are only for lints. @@ -597,6 +599,7 @@ impl<'tcx> std::cmp::PartialEq for Constructor<'tcx> { fn eq(&self, other: &Self) -> bool { match (self, other) { (Constructor::Single, Constructor::Single) => true, + (Constructor::NonExhaustive, Constructor::NonExhaustive) => true, (Constructor::Variant(a), Constructor::Variant(b)) => a == b, (Constructor::ConstantValue(a, _), Constructor::ConstantValue(b, _)) => a == b, ( @@ -771,6 +774,8 @@ impl<'tcx> Constructor<'tcx> { // ranges have been omitted. remaining_ctors } + // This constructor is never covered by anything else + NonExhaustive => vec![NonExhaustive], } } @@ -842,7 +847,7 @@ impl<'tcx> Constructor<'tcx> { } _ => bug!("bad slice pattern {:?} {:?}", self, ty), }, - ConstantValue(..) | ConstantRange(..) => vec![], + ConstantValue(..) | ConstantRange(..) | NonExhaustive => vec![], } } @@ -865,7 +870,7 @@ impl<'tcx> Constructor<'tcx> { }, FixedLenSlice(length) => *length, VarLenSlice(prefix, suffix) => prefix + suffix, - ConstantValue(..) | ConstantRange(..) => 0, + ConstantValue(..) | ConstantRange(..) | NonExhaustive => 0, } } @@ -932,6 +937,7 @@ impl<'tcx> Constructor<'tcx> { hi: ty::Const::from_bits(cx.tcx, hi, ty::ParamEnv::empty().and(ty)), end, }), + NonExhaustive => PatKind::Wild, }; Pat { ty, span: DUMMY_SP, kind: Box::new(pat) } @@ -1193,6 +1199,36 @@ fn all_constructors<'a, 'tcx>( } } }; + + // FIXME: currently the only way I know of something can + // be a privately-empty enum is when the exhaustive_patterns + // feature flag is not present, so this is only + // needed for that case. + let is_privately_empty = ctors.is_empty() && !cx.is_uninhabited(pcx.ty); + let is_declared_nonexhaustive = cx.is_non_exhaustive_enum(pcx.ty) && !cx.is_local(pcx.ty); + let is_non_exhaustive = is_privately_empty + || is_declared_nonexhaustive + || (pcx.ty.is_ptr_sized_integral() && !cx.tcx.features().precise_pointer_size_matching); + if is_non_exhaustive { + // If our scrutinee is *privately* an empty enum, we must treat it as though it had an + // "unknown" constructor (in that case, all other patterns obviously can't be variants) to + // avoid exposing its emptyness. See the `match_privately_empty` test for details. + // + // If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an additionnal + // "unknown" constructor. However there is no point in enumerating all possible variants, + // because the user can't actually match against them themselves. So we 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. + return vec![NonExhaustive]; + } + ctors } @@ -1591,9 +1627,6 @@ pub fn is_useful<'p, 'a, 'tcx>( let all_ctors = all_constructors(cx, pcx); debug!("all_ctors = {:#?}", all_ctors); - let is_privately_empty = all_ctors.is_empty() && !cx.is_uninhabited(pcx.ty); - let is_declared_nonexhaustive = cx.is_non_exhaustive_enum(pcx.ty) && !cx.is_local(pcx.ty); - // `missing_ctors` is the set of constructors from the same type as the // first column of `matrix` that are matched only by wildcard patterns // from the first column. @@ -1601,38 +1634,15 @@ pub fn is_useful<'p, 'a, 'tcx>( // Therefore, if there is some pattern that is unmatched by `matrix`, // it will still be unmatched if the first constructor is replaced by // any of the constructors in `missing_ctors` - // - // However, if our scrutinee is *privately* an empty enum, we - // must treat it as though it had an "unknown" constructor (in - // that case, all other patterns obviously can't be variants) - // to avoid exposing its emptyness. See the `match_privately_empty` - // test for details. - // - // FIXME: currently the only way I know of something can - // be a privately-empty enum is when the exhaustive_patterns - // feature flag is not present, so this is only - // needed for that case. - // Missing constructors are those that are not matched by any - // non-wildcard patterns in the current column. To determine if - // the set is empty, we can check that `.peek().is_none()`, so - // we only fully construct them on-demand, because they're rarely used and can be big. + // Missing constructors are those that are not matched by any non-wildcard patterns in the + // current column. We only fully construct them on-demand, because they're rarely used and + // can be big. let missing_ctors = MissingConstructors::new(cx.tcx, cx.param_env, all_ctors, used_ctors); - debug!( - "missing_ctors.empty()={:#?} is_privately_empty={:#?} is_declared_nonexhaustive={:#?}", - missing_ctors.is_empty(), - is_privately_empty, - is_declared_nonexhaustive - ); + debug!("missing_ctors.empty()={:#?}", missing_ctors.is_empty(),); - // For privately empty and non-exhaustive enums, we work as if there were an "extra" - // `_` constructor for the type, so we can never match over all constructors. - let is_non_exhaustive = is_privately_empty - || is_declared_nonexhaustive - || (pcx.ty.is_ptr_sized_integral() && !cx.tcx.features().precise_pointer_size_matching); - - if missing_ctors.is_empty() && !is_non_exhaustive { + if missing_ctors.is_empty() { let (all_ctors, _) = missing_ctors.into_inner(); split_grouped_constructors(cx.tcx, cx.param_env, pcx, all_ctors, matrix, DUMMY_SP, None) .into_iter() @@ -1661,26 +1671,9 @@ pub fn is_useful<'p, 'a, 'tcx>( // // we can report 3 witnesses: `S`, `E`, and `W`. // - // However, there are 2 cases where we don't want + // However, there is a case where we don't want // to do this and instead report a single `_` witness: - // - // 1) If the user is matching against a non-exhaustive - // enum, there is no point in enumerating all possible - // variants, because the user can't actually match - // against them themselves, 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 `_` as the witness (this is - // actually *required* if the user specified *all* - // IO errors, but is probably what we want in every - // case). - // - // 2) If the user didn't actually specify a constructor + // if the user didn't actually specify a constructor // in this arm, e.g., in // ``` // let x: (Direction, Direction, bool) = ...; @@ -1690,7 +1683,7 @@ pub fn is_useful<'p, 'a, 'tcx>( // `(, , true)` - we are // satisfied with `(_, _, true)`. In this case, // `used_ctors` is empty. - if is_non_exhaustive || missing_ctors.all_ctors_are_missing() { + if missing_ctors.all_ctors_are_missing() { // All constructors are unused. Add a wild pattern // rather than each individual constructor. usefulness.apply_wildcard(pcx.ty) @@ -2217,13 +2210,21 @@ fn patterns_for_variant<'p, 'a: 'p, 'tcx>( /// fields filled with wild patterns. fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( cx: &mut MatchCheckCtxt<'a, 'tcx>, - pat: &'q Pat<'tcx>, + mut pat: &'q Pat<'tcx>, constructor: &Constructor<'tcx>, ctor_wild_subpatterns: &[&'p Pat<'tcx>], ) -> Option> { + while let PatKind::AscribeUserType { ref subpattern, .. } = *pat.kind { + pat = subpattern; + } + + if let NonExhaustive = constructor { + // Only a wildcard pattern can match the special extra constructor + return if pat.is_wildcard() { Some(PatStack::default()) } else { None }; + } + let result = match *pat.kind { - PatKind::AscribeUserType { ref subpattern, .. } => PatStack::from_pattern(subpattern) - .specialize_constructor(cx, constructor, ctor_wild_subpatterns), + PatKind::AscribeUserType { .. } => bug!(), // Handled above PatKind::Binding { .. } | PatKind::Wild => { Some(PatStack::from_slice(ctor_wild_subpatterns))