]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_const_eval/check_match.rs
use a more conservative inhabitableness rule
[rust.git] / src / librustc_const_eval / check_match.rs
index 52f332a30c03b81f591af3bd4e099a6751eac8f1..9b30946c0bebbc1d834cd9cdd045352dbd76be28 100644 (file)
@@ -42,7 +42,7 @@ struct OuterVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx> }
 
 impl<'a, 'tcx> Visitor<'tcx> for OuterVisitor<'a, 'tcx> {
     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
-        NestedVisitorMap::OnlyBodies(&self.tcx.map)
+        NestedVisitorMap::OnlyBodies(&self.tcx.hir)
     }
 
     fn visit_fn(&mut self, fk: FnKind<'tcx>, fd: &'tcx hir::FnDecl,
@@ -53,7 +53,7 @@ fn visit_fn(&mut self, fk: FnKind<'tcx>, fd: &'tcx hir::FnDecl,
             tcx: self.tcx,
             tables: self.tcx.body_tables(b),
             param_env: &ty::ParameterEnvironment::for_item(self.tcx, id)
-        }.visit_body(self.tcx.map.body(b));
+        }.visit_body(self.tcx.hir.body(b));
     }
 }
 
@@ -69,7 +69,7 @@ fn create_e0004<'a>(sess: &'a Session, sp: Span, error_message: String) -> Diagn
 
 struct MatchVisitor<'a, 'tcx: 'a> {
     tcx: TyCtxt<'a, 'tcx, 'tcx>,
-    tables: &'a ty::Tables<'tcx>,
+    tables: &'a ty::TypeckTables<'tcx>,
     param_env: &'a ty::ParameterEnvironment<'tcx>
 }
 
@@ -152,7 +152,7 @@ fn check_match(
             }
         }
 
-        let module = self.tcx.map.local_def_id(self.tcx.map.get_module_parent(scrut.id));
+        let module = self.tcx.hir.local_def_id(self.tcx.hir.get_module_parent(scrut.id));
         MatchCheckCtxt::create_and_enter(self.tcx, module, |ref mut cx| {
             let mut have_errors = false;
 
@@ -177,6 +177,31 @@ fn check_match(
             // Fourth, check for unreachable arms.
             check_arms(cx, &inlined_arms, source);
 
+            // Then, if the match has no arms, check whether the scrutinee
+            // is uninhabited.
+            let pat_ty = self.tables.node_id_to_type(scrut.id);
+            let module = self.tcx.hir.local_def_id(self.tcx.hir.get_module_parent(scrut.id));
+            if inlined_arms.is_empty() {
+                let scrutinee_is_uninhabited = if self.tcx.sess.features.borrow().never_type {
+                    pat_ty.is_uninhabited_from(module, self.tcx)
+                } else {
+                    self.conservative_is_uninhabited(pat_ty)
+                };
+                if !scrutinee_is_uninhabited {
+                    // We know the type is inhabited, so this must be wrong
+                    let mut err = create_e0004(self.tcx.sess, scrut.span,
+                                               format!("non-exhaustive patterns: type {} \
+                                                        is non-empty",
+                                                       pat_ty));
+                    span_help!(&mut err, scrut.span,
+                               "Please ensure that all possible cases are being handled; \
+                                possibly adding wildcards or more match arms.");
+                    err.emit();
+                }
+                // If the type *is* uninhabited, it's vacuously exhaustive
+                return;
+            }
+
             let matrix: Matrix = inlined_arms
                 .iter()
                 .filter(|&&(_, guard)| guard.is_none())
@@ -188,6 +213,15 @@ fn check_match(
         })
     }
 
+    fn conservative_is_uninhabited(&self, scrutinee_ty: Ty<'tcx>) -> bool {
+        // "rustc-1.0-style" uncontentious uninhabitableness check
+        match scrutinee_ty.sty {
+            ty::TyNever => true,
+            ty::TyAdt(def, _) => def.variants.is_empty(),
+            _ => false
+        }
+    }
+
     fn check_irrefutable(&self, pat: &Pat, is_fn_arg: bool) {
         let origin = if is_fn_arg {
             "function argument"
@@ -195,7 +229,7 @@ fn check_irrefutable(&self, pat: &Pat, is_fn_arg: bool) {
             "local binding"
         };
 
-        let module = self.tcx.map.local_def_id(self.tcx.map.get_module_parent(pat.id));
+        let module = self.tcx.hir.local_def_id(self.tcx.hir.get_module_parent(pat.id));
         MatchCheckCtxt::create_and_enter(self.tcx, module, |ref mut cx| {
             let mut patcx = PatternContext::new(self.tcx, self.tables);
             let pattern = patcx.lower_pattern(pat);
@@ -273,7 +307,7 @@ fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
     let mut seen = Matrix::empty();
     let mut catchall = None;
     let mut printed_if_let_err = false;
-    for &(ref pats, guard) in arms {
+    for (arm_index, &(ref pats, guard)) in arms.iter().enumerate() {
         for &(pat, hir_pat) in pats {
             let v = vec![pat];
 
@@ -302,10 +336,27 @@ fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
                             let &(ref first_arm_pats, _) = &arms[0];
                             let first_pat = &first_arm_pats[0];
                             let span = first_pat.0.span;
-                            struct_span_err!(cx.tcx.sess, span, E0165,
-                                             "irrefutable while-let pattern")
-                                .span_label(span, &format!("irrefutable pattern"))
-                                .emit();
+
+                            // check which arm we're on.
+                            match arm_index {
+                                // The arm with the user-specified pattern.
+                                0 => {
+                                    let mut diagnostic = Diagnostic::new(Level::Warning,
+                                                                         "unreachable pattern");
+                                    diagnostic.set_span(pat.span);
+                                    cx.tcx.sess.add_lint_diagnostic(
+                                            lint::builtin::UNREACHABLE_PATTERNS,
+                                            hir_pat.id, diagnostic);
+                                },
+                                // The arm with the wildcard pattern.
+                                1 => {
+                                    struct_span_err!(cx.tcx.sess, span, E0165,
+                                                     "irrefutable while-let pattern")
+                                        .span_label(span, &format!("irrefutable pattern"))
+                                        .emit();
+                                },
+                                _ => bug!(),
+                            }
                         },
 
                         hir::MatchSource::ForLoopDesugar |