]> git.lizzy.rs Git - rust.git/commitdiff
Introduce Constructor::NonExhaustive
authorNadrieril <nadrieril+git@gmail.com>
Tue, 12 Nov 2019 12:44:00 +0000 (12:44 +0000)
committerNadrieril <nadrieril+git@gmail.com>
Tue, 12 Nov 2019 15:02:00 +0000 (15:02 +0000)
It counts as an extra constructor for types that are not allowed to be
matched exhaustively.

src/librustc_mir/hair/pattern/_match.rs

index b19ddf7a77db8a180646014792ebf0566407ce23..fbf073d9423d931262e6f1f4b28b72ad581482af 100644 (file)
@@ -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 @@ fn subtract_ctors(
                 // ranges have been omitted.
                 remaining_ctors
             }
+            // This constructor is never covered by anything else
+            NonExhaustive => vec![NonExhaustive],
         }
     }
 
@@ -842,7 +847,7 @@ fn wildcard_subpatterns<'a>(
                 }
                 _ => bug!("bad slice pattern {:?} {:?}", self, ty),
             },
-            ConstantValue(..) | ConstantRange(..) => vec![],
+            ConstantValue(..) | ConstantRange(..) | NonExhaustive => vec![],
         }
     }
 
@@ -865,7 +870,7 @@ fn arity<'a>(&self, cx: &MatchCheckCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> u64 {
             },
             FixedLenSlice(length) => *length,
             VarLenSlice(prefix, suffix) => prefix + suffix,
-            ConstantValue(..) | ConstantRange(..) => 0,
+            ConstantValue(..) | ConstantRange(..) | NonExhaustive => 0,
         }
     }
 
@@ -932,6 +937,7 @@ fn apply<'a>(
                 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.
-        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
-        );
+        // 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);
 
-        // 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);
+        debug!("missing_ctors.empty()={:#?}", missing_ctors.is_empty(),);
 
-        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>(
             // `(<direction-1>, <direction-2>, 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<PatStack<'p, 'tcx>> {
+    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))