about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2017-03-17 09:51:31 -0400
committerNiko Matsakis <niko@alum.mit.edu>2017-03-30 07:55:29 -0400
commit56847af9163284f928d5632a3d0d29399716414f (patch)
treec24655c7585a84a0e9f09db0d3552b11af72ed0c
parentdad314040764122fc78a7f8b70ffa9ef77bde3c5 (diff)
downloadrust-56847af9163284f928d5632a3d0d29399716414f.tar.gz
rust-56847af9163284f928d5632a3d0d29399716414f.zip
port the match code to use `CoerceMany`
`match { }` now (correctly?) indicates divergence, which results in more
unreachable warnings. We also avoid fallback to `!` if there is just one
arm (see new test: `match-unresolved-one-arm.rs`).
-rw-r--r--src/librustc_typeck/check/_match.rs90
-rw-r--r--src/libsyntax/parse/obsolete.rs1
-rw-r--r--src/test/compile-fail/match-no-arms-unreachable-after.rs22
-rw-r--r--src/test/compile-fail/match-unreachable-warning-with-diverging-discrim.rs16
-rw-r--r--src/test/compile-fail/match-unresolved-one-arm.rs17
5 files changed, 95 insertions, 51 deletions
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index feed5752cf8..f0d2598a0fb 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -16,6 +16,7 @@ use rustc::infer::type_variable::TypeVariableOrigin;
 use rustc::traits::ObligationCauseCode;
 use rustc::ty::{self, Ty, TypeFoldable, LvaluePreference};
 use check::{FnCtxt, Expectation, Diverges};
+use check::coercion::CoerceMany;
 use util::nodemap::FxHashMap;
 
 use std::collections::hash_map::Entry::{Occupied, Vacant};
@@ -414,6 +415,20 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             discrim_ty = self.next_ty_var(TypeVariableOrigin::TypeInference(discrim.span));
             self.check_expr_has_type(discrim, discrim_ty);
         };
+
+        // If the discriminant diverges, the match is pointless (e.g.,
+        // `match (return) { }`).
+        self.warn_if_unreachable(expr.id, expr.span, "expression");
+
+        // If there are no arms, that is a diverging match; a special case.
+        if arms.is_empty() {
+            self.diverges.set(self.diverges.get() | Diverges::Always);
+            return tcx.types.never;
+        }
+
+        // Otherwise, we have to union together the types that the
+        // arms produce and so forth.
+
         let discrim_diverges = self.diverges.get();
         self.diverges.set(Diverges::Maybe);
 
@@ -426,6 +441,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 self.check_pat(&p, discrim_ty);
                 all_pats_diverge &= self.diverges.get();
             }
+
             // As discussed with @eddyb, this is for disabling unreachable_code
             // warnings on patterns (they're now subsumed by unreachable_patterns
             // warnings).
@@ -444,20 +460,21 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // on any empty type and is therefore unreachable; should the flow
         // of execution reach it, we will panic, so bottom is an appropriate
         // type in that case)
-        let expected = expected.adjust_for_branches(self);
-        let mut result_ty = self.next_diverging_ty_var(
-            TypeVariableOrigin::DivergingBlockExpr(expr.span));
         let mut all_arms_diverge = Diverges::WarnedAlways;
