about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/infer/error_reporting/mod.rs33
-rw-r--r--src/librustc/traits/mod.rs3
-rw-r--r--src/librustc/traits/structural_impls.rs17
-rw-r--r--src/librustc_typeck/check/_match.rs29
-rw-r--r--src/test/ui/if/if-let-arm-types.rs9
-rw-r--r--src/test/ui/if/if-let-arm-types.stderr22
-rw-r--r--src/test/ui/issues/issue-11319.rs10
-rw-r--r--src/test/ui/issues/issue-11319.stderr18
-rw-r--r--src/test/ui/issues/issue-17728.rs3
-rw-r--r--src/test/ui/issues/issue-17728.stderr10
-rw-r--r--src/test/ui/issues/issue-24036.rs2
-rw-r--r--src/test/ui/issues/issue-24036.stderr14
-rw-r--r--src/test/ui/match/match-type-err-first-arm.rs45
-rw-r--r--src/test/ui/match/match-type-err-first-arm.stderr53
14 files changed, 206 insertions, 62 deletions
diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs
index ff4e520d8e0..705d9c4cf93 100644
--- a/src/librustc/infer/error_reporting/mod.rs
+++ b/src/librustc/infer/error_reporting/mod.rs
@@ -508,22 +508,31 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                     }
                 }
             }
-            ObligationCauseCode::MatchExpressionArm { arm_span, source } => match source {
+            ObligationCauseCode::MatchExpressionArm {
+                source,
+                ref prior_arms,
+                last_ty,
+                ..
+            } => match source {
                 hir::MatchSource::IfLetDesugar { .. } => {
-                    let msg = "`if let` arm with an incompatible type";
-                    if self.tcx.sess.source_map().is_multiline(arm_span) {
-                        err.span_note(arm_span, msg);
-                    } else {
-                        err.span_label(arm_span, msg);
-                    }
+                    let msg = "`if let` arms have incompatible types";
+                    err.span_label(cause.span, msg);
                 }
                 hir::MatchSource::TryDesugar => {}
                 _ => {
-                    let msg = "match arm with an incompatible type";
-                    if self.tcx.sess.source_map().is_multiline(arm_span) {
-                        err.span_note(arm_span, msg);
-                    } else {
-                        err.span_label(arm_span, msg);
+                    let msg = "`match` arms have incompatible types";
+                    err.span_label(cause.span, msg);
+                    if prior_arms.len() <= 4 {
+                        for sp in prior_arms {
+                            err.span_label(*sp, format!(
+                                "this is found to be of type `{}`",
+                                last_ty,
+                            ));
+                        }
+                    } else if let Some(sp) = prior_arms.last() {
+                        err.span_label(*sp, format!(
+                            "this and all prior arms are found to be of type `{}`", last_ty,
+                        ));
                     }
                 }
             },
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index 459ff4db9e9..99d1e32d523 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -145,6 +145,7 @@ impl<'tcx> ObligationCause<'tcx> {
             ObligationCauseCode::StartFunctionType => {
                 tcx.sess.source_map().def_span(self.span)
             }
+            ObligationCauseCode::MatchExpressionArm { arm_span, .. } => arm_span,
             _ => self.span,
         }
     }
@@ -223,6 +224,8 @@ pub enum ObligationCauseCode<'tcx> {
     MatchExpressionArm {
         arm_span: Span,
         source: hir::MatchSource,
+        prior_arms: Vec<Span>,
+        last_ty: Ty<'tcx>,
     },
 
     /// Computing common supertype in the pattern guard for the arms of a match expression
diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs
index c5cc9e8b401..b5be1777fa0 100644
--- a/src/librustc/traits/structural_impls.rs
+++ b/src/librustc/traits/structural_impls.rs
@@ -513,10 +513,21 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
                 trait_item_def_id,
             }),
             super::ExprAssignable => Some(super::ExprAssignable),
