diff options
| author | Michael Goulet <michael@errs.io> | 2023-11-27 19:06:46 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-11-27 19:06:46 -0500 |
| commit | 8ff558b8fce007a541684f2adea48fa4339ce8f2 (patch) | |
| tree | 4be4271445808525908e2d7a10c789cba5865f26 | |
| parent | 1742a9fce6530d4e6637a0d7093e8738335e6e16 (diff) | |
| parent | 8221f9c837f46461d2dd6f29a4468b3a0ee4b508 (diff) | |
| download | rust-8ff558b8fce007a541684f2adea48fa4339ce8f2.tar.gz rust-8ff558b8fce007a541684f2adea48fa4339ce8f2.zip | |
Rollup 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.
| -rw-r--r-- | compiler/rustc_hir_typeck/src/_match.rs | 36 | ||||
| -rw-r--r-- | compiler/rustc_hir_typeck/src/coercion.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_hir_typeck/src/expr.rs | 7 | ||||
| -rw-r--r-- | compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs | 1 | ||||
| -rw-r--r-- | tests/ui/match/match-tail-expr-never-type-error.rs | 16 | ||||
| -rw-r--r-- | tests/ui/match/match-tail-expr-never-type-error.stderr | 21 |
6 files changed, 78 insertions, 4 deletions
diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index a29d9c95e5e..e7cbbc71335 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -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, diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index ae2a4a9504c..587038d57bd 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -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) = ( diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index f16269795e9..7f15478349e 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -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) { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 6660bea03d8..c74ef8f2713 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -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. diff --git a/tests/ui/match/match-tail-expr-never-type-error.rs b/tests/ui/match/match-tail-expr-never-type-error.rs new file mode 100644 index 00000000000..786ed3fa904 --- /dev/null +++ b/tests/ui/match/match-tail-expr-never-type-error.rs @@ -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); +} diff --git a/tests/ui/match/match-tail-expr-never-type-error.stderr b/tests/ui/match/match-tail-expr-never-type-error.stderr new file mode 100644 index 00000000000..226d33daeb2 --- /dev/null +++ b/tests/ui/match/match-tail-expr-never-type-error.stderr @@ -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`. |
