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.
This commit is contained in:
parent
aa330518f4
commit
8221f9c837
6 changed files with 78 additions and 4 deletions
|
@ -139,7 +139,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
&cause,
|
||||
Some(arm.body),
|
||||
arm_ty,
|
||||
|err| self.suggest_removing_semicolon_for_coerce(err, expr, arm_ty, prior_arm),
|
||||
|err| {
|
||||
self.explain_never_type_coerced_to_unit(err, arm, arm_ty, prior_arm, expr);
|
||||
},
|
||||
false,
|
||||
);
|
||||
|
||||
|
@ -177,6 +179,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
coercion.complete(self)
|
||||
}
|
||||
|
||||
fn explain_never_type_coerced_to_unit(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
arm: &hir::Arm<'tcx>,
|
||||
arm_ty: Ty<'tcx>,
|
||||
prior_arm: Option<(Option<hir::HirId>, Ty<'tcx>, Span)>,
|
||||
expr: &hir::Expr<'tcx>,
|
||||
) {
|
||||
if let hir::ExprKind::Block(block, _) = arm.body.kind
|
||||
&& let Some(expr) = block.expr
|
||||
&& let arm_tail_ty = self.node_ty(expr.hir_id)
|
||||
&& arm_tail_ty.is_never()
|
||||
&& !arm_ty.is_never()
|
||||
{
|
||||
err.span_label(
|
||||
expr.span,
|
||||
format!(
|
||||
"this expression is of type `!`, but it is coerced to `{arm_ty}` due to its \
|
||||
surrounding expression",
|
||||
),
|
||||
);
|
||||
self.suggest_mismatched_types_on_tail(
|
||||
err,
|
||||
expr,
|
||||
arm_ty,
|
||||
prior_arm.map_or(arm_tail_ty, |(_, ty, _)| ty),
|
||||
expr.hir_id,
|
||||
);
|
||||
}
|
||||
self.suggest_removing_semicolon_for_coerce(err, expr, arm_ty, prior_arm)
|
||||
}
|
||||
|
||||
fn suggest_removing_semicolon_for_coerce(
|
||||
&self,
|
||||
diag: &mut Diagnostic,
|
||||
|
|
|
@ -1715,6 +1715,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
|
|||
// label pointing out the cause for the type coercion will be wrong
|
||||
// as prior return coercions would not be relevant (#57664).
|
||||
let fn_decl = if let (Some(expr), Some(blk_id)) = (expression, blk_id) {
|
||||
fcx.suggest_missing_semicolon(&mut err, expr, expected, false);
|
||||
let pointing_at_return_type =
|
||||
fcx.suggest_mismatched_types_on_tail(&mut err, expr, expected, found, blk_id);
|
||||
if let (Some(cond_expr), true, false) = (
|
||||
|
|
|
@ -663,8 +663,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
coerce.coerce_forced_unit(
|
||||
self,
|
||||
&cause,
|
||||
|err| {
|
||||
self.suggest_mismatched_types_on_tail(err, expr, ty, e_ty, target_id);
|
||||
|mut err| {
|
||||
self.suggest_missing_semicolon(&mut err, expr, e_ty, false);
|
||||
self.suggest_mismatched_types_on_tail(
|
||||
&mut err, expr, ty, e_ty, target_id,
|
||||
);
|
||||
let error = Some(Sorts(ExpectedFound { expected: ty, found: e_ty }));
|
||||
self.annotate_loop_expected_due_to_inference(err, expr, error);
|
||||
if let Some(val) = ty_kind_suggestion(ty) {
|
||||
|
|
|
@ -72,7 +72,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
blk_id: hir::HirId,
|
||||
) -> bool {
|
||||
let expr = expr.peel_drop_temps();
|
||||
self.suggest_missing_semicolon(err, expr, expected, false);
|
||||
let mut pointing_at_return_type = false;
|
||||
if let hir::ExprKind::Break(..) = expr.kind {
|
||||
// `break` type mismatches provide better context for tail `loop` expressions.
|
||||
|
|
16
tests/ui/match/match-tail-expr-never-type-error.rs
Normal file
16
tests/ui/match/match-tail-expr-never-type-error.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
fn never() -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn bar(a: bool) {
|
||||
match a {
|
||||
true => 1,
|
||||
false => {
|
||||
never() //~ ERROR `match` arms have incompatible types
|
||||
}
|
||||
}
|
||||
}
|
||||
fn main() {
|
||||
bar(true);
|
||||
bar(false);
|
||||
}
|
21
tests/ui/match/match-tail-expr-never-type-error.stderr
Normal file
21
tests/ui/match/match-tail-expr-never-type-error.stderr
Normal file
|
@ -0,0 +1,21 @@
|
|||
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 is coerced to `()` due to its surrounding expression
|
||||
LL | | }
|
||||
LL | | }
|
||||
| |_____- `match` arms have incompatible types
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
Loading…
Add table
Reference in a new issue