about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNadrieril <nadrieril+git@gmail.com>2019-11-12 14:51:59 +0000
committerNadrieril <nadrieril+git@gmail.com>2019-11-12 15:02:00 +0000
commite398d897b09f69bc4b5a1ab531db1c8742001bff (patch)
tree297353d9f1049043fe6bc674c3ef423347216bb0
parent357d53c4ce6eed4d93fe1ed576613687d34d10b2 (diff)
downloadrust-e398d897b09f69bc4b5a1ab531db1c8742001bff.tar.gz
rust-e398d897b09f69bc4b5a1ab531db1c8742001bff.zip
Move NonExhaustive checks to the relevant match branches
-rw-r--r--src/librustc_mir/hair/pattern/_match.rs105
1 files changed, 54 insertions, 51 deletions
diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs
index fbf073d9423..8e574cc961b 100644
--- a/src/librustc_mir/hair/pattern/_match.rs
+++ b/src/librustc_mir/hair/pattern/_match.rs
@@ -560,13 +560,6 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
         }
     }
 
-    fn is_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool {
-        match ty.kind {
-            ty::Adt(adt_def, ..) => adt_def.is_variant_list_non_exhaustive(),
-            _ => false,
-        }
-    }
-
     fn is_local(&self, ty: Ty<'tcx>) -> bool {
         match ty.kind {
             ty::Adt(adt_def, ..) => adt_def.did.is_local(),
@@ -1133,7 +1126,7 @@ fn all_constructors<'a, 'tcx>(
     pcx: PatCtxt<'tcx>,
 ) -> Vec<Constructor<'tcx>> {
     debug!("all_constructors({:?})", pcx.ty);
-    let ctors = match pcx.ty.kind {
+    match pcx.ty.kind {
         ty::Bool => [true, false]
             .iter()
             .map(|&b| ConstantValue(ty::Const::from_bool(cx.tcx, b), pcx.span))
@@ -1150,17 +1143,49 @@ fn all_constructors<'a, 'tcx>(
                 vec![VarLenSlice(0, 0)]
             }
         }
-        ty::Adt(def, substs) if def.is_enum() => def
-            .variants
-            .iter()
-            .filter(|v| {
-                !cx.tcx.features().exhaustive_patterns
-                    || !v
-                        .uninhabited_from(cx.tcx, substs, def.adt_kind())
-                        .contains(cx.tcx, cx.module)
-            })
-            .map(|v| Variant(v.def_id))
-            .collect(),
+        ty::Adt(def, substs) if def.is_enum() => {
+            let ctors: Vec<_> = def
+                .variants
+                .iter()
+                .filter(|v| {
+                    !cx.tcx.features().exhaustive_patterns
+                        || !v
+                            .uninhabited_from(cx.tcx, substs, def.adt_kind())
+                            .contains(cx.tcx, cx.module)
+                })
+                .map(|v| Variant(v.def_id))
+                .collect();
+
+            // 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.
+            let is_privately_empty = ctors.is_empty() && !cx.is_uninhabited(pcx.ty);
+            // If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an
+            // additionnal "unknown" constructor.
+            let is_declared_nonexhaustive =
+                def.is_variant_list_non_exhaustive() && !cx.is_local(pcx.ty);
+
+            if is_privately_empty || is_declared_nonexhaustive {
+                // 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.
+                vec![NonExhaustive]
+            } else {
+                ctors
+            }
+        }
         ty::Char => {
             vec![
                 // The valid Unicode Scalar Value ranges.
@@ -1180,6 +1205,15 @@ fn all_constructors<'a, 'tcx>(
                 ),
             ]
         }
+        ty::Int(_) | ty::Uint(_)
+            if pcx.ty.is_ptr_sized_integral()
+                && !cx.tcx.features().precise_pointer_size_matching =>
+        {
+            // `usize`/`isize` are not allowed to be matched exhaustively unless the
+            // `precise_pointer_size_matching` feature is enabled. So we treat those types like
+            // `#[non_exhaustive]` enums by returning a special unmatcheable constructor.
+            vec![NonExhaustive]
+        }
         ty::Int(ity) => {
             let bits = Integer::from_attr(&cx.tcx, SignedInt(ity)).size().bits() as u128;
             let min = 1u128 << (bits - 1);
@@ -1198,38 +1232,7 @@ fn all_constructors<'a, 'tcx>(
                 vec![Single]
             }
         }
-    };
-
-    // 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
+    }
 }
 
 /// An inclusive interval, used for precise integer exhaustiveness checking.