-            super::MatchExpressionArm { arm_span, source } => Some(super::MatchExpressionArm {
+            super::MatchExpressionArm {
                 arm_span,
-                source: source,
-            }),
+                source,
+                ref prior_arms,
+                last_ty,
+            } => {
+                tcx.lift(&last_ty).map(|last_ty| {
+                    super::MatchExpressionArm {
+                        arm_span,
+                        source,
+                        prior_arms: prior_arms.clone(),
+                        last_ty,
+                    }
+                })
+            }
             super::MatchExpressionArmPattern { span, ty } => {
                 tcx.lift(&ty).map(|ty| super::MatchExpressionArmPattern { span, ty })
             }
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index 3a670c8e2f1..0e6ab5b1eb3 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -689,6 +689,8 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
             CoerceMany::with_coercion_sites(coerce_first, arms)
         };
 
+        let mut other_arms = vec![];  // used only for diagnostics
+        let mut prior_arm_ty = None;
         for (i, (arm, pats_diverge)) in arms.iter().zip(all_arm_pats_diverge).enumerate() {
             if let Some(ref g) = arm.guard {
                 self.diverges.set(pats_diverge);
@@ -709,17 +711,36 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
                 _ => false
             };
 
+            let arm_span = if let hir::ExprKind::Block(ref blk, _) = arm.body.node {
+                // Point at the block expr instead of the entire block
+                blk.expr.as_ref().map(|e| e.span).unwrap_or(arm.body.span)
+            } else {
+                arm.body.span
+            };
             if is_if_let_fallback {
                 let cause = self.cause(expr.span, ObligationCauseCode::IfExpressionWithNoElse);
                 assert!(arm_ty.is_unit());
                 coercion.coerce_forced_unit(self, &cause, &mut |_| (), true);
             } else {
-                let cause = self.cause(expr.span, ObligationCauseCode::MatchExpressionArm {
-                    arm_span: arm.body.span,
-                    source: match_src
-                });
+                let cause = if i == 0 {
+                    // The reason for the first arm to fail is not that the match arms diverge,
+                    // but rather that there's a prior obligation that doesn't hold.
+                    self.cause(arm_span, ObligationCauseCode::BlockTailExpression(arm.body.id))
+                } else {
+                    self.cause(expr.span, ObligationCauseCode::MatchExpressionArm {
+                        arm_span,
+                        source: match_src,
+                        prior_arms: other_arms.clone(),
+                        last_ty: prior_arm_ty.unwrap(),
+                    })
+                };
                 coercion.coerce(self, &cause, &arm.body, arm_ty);
             }
+            other_arms.push(arm_span);
+            if other_arms.len() > 5 {
+                other_arms.remove(0);
+            }
+            prior_arm_ty = Some(arm_ty);
         }
 
         // We won't diverge unless the discriminant or all arms diverge.