-        let coerce_first = match expected {
-            // We don't coerce to `()` so that if the match expression is a
-            // statement it's branches can have any consistent type. That allows
-            // us to give better error messages (pointing to a usually better
-            // arm for inconsistent arms or to the whole match when a `()` type
-            // is required).
-            Expectation::ExpectHasType(ety) if ety != self.tcx.mk_nil() => {
-                ety
-            }
-            _ => result_ty
+
+        let expected = expected.adjust_for_branches(self);
+
+        let mut coercion = {
+            let coerce_first = match expected {
+                // We don't coerce to `()` so that if the match expression is a
+                // statement it's branches can have any consistent type. That allows
+                // us to give better error messages (pointing to a usually better
+                // arm for inconsistent arms or to the whole match when a `()` type
+                // is required).
+                Expectation::ExpectHasType(ety) if ety != self.tcx.mk_nil() => ety,
+                _ => self.next_ty_var(TypeVariableOrigin::MiscVariable(expr.span)),
+            };
+            CoerceMany::new(coerce_first)
         };
 
         for (i, (arm, pats_diverge)) in arms.iter().zip(all_arm_pats_diverge).enumerate() {
@@ -470,11 +487,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             let arm_ty = self.check_expr_with_expectation(&arm.body, expected);
             all_arms_diverge &= self.diverges.get();
 
-            if result_ty.references_error() || arm_ty.references_error() {
-                result_ty = tcx.types.err;
-                continue;
-            }
-
             // Handle the fallback arm of a desugared if-let like a missing else.
             let is_if_let_fallback = match match_src {
                 hir::MatchSource::IfLetDesugar { contains_else_clause: false } => {
@@ -483,47 +495,23 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 _ => false
             };
 
-            let cause = if is_if_let_fallback {
-                self.cause(expr.span, ObligationCauseCode::IfExpressionWithNoElse)
+            if is_if_let_fallback {
+                let cause = self.cause(expr.span, ObligationCauseCode::IfExpressionWithNoElse);
+                assert!(arm_ty.is_nil());
+                coercion.coerce_forced_unit(self, &cause);
             } else {
-                self.cause(expr.span, ObligationCauseCode::MatchExpressionArm {
+                let cause = self.cause(expr.span, ObligationCauseCode::MatchExpressionArm {
                     arm_span: arm.body.span,
                     source: match_src
-                })
-            };
-
-            let result = if is_if_let_fallback {
-                self.eq_types(true, &cause, arm_ty, result_ty)
-                    .map(|infer_ok| {
-                        self.register_infer_ok_obligations(infer_ok);
-                        arm_ty
-                    })
-            } else if i == 0 {
-                // Special-case the first arm, as it has no "previous expressions".
-                self.try_coerce(&arm.body, arm_ty, coerce_first)
-            } else {
-                let prev_arms = || arms[..i].iter().map(|arm| &*arm.body);
-                self.try_find_coercion_lub(&cause, prev_arms, result_ty, &arm.body, arm_ty)
-            };
-
-            result_ty = match result {
-                Ok(ty) => ty,
-                Err(e) => {
-                    let (expected, found) = if is_if_let_fallback {
-                        (arm_ty, result_ty)
-                    } else {
-                        (result_ty, arm_ty)
-                    };
-                    self.report_mismatched_types(&cause, expected, found, e).emit();
-                    self.tcx.types.err
-                }
-            };
+                });
+                coercion.coerce(self, &cause, &arm.body, arm_ty);
+            }
         }
 
         // We won't diverge unless the discriminant or all arms diverge.
         self.diverges.set(discrim_diverges | all_arms_diverge);
 
-        result_ty
+        coercion.complete(self)
     }
 
     fn check_pat_struct(&self,
diff --git a/src/libsyntax/parse/obsolete.rs b/src/libsyntax/parse/obsolete.rs
index a46a788ca08..d5baec675e4 100644
--- a/src/libsyntax/parse/obsolete.rs
+++ b/src/libsyntax/parse/obsolete.rs
@@ -36,6 +36,7 @@ pub trait ParserObsoleteMethods {
 impl<'a> ParserObsoleteMethods for parser::Parser<'a> {
     /// Reports an obsolete syntax non-fatal error.
     #[allow(unused_variables)]
+    #[allow(unreachable_code)]
     fn obsolete(&mut self, sp: Span, kind: ObsoleteSyntax) {
         let (kind_str, desc, error) = match kind {
             // Nothing here at the moment
diff --git a/src/test/compile-fail/match-no-arms-unreachable-after.rs b/src/test/compile-fail/match-no-arms-unreachable-after.rs
new file mode 100644
index 00000000000..db08f5e5e66
--- /dev/null
+++ b/src/test/compile-fail/match-no-arms-unreachable-after.rs
@@ -0,0 +1,22 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(warnings)]
+#![deny(unreachable_code)]
+
+enum Void { }
+
+fn foo(v: Void) {
+    match v { }
+    let x = 2; //~ ERROR unreachable
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/match-unreachable-warning-with-diverging-discrim.rs b/src/test/compile-fail/match-unreachable-warning-with-diverging-discrim.rs
new file mode 100644
index 00000000000..aae0f3135d8
--- /dev/null
+++ b/src/test/compile-fail/match-unreachable-warning-with-diverging-discrim.rs
@@ -0,0 +1,16 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(unused_parens)]
+#![deny(unreachable_code)]
+
+fn main() {
+    match (return) { } //~ ERROR unreachable expression
+}
diff --git a/src/test/compile-fail/match-unresolved-one-arm.rs b/src/test/compile-fail/match-unresolved-one-arm.rs
new file mode 100644
index 00000000000..ea0f8db99e8
--- /dev/null
+++ b/src/test/compile-fail/match-unresolved-one-arm.rs
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn foo<T>() -> T { panic!("Rocks for my pillow") }
+
+fn main() {
+    let x = match () { //~ ERROR type annotations needed
+        () => foo() // T here should be unresolved
+    };
+}