about summary refs log tree commit diff
path: root/src/librustc_mir
diff options
context:
space:
mode:
authorDavid Wood <david@davidtw.co>2019-10-14 18:55:32 +0100
committerDavid Wood <david@davidtw.co>2019-10-23 22:10:58 +0100
commit7ffbd62445b9706325d01ec8be8e70c41404fe0d (patch)
treed968523abcb2a40c5dc6cc34f694d3bd999316c7 /src/librustc_mir
parent4a8c5b20c7772bc5342b83d4b0696ea216ef75a7 (diff)
downloadrust-7ffbd62445b9706325d01ec8be8e70c41404fe0d.tar.gz
rust-7ffbd62445b9706325d01ec8be8e70c41404fe0d.zip
ignore uninhabited non-exhaustive variant fields
This commit modifies the uninhabitedness checking so that the fields of
a non-exhaustive variant (which is not local) are ignored if they are
uninhabited. This is an improvement over the previous behaviour which
considered all non-local non-exhaustive variants useful because
unreachable patterns are now detected.

Signed-off-by: David Wood <david@davidtw.co>
Diffstat (limited to 'src/librustc_mir')
-rw-r--r--src/librustc_mir/hair/pattern/_match.rs88
1 files changed, 39 insertions, 49 deletions
diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs
index dc6d4b27886..907c84b6f8c 100644
--- a/src/librustc_mir/hair/pattern/_match.rs
+++ b/src/librustc_mir/hair/pattern/_match.rs
@@ -394,16 +394,6 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
         }
     }
 
-    fn is_non_exhaustive_variant<'p>(&self, pattern: &'p Pat<'tcx>) -> bool {
-        match *pattern.kind {
-            PatKind::Variant { adt_def, variant_index, .. } => {
-                let ref variant = adt_def.variants[variant_index];
-                variant.is_field_list_non_exhaustive()
-            }
-            _ => false,
-        }
-    }
-
     fn is_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool {
         match ty.kind {
             ty::Adt(adt_def, ..) => adt_def.is_variant_list_non_exhaustive(),
@@ -1252,19 +1242,12 @@ pub fn is_useful<'p, 'a, 'tcx>(
     debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v[0]);
 
     if let Some(constructors) = pat_constructors(cx, v[0], pcx) {
-        let is_declared_nonexhaustive = cx.is_non_exhaustive_variant(v[0]) && !cx.is_local(pcx.ty);
-        debug!("is_useful - expanding constructors: {:#?}, is_declared_nonexhaustive: {:?}",
-               constructors, is_declared_nonexhaustive);
-
-        if is_declared_nonexhaustive {
-            Useful
-        } else {
-            split_grouped_constructors(
-                cx.tcx, cx.param_env, constructors, matrix, pcx.ty, pcx.span, Some(hir_id),
-            ).into_iter().map(|c|
-                is_useful_specialized(cx, matrix, v, c, pcx.ty, witness, hir_id)
-            ).find(|result| result.is_useful()).unwrap_or(NotUseful)
-        }
+        debug!("is_useful - expanding constructors: {:#?}", constructors);
+        split_grouped_constructors(
+            cx.tcx, cx.param_env, constructors, matrix, pcx.ty, pcx.span, Some(hir_id),
+        ).into_iter().map(|c|
+            is_useful_specialized(cx, matrix, v, c, pcx.ty, witness, hir_id)
+        ).find(|result| result.is_useful()).unwrap_or(NotUseful)
     } else {
         debug!("is_useful - expanding wildcard");
 
@@ -1548,27 +1531,30 @@ fn constructor_sub_pattern_tys<'a, 'tcx>(
                 // Use T as the sub pattern type of Box<T>.
                 vec![substs.type_at(0)]
             } else {
-                adt.variants[ctor.variant_index_for_adt(cx, adt)].fields.iter().map(|field| {
+                let variant = &adt.variants[ctor.variant_index_for_adt(cx, adt)];
+                let is_non_exhaustive = variant.is_field_list_non_exhaustive() && !cx.is_local(ty);
+                variant.fields.iter().map(|field| {
                     let is_visible = adt.is_enum()
                         || field.vis.is_accessible_from(cx.module, cx.tcx);
-                    if is_visible {
-                        let ty = field.ty(cx.tcx, substs);
-                        match ty.kind {
-                            // If the field type returned is an array of an unknown
-                            // size return an TyErr.
-                            ty::Array(_, len)
-                                if len.try_eval_usize(cx.tcx, cx.param_env).is_none() =>
-                                cx.tcx.types.err,
-                            _ => ty,
-                        }
-                    } else {
-                        // Treat all non-visible fields as TyErr. They
-                        // can't appear in any other pattern from
-                        // this match (because they are private),
-                        // so their type does not matter - but
-                        // we don't want to know they are
-                        // uninhabited.
-                        cx.tcx.types.err
+                    let is_uninhabited = cx.is_uninhabited(field.ty(cx.tcx, substs));
+                    match (is_visible, is_non_exhaustive, is_uninhabited) {
+                        // Treat all uninhabited types in non-exhaustive variants as `TyErr`.
+                        (_, true, true) => cx.tcx.types.err,
+                        // Treat all non-visible fields as `TyErr`. They can't appear in any
+                        // other pattern from this match (because they are private), so their
+                        // type does not matter - but we don't want to know they are uninhabited.
+                        (false, ..) => cx.tcx.types.err,
+                        (true, ..) => {
+                            let ty = field.ty(cx.tcx, substs);
+                            match ty.kind {
+                                // If the field type returned is an array of an unknown
+                                // size return an TyErr.
+                                ty::Array(_, len)
+                                    if len.try_eval_usize(cx.tcx, cx.param_env).is_none() =>
+                                    cx.tcx.types.err,
+                                _ => ty,
+                            }
+                        },
                     }
                 }).collect()
             }
@@ -1874,15 +1860,18 @@ fn constructor_covered_by_range<'tcx>(
     }
 }
 
-fn patterns_for_variant<'p, 'tcx>(
+fn patterns_for_variant<'p, 'a: 'p, 'tcx>(
+    cx: &mut MatchCheckCtxt<'a, 'tcx>,
     subpatterns: &'p [FieldPat<'tcx>],
-    wild_patterns: &[&'p Pat<'tcx>])
-    -> SmallVec<[&'p Pat<'tcx>; 2]>
-{
+    wild_patterns: &[&'p Pat<'tcx>],
+    is_non_exhaustive: bool,
+) -> SmallVec<[&'p Pat<'tcx>; 2]> {
     let mut result = SmallVec::from_slice(wild_patterns);
 
     for subpat in subpatterns {
-        result[subpat.field.index()] = &subpat.pattern;
+        if !is_non_exhaustive || !cx.is_uninhabited(subpat.pattern.ty) {
+            result[subpat.field.index()] = &subpat.pattern;
+        }
     }
 
     debug!("patterns_for_variant({:#?}, {:#?}) = {:#?}", subpatterns, wild_patterns, result);
@@ -1916,13 +1905,14 @@ fn specialize<'p, 'a: 'p, 'tcx>(
 
         PatKind::Variant { adt_def, variant_index, ref subpatterns, .. } => {
             let ref variant = adt_def.variants[variant_index];
+            let is_non_exhaustive = variant.is_field_list_non_exhaustive() && !cx.is_local(pat.ty);
             Some(Variant(variant.def_id))
                 .filter(|variant_constructor| variant_constructor == constructor)
-                .map(|_| patterns_for_variant(subpatterns, wild_patterns))
+                .map(|_| patterns_for_variant(cx, subpatterns, wild_patterns, is_non_exhaustive))
         }
 
         PatKind::Leaf { ref subpatterns } => {
-            Some(patterns_for_variant(subpatterns, wild_patterns))
+            Some(patterns_for_variant(cx, subpatterns, wild_patterns, false))
         }
 
         PatKind::Deref { ref subpattern } => {