diff --git a/src/test/ui/if/if-let-arm-types.rs b/src/test/ui/if/if-let-arm-types.rs
index 749c089ae97..819f5dd1cfc 100644
--- a/src/test/ui/if/if-let-arm-types.rs
+++ b/src/test/ui/if/if-let-arm-types.rs
@@ -1,10 +1,11 @@
 fn main() {
-    if let Some(b) = None { //~ ERROR: `if let` arms have incompatible types
-        //~^ expected (), found integer
-        //~| expected type `()`
-        //~| found type `{integer}`
+    if let Some(b) = None {
+        //~^ NOTE if let` arms have incompatible types
         ()
     } else {
         1
     };
+    //~^^ ERROR: `if let` arms have incompatible types
+    //~| NOTE expected (), found integer
+    //~| NOTE expected type `()`
 }
diff --git a/src/test/ui/if/if-let-arm-types.stderr b/src/test/ui/if/if-let-arm-types.stderr
index fcf9e4695f6..6401a62c06b 100644
--- a/src/test/ui/if/if-let-arm-types.stderr
+++ b/src/test/ui/if/if-let-arm-types.stderr
@@ -1,25 +1,17 @@
 error[E0308]: `if let` arms have incompatible types
-  --> $DIR/if-let-arm-types.rs:2:5
+  --> $DIR/if-let-arm-types.rs:6:9
    |
-LL | /     if let Some(b) = None { //~ ERROR: `if let` arms have incompatible types
-LL | |         //~^ expected (), found integer
-LL | |         //~| expected type `()`
-LL | |         //~| found type `{integer}`
-...  |
+LL | /     if let Some(b) = None {
+LL | |         //~^ NOTE if let` arms have incompatible types
+LL | |         ()
+LL | |     } else {
 LL | |         1
+   | |         ^ expected (), found integer
 LL | |     };
-   | |_____^ expected (), found integer
+   | |_____- `if let` arms have incompatible types
    |
    = note: expected type `()`
               found type `{integer}`
-note: `if let` arm with an incompatible type
-  --> $DIR/if-let-arm-types.rs:7:12
-   |
-LL |       } else {
-   |  ____________^
-LL | |         1
-LL | |     };
-   | |_____^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-11319.rs b/src/test/ui/issues/issue-11319.rs
index ea901205544..726c437355e 100644
--- a/src/test/ui/issues/issue-11319.rs
+++ b/src/test/ui/issues/issue-11319.rs
@@ -1,12 +1,14 @@
 fn main() {
     match Some(10) {
-    //~^ ERROR match arms have incompatible types
-    //~| expected type `bool`
-    //~| found type `()`
-    //~| expected bool, found ()
+    //~^ NOTE `match` arms have incompatible types
         Some(5) => false,
+        //~^ NOTE this is found to be of type `bool`
         Some(2) => true,
+        //~^ NOTE this is found to be of type `bool`
         None    => (),
+        //~^ ERROR match arms have incompatible types
+        //~| NOTE expected bool, found ()
+        //~| NOTE expected type `bool`
         _       => true
     }
 }
diff --git a/src/test/ui/issues/issue-11319.stderr b/src/test/ui/issues/issue-11319.stderr
index 44d63ba3e68..10db477b8ca 100644
--- a/src/test/ui/issues/issue-11319.stderr
+++ b/src/test/ui/issues/issue-11319.stderr
@@ -1,16 +1,20 @@
 error[E0308]: match arms have incompatible types
-  --> $DIR/issue-11319.rs:2:5
+  --> $DIR/issue-11319.rs:8:20
    |
 LL | /     match Some(10) {
-LL | |     //~^ ERROR match arms have incompatible types
-LL | |     //~| expected type `bool`
-LL | |     //~| found type `()`
-...  |
+LL | |     //~^ NOTE `match` arms have incompatible types
+LL | |         Some(5) => false,
+   | |                    ----- this is found to be of type `bool`
+LL | |         //~^ NOTE this is found to be of type `bool`
+LL | |         Some(2) => true,
+   | |                    ---- this is found to be of type `bool`
+LL | |         //~^ NOTE this is found to be of type `bool`
 LL | |         None    => (),
-   | |                    -- match arm with an incompatible type
+   | |                    ^^ expected bool, found ()
+...  |
 LL | |         _       => true
 LL | |     }
-   | |_____^ expected bool, found ()
+   | |_____- `match` arms have incompatible types
    |
    = note: expected type `bool`
               found type `()`
diff --git a/src/test/ui/issues/issue-17728.rs b/src/test/ui/issues/issue-17728.rs
index 0f13ae3304d..15cea1d609d 100644
--- a/src/test/ui/issues/issue-17728.rs
+++ b/src/test/ui/issues/issue-17728.rs
@@ -97,7 +97,7 @@ impl Debug for Player {
 }
 
 fn str_to_direction(to_parse: &str) -> RoomDirection {
-    match to_parse { //~ ERROR match arms have incompatible types
+    match to_parse {
         "w" | "west" => RoomDirection::West,
         "e" | "east" => RoomDirection::East,
         "n" | "north" => RoomDirection::North,
@@ -108,6 +108,7 @@ fn str_to_direction(to_parse: &str) -> RoomDirection {
         "down" => RoomDirection::Down,
         _ => None
     }
+        //~^^ ERROR match arms have incompatible types
 }
 
 fn main() {
diff --git a/src/test/ui/issues/issue-17728.stderr b/src/test/ui/issues/issue-17728.stderr
index 355868f0569..2c2efad19f5 100644
--- a/src/test/ui/issues/issue-17728.stderr
+++ b/src/test/ui/issues/issue-17728.stderr
@@ -10,17 +10,19 @@ LL |             Some(entry) => Ok(entry),
    |                            ^^^^^^^^^ ...but data from `room` is returned here
 
 error[E0308]: match arms have incompatible types
-  --> $DIR/issue-17728.rs:100:5
+  --> $DIR/issue-17728.rs:109:14
    |
-LL | /     match to_parse { //~ ERROR match arms have incompatible types
+LL | /     match to_parse {
 LL | |         "w" | "west" => RoomDirection::West,
 LL | |         "e" | "east" => RoomDirection::East,
 LL | |         "n" | "north" => RoomDirection::North,
 ...  |
+LL | |         "down" => RoomDirection::Down,
+   | |                   ------------------- this and all prior arms are found to be of type `RoomDirection`
 LL | |         _ => None
-   | |              ---- match arm with an incompatible type
+   | |              ^^^^ expected enum `RoomDirection`, found enum `std::option::Option`
 LL | |     }
-   | |_____^ expected enum `RoomDirection`, found enum `std::option::Option`
+   | |_____- `match` arms have incompatible types
    |
    = note: expected type `RoomDirection`
               found type `std::option::Option<_>`
diff --git a/src/test/ui/issues/issue-24036.rs b/src/test/ui/issues/issue-24036.rs
index 3642085934a..2f501b941b5 100644
--- a/src/test/ui/issues/issue-24036.rs
+++ b/src/test/ui/issues/issue-24036.rs
@@ -6,11 +6,11 @@ fn closure_to_loc() {
 
 fn closure_from_match() {
     let x = match 1usize {
-    //~^ ERROR match arms have incompatible types
         1 => |c| c + 1,
         2 => |c| c - 1,
         _ => |c| c - 1
     };
+    //~^^^ ERROR match arms have incompatible types
 }
 
 fn main() { }
diff --git a/src/test/ui/issues/issue-24036.stderr b/src/test/ui/issues/issue-24036.stderr
index 9f799c9b450..fa9935fcf61 100644
--- a/src/test/ui/issues/issue-24036.stderr
+++ b/src/test/ui/issues/issue-24036.stderr
@@ -10,20 +10,20 @@ LL |     x = |c| c + 1;
    = help: consider boxing your closure and/or using it as a trait object
 
 error[E0308]: match arms have incompatible types
-  --> $DIR/issue-24036.rs:8:13
+  --> $DIR/issue-24036.rs:10:14
    |
 LL |       let x = match 1usize {
-   |  _____________^
-LL | |     //~^ ERROR match arms have incompatible types
+   |  _____________-
 LL | |         1 => |c| c + 1,
+   | |              --------- this is found to be of type `[closure@$DIR/issue-24036.rs:9:14: 9:23]`
 LL | |         2 => |c| c - 1,
-   | |              --------- match arm with an incompatible type
+   | |              ^^^^^^^^^ expected closure, found a different closure
 LL | |         _ => |c| c - 1
 LL | |     };
-   | |_____^ expected closure, found a different closure
+   | |_____- `match` arms have incompatible types
    |
-   = note: expected type `[closure@$DIR/issue-24036.rs:10:14: 10:23]`
-              found type `[closure@$DIR/issue-24036.rs:11:14: 11:23]`
+   = note: expected type `[closure@$DIR/issue-24036.rs:9:14: 9:23]`
+              found type `[closure@$DIR/issue-24036.rs:10:14: 10:23]`
    = note: no two closures, even if identical, have the same type
    = help: consider boxing your closure and/or using it as a trait object
 
diff --git a/src/test/ui/match/match-type-err-first-arm.rs b/src/test/ui/match/match-type-err-first-arm.rs
new file mode 100644
index 00000000000..b4b84ef8f1c
--- /dev/null
+++ b/src/test/ui/match/match-type-err-first-arm.rs
@@ -0,0 +1,45 @@
+fn main() {
+    let _ = test_func1(1);
+    let _ = test_func2(1);
+}
+
+fn test_func1(n: i32) -> i32 {
+    //~^ NOTE expected `i32` because of return type
+    match n {
+        12 => 'b',
+        //~^ ERROR mismatched types
+        //~| NOTE expected i32, found char
+        _ => 42,
+    }
+}
+
+fn test_func2(n: i32) -> i32 {
+    let x = match n {
+    //~^ NOTE `match` arms have incompatible types
+        12 => 'b',
+        //~^ NOTE this is found to be of type `char`
+        _ => 42,
+        //~^ ERROR match arms have incompatible types
+        //~| NOTE expected char, found integer
+        //~| NOTE expected type `char`
+    };
+    x
+}
+
+fn test_func3(n: i32) -> i32 {
+    let x = match n {
+    //~^ NOTE `match` arms have incompatible types
+        1 => 'b',
+        2 => 'b',
+        3 => 'b',
+        4 => 'b',
+        5 => 'b',
+        6 => 'b',
+        //~^ NOTE this and all prior arms are found to be of type `char`
+        _ => 42,
+        //~^ ERROR match arms have incompatible types
+        //~| NOTE expected char, found integer
+        //~| NOTE expected type `char`
+    };
+    x
+}
diff --git a/src/test/ui/match/match-type-err-first-arm.stderr b/src/test/ui/match/match-type-err-first-arm.stderr
new file mode 100644
index 00000000000..db8bef8dc77
--- /dev/null
+++ b/src/test/ui/match/match-type-err-first-arm.stderr
@@ -0,0 +1,53 @@
+error[E0308]: mismatched types
+  --> $DIR/match-type-err-first-arm.rs:9:15
+   |
+LL | fn test_func1(n: i32) -> i32 {
+   |                          --- expected `i32` because of return type
+...
+LL |         12 => 'b',
+   |               ^^^ expected i32, found char
+
+error[E0308]: match arms have incompatible types
+  --> $DIR/match-type-err-first-arm.rs:21:14
+   |
+LL |       let x = match n {
+   |  _____________-
+LL | |     //~^ NOTE `match` arms have incompatible types
+LL | |         12 => 'b',
+   | |               --- this is found to be of type `char`
+LL | |         //~^ NOTE this is found to be of type `char`
+LL | |         _ => 42,
+   | |              ^^ expected char, found integer
+...  |
+LL | |         //~| NOTE expected type `char`
+LL | |     };
+   | |_____- `match` arms have incompatible types
+   |
+   = note: expected type `char`
+              found type `{integer}`
+
+error[E0308]: match arms have incompatible types
+  --> $DIR/match-type-err-first-arm.rs:39:14
+   |
+LL |       let x = match n {
+   |  _____________-
+LL | |     //~^ NOTE `match` arms have incompatible types
+LL | |         1 => 'b',
+LL | |         2 => 'b',
+...  |
+LL | |         6 => 'b',
+   | |              --- this and all prior arms are found to be of type `char`
+LL | |         //~^ NOTE this and all prior arms are found to be of type `char`
+LL | |         _ => 42,
+   | |              ^^ expected char, found integer
+...  |
+LL | |         //~| NOTE expected type `char`
+LL | |     };
+   | |_____- `match` arms have incompatible types
+   |
+   = note: expected type `char`
+              found type `{integer}`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.