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,
|
&cause,
|
||||||
Some(arm.body),
|
Some(arm.body),
|
||||||
arm_ty,
|
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,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -177,6 +179,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
coercion.complete(self)
|
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(
|
fn suggest_removing_semicolon_for_coerce(
|
||||||
&self,
|
&self,
|
||||||
diag: &mut Diagnostic,
|
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
|
// label pointing out the cause for the type coercion will be wrong
|
||||||
// as prior return coercions would not be relevant (#57664).
|
// as prior return coercions would not be relevant (#57664).
|
||||||
let fn_decl = if let (Some(expr), Some(blk_id)) = (expression, blk_id) {
|
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 =
|
let pointing_at_return_type =
|
||||||
fcx.suggest_mismatched_types_on_tail(&mut err, expr, expected, found, blk_id);
|
fcx.suggest_mismatched_types_on_tail(&mut err, expr, expected, found, blk_id);
|
||||||
if let (Some(cond_expr), true, false) = (
|
if let (Some(cond_expr), true, false) = (
|
||||||
|
|
|
@ -663,8 +663,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
coerce.coerce_forced_unit(
|
coerce.coerce_forced_unit(
|
||||||
self,
|
self,
|
||||||
&cause,
|
&cause,
|
||||||
|err| {
|
|mut err| {
|
||||||
self.suggest_mismatched_types_on_tail(err, expr, ty, e_ty, target_id);
|
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 }));
|
let error = Some(Sorts(ExpectedFound { expected: ty, found: e_ty }));
|
||||||
self.annotate_loop_expected_due_to_inference(err, expr, error);
|
self.annotate_loop_expected_due_to_inference(err, expr, error);
|
||||||
if let Some(val) = ty_kind_suggestion(ty) {
|
if let Some(val) = ty_kind_suggestion(ty) {
|
||||||
|
|
|
@ -72,7 +72,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
blk_id: hir::HirId,
|
blk_id: hir::HirId,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let expr = expr.peel_drop_temps();
|
let expr = expr.peel_drop_temps();
|
||||||
self.suggest_missing_semicolon(err, expr, expected, false);
|
|
||||||
let mut pointing_at_return_type = false;
|
let mut pointing_at_return_type = false;
|
||||||
if let hir::ExprKind::Break(..) = expr.kind {
|
if let hir::ExprKind::Break(..) = expr.kind {
|
||||||
// `break` type mismatches provide better context for tail `loop` expressions.
|
// `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