Skip to content

Commit d2fb319

Browse files
committed
Auto merge of #117526 - estebank:issue-24157, r=b-naber
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.
2 parents 48cfbe0 + 1d8e053 commit d2fb319

File tree

6 files changed

+78
-4
lines changed

6 files changed

+78
-4
lines changed

compiler/rustc_hir_typeck/src/_match.rs

+35-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,9 @@ 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+
self.explain_never_type_coerced_to_unit(err, arm, arm_ty, prior_arm, expr);
144+
},
143145
false,
144146
);
145147

@@ -177,6 +179,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
177179
coercion.complete(self)
178180
}
179181

182+
fn explain_never_type_coerced_to_unit(
183+
&self,
184+
err: &mut Diagnostic,
185+
arm: &hir::Arm<'tcx>,
186+
arm_ty: Ty<'tcx>,
187+
prior_arm: Option<(Option<hir::HirId>, Ty<'tcx>, Span)>,
188+
expr: &hir::Expr<'tcx>,
189+
) {
190+
if let hir::ExprKind::Block(block, _) = arm.body.kind
191+
&& let Some(expr) = block.expr
192+
&& let arm_tail_ty = self.node_ty(expr.hir_id)
193+
&& arm_tail_ty.is_never()
194+
&& !arm_ty.is_never()
195+
{
196+
err.span_label(
197+
expr.span,
198+
format!(
199+
"this expression is of type `!`, but it is coerced to `{arm_ty}` due to its \
200+
surrounding expression",
201+
),
202+
);
203+
self.suggest_mismatched_types_on_tail(
204+
err,
205+
expr,
206+
arm_ty,
207+
prior_arm.map_or(arm_tail_ty, |(_, ty, _)| ty),
208+
expr.hir_id,
209+
);
210+
}
211+
self.suggest_removing_semicolon_for_coerce(err, expr, arm_ty, prior_arm)
212+
}
213+
180214
fn suggest_removing_semicolon_for_coerce(
181215
&self,
182216
diag: &mut Diagnostic,

compiler/rustc_hir_typeck/src/coercion.rs

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

compiler/rustc_hir_typeck/src/expr.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -663,8 +663,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
663663
coerce.coerce_forced_unit(
664664
self,
665665
&cause,
666-
|err| {
667-
self.suggest_mismatched_types_on_tail(err, expr, ty, e_ty, target_id);
666+
|mut err| {
667+
self.suggest_missing_semicolon(&mut err, expr, e_ty, false);
668+
self.suggest_mismatched_types_on_tail(
669+
&mut err, expr, ty, e_ty, target_id,
670+
);
668671
let error = Some(Sorts(ExpectedFound { expected: ty, found: e_ty }));
669672
self.annotate_loop_expected_due_to_inference(err, expr, error);
670673
if let Some(val) = ty_kind_suggestion(ty) {

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 is 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)