Skip to content

Commit b47dac0

Browse files
committed
Account for ! arm in tail match expr
On functions with a default return type that influences the coerced type of `match` arms, check if the failing arm is actually of type `!`. If so, suggest changing the return type so the coercion against the prior arms is successful. ``` error[E0308]: `match` arms have incompatible types --> $DIR/match-tail-expr-never-type-error.rs:9:13 | LL | fn bar(a: bool) { | - help: try adding a return type: `-> i32` LL | / match a { LL | | true => 1, | | - this is found to be of type `{integer}` LL | | false => { LL | | never() | | ^^^^^^^ | | | | | expected integer, found `()` | | this expression is of type `!`, but it get's coerced to `()` due to its surrounding expression LL | | } LL | | } | |_____- `match` arms have incompatible types ``` Fix #24157.
1 parent 722b3ee commit b47dac0

File tree

6 files changed

+63
-2
lines changed

6 files changed

+63
-2
lines changed

compiler/rustc_hir_typeck/src/_match.rs

+24-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
139139
&cause,
140140
Some(&arm.body),
141141
arm_ty,
142-
|err| self.suggest_removing_semicolon_for_coerce(err, expr, arm_ty, prior_arm),
142+
|err| {
143+
if let hir::ExprKind::Block(block, _) = arm.body.kind
144+
&& let Some(expr) = block.expr
145+
&& let arm_tail_ty = self.node_ty(expr.hir_id)
146+
&& arm_tail_ty.is_never()
147+
&& !arm_ty.is_never()
148+
{
149+
err.span_label(
150+
expr.span,
151+
format!(
152+
"this expression is of type `!`, but it get's coerced to \
153+
`{arm_ty}` due to its surrounding expression",
154+
),
155+
);
156+
self.suggest_mismatched_types_on_tail(
157+
err,
158+
expr,
159+
arm_ty,
160+
prior_arm.map_or(arm_tail_ty, |(_, ty, _)| ty),
161+
expr.hir_id,
162+
);
163+
}
164+
self.suggest_removing_semicolon_for_coerce(err, expr, arm_ty, prior_arm)
165+
},
143166
false,
144167
);
145168

compiler/rustc_hir_typeck/src/coercion.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1738,6 +1738,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
17381738
// label pointing out the cause for the type coercion will be wrong
17391739
// as prior return coercions would not be relevant (#57664).
17401740
let fn_decl = if let (Some(expr), Some(blk_id)) = (expression, blk_id) {
1741+
fcx.suggest_missing_semicolon(&mut err, expr, expected, false);
17411742
let pointing_at_return_type =
17421743
fcx.suggest_mismatched_types_on_tail(&mut err, expr, expected, found, blk_id);
17431744
if let (Some(cond_expr), true, false) = (

compiler/rustc_hir_typeck/src/expr.rs

+1
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
668668
self,
669669
&cause,
670670
|mut err| {
671+
self.suggest_missing_semicolon(&mut err, expr, e_ty, false);
671672
self.suggest_mismatched_types_on_tail(
672673
&mut err, expr, ty, e_ty, target_id,
673674
);

compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
7272
blk_id: hir::HirId,
7373
) -> bool {
7474
let expr = expr.peel_drop_temps();
75-
self.suggest_missing_semicolon(err, expr, expected, false);
7675
let mut pointing_at_return_type = false;
7776
if let hir::ExprKind::Break(..) = expr.kind {
7877
// `break` type mismatches provide better context for tail `loop` expressions.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
fn never() -> ! {
2+
loop {}
3+
}
4+
5+
fn bar(a: bool) {
6+
match a {
7+
true => 1,
8+
false => {
9+
never() //~ ERROR `match` arms have incompatible types
10+
}
11+
}
12+
}
13+
fn main() {
14+
bar(true);
15+
bar(false);
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0308]: `match` arms have incompatible types
2+
--> $DIR/match-tail-expr-never-type-error.rs:9:13
3+
|
4+
LL | fn bar(a: bool) {
5+
| - help: try adding a return type: `-> i32`
6+
LL | / match a {
7+
LL | | true => 1,
8+
| | - this is found to be of type `{integer}`
9+
LL | | false => {
10+
LL | | never()
11+
| | ^^^^^^^
12+
| | |
13+
| | expected integer, found `()`
14+
| | this expression is of type `!`, but it get's coerced to `()` due to its surrounding expression
15+
LL | | }
16+
LL | | }
17+
| |_____- `match` arms have incompatible types
18+
19+
error: aborting due to previous error
20+
21+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)