Skip to content

Tweak "incompatible match arms" error #58267

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 21 additions & 12 deletions src/librustc/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -509,22 +509,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,
));
}
}
},
Expand Down
3 changes: 3 additions & 0 deletions src/librustc/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}
Expand Down Expand Up @@ -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
Expand Down
17 changes: 14 additions & 3 deletions src/librustc/traits/structural_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 })
}
Expand Down
29 changes: 25 additions & 4 deletions src/librustc_typeck/check/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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.
Expand Down
9 changes: 5 additions & 4 deletions src/test/ui/if/if-let-arm-types.rs
Original file line number Diff line number Diff line change
@@ -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 `()`
}
22 changes: 7 additions & 15 deletions src/test/ui/if/if-let-arm-types.stderr
Original file line number Diff line number Diff line change
@@ -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

Expand Down
10 changes: 6 additions & 4 deletions src/test/ui/issues/issue-11319.rs
Original file line number Diff line number Diff line change
@@ -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
}
}
18 changes: 11 additions & 7 deletions src/test/ui/issues/issue-11319.stderr
Original file line number Diff line number Diff line change
@@ -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 `()`
Expand Down
3 changes: 2 additions & 1 deletion src/test/ui/issues/issue-17728.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -108,6 +108,7 @@ fn str_to_direction(to_parse: &str) -> RoomDirection {
"down" => RoomDirection::Down,
_ => None
}
//~^^ ERROR match arms have incompatible types
}

fn main() {
Expand Down
10 changes: 6 additions & 4 deletions src/test/ui/issues/issue-17728.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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<_>`
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/issues/issue-24036.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() { }
14 changes: 7 additions & 7 deletions src/test/ui/issues/issue-24036.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
45 changes: 45 additions & 0 deletions src/test/ui/match/match-type-err-first-arm.rs
Original file line number Diff line number Diff line change
@@ -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
}
Loading