diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index d55ea52d16b0..97490194e255 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -713,6 +713,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if Some(e.did()) != self.tcx.get_diagnostic_item(sym::Result) { return false; } + let map = self.tcx.hir(); + if let Some(hir::Node::Expr(expr)) = map.find_parent(expr.hir_id) + && let hir::ExprKind::Ret(_) = expr.kind + { + // `return foo;` + } else if map.get_return_block(expr.hir_id).is_some() { + // Function's tail expression. + } else { + return false; + } let e = substs_e.type_at(1); let f = substs_f.type_at(1); if self diff --git a/tests/ui/type/type-check/coerce-result-return-value-2.rs b/tests/ui/type/type-check/coerce-result-return-value-2.rs new file mode 100644 index 000000000000..23bafa6c5c94 --- /dev/null +++ b/tests/ui/type/type-check/coerce-result-return-value-2.rs @@ -0,0 +1,24 @@ +struct A; +struct B; +impl From for B { + fn from(_: A) -> Self { B } +} +fn foo4(x: Result<(), A>) -> Result<(), B> { + match true { + true => x, //~ ERROR mismatched types + false => x, + } +} +fn foo5(x: Result<(), A>) -> Result<(), B> { + match true { + true => return x, //~ ERROR mismatched types + false => return x, + } +} +fn main() { + let _ = foo4(Ok(())); + let _ = foo5(Ok(())); + let _: Result<(), B> = { //~ ERROR mismatched types + Err(A); + }; +} diff --git a/tests/ui/type/type-check/coerce-result-return-value-2.stderr b/tests/ui/type/type-check/coerce-result-return-value-2.stderr new file mode 100644 index 000000000000..64a8c779fce7 --- /dev/null +++ b/tests/ui/type/type-check/coerce-result-return-value-2.stderr @@ -0,0 +1,47 @@ +error[E0308]: mismatched types + --> $DIR/coerce-result-return-value-2.rs:8:17 + | +LL | fn foo4(x: Result<(), A>) -> Result<(), B> { + | ------------- expected `Result<(), B>` because of return type +LL | match true { +LL | true => x, + | ^ expected struct `B`, found struct `A` + | + = note: expected enum `Result<_, B>` + found enum `Result<_, A>` +help: you can rely on the implicit conversion that `?` does to transform the error type + | +LL | true => Ok(x?), + | +++ ++ + +error[E0308]: mismatched types + --> $DIR/coerce-result-return-value-2.rs:14:24 + | +LL | fn foo5(x: Result<(), A>) -> Result<(), B> { + | ------------- expected `Result<(), B>` because of return type +LL | match true { +LL | true => return x, + | ^ expected struct `B`, found struct `A` + | + = note: expected enum `Result<_, B>` + found enum `Result<_, A>` +help: you can rely on the implicit conversion that `?` does to transform the error type + | +LL | true => return Ok(x?), + | +++ ++ + +error[E0308]: mismatched types + --> $DIR/coerce-result-return-value-2.rs:21:28 + | +LL | let _: Result<(), B> = { + | ____________________________^ +LL | | Err(A); +LL | | }; + | |_____^ expected enum `Result`, found `()` + | + = note: expected enum `Result<(), B>` + found unit type `()` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/type/type-check/coerce-result-return-value.fixed b/tests/ui/type/type-check/coerce-result-return-value.fixed index 91066262303e..8a05407070da 100644 --- a/tests/ui/type/type-check/coerce-result-return-value.fixed +++ b/tests/ui/type/type-check/coerce-result-return-value.fixed @@ -10,7 +10,15 @@ fn foo1(x: Result<(), A>) -> Result<(), B> { fn foo2(x: Result<(), A>) -> Result<(), B> { return Ok(x?); //~ ERROR mismatched types } +fn foo3(x: Result<(), A>) -> Result<(), B> { + if true { + Ok(x?) //~ ERROR mismatched types + } else { + Ok(x?) //~ ERROR mismatched types + } +} fn main() { let _ = foo1(Ok(())); let _ = foo2(Ok(())); + let _ = foo3(Ok(())); } diff --git a/tests/ui/type/type-check/coerce-result-return-value.rs b/tests/ui/type/type-check/coerce-result-return-value.rs index 9a71376f462d..442203addb78 100644 --- a/tests/ui/type/type-check/coerce-result-return-value.rs +++ b/tests/ui/type/type-check/coerce-result-return-value.rs @@ -10,7 +10,15 @@ fn foo1(x: Result<(), A>) -> Result<(), B> { fn foo2(x: Result<(), A>) -> Result<(), B> { return x; //~ ERROR mismatched types } +fn foo3(x: Result<(), A>) -> Result<(), B> { + if true { + x //~ ERROR mismatched types + } else { + x //~ ERROR mismatched types + } +} fn main() { let _ = foo1(Ok(())); let _ = foo2(Ok(())); + let _ = foo3(Ok(())); } diff --git a/tests/ui/type/type-check/coerce-result-return-value.stderr b/tests/ui/type/type-check/coerce-result-return-value.stderr index 7aebc9dcc7ad..18993b3cef1b 100644 --- a/tests/ui/type/type-check/coerce-result-return-value.stderr +++ b/tests/ui/type/type-check/coerce-result-return-value.stderr @@ -28,6 +28,38 @@ help: you can rely on the implicit conversion that `?` does to transform the err LL | return Ok(x?); | +++ ++ -error: aborting due to 2 previous errors +error[E0308]: mismatched types + --> $DIR/coerce-result-return-value.rs:15:9 + | +LL | fn foo3(x: Result<(), A>) -> Result<(), B> { + | ------------- expected `Result<(), B>` because of return type +LL | if true { +LL | x + | ^ expected struct `B`, found struct `A` + | + = note: expected enum `Result<_, B>` + found enum `Result<_, A>` +help: you can rely on the implicit conversion that `?` does to transform the error type + | +LL | Ok(x?) + | +++ ++ + +error[E0308]: mismatched types + --> $DIR/coerce-result-return-value.rs:17:9 + | +LL | fn foo3(x: Result<(), A>) -> Result<(), B> { + | ------------- expected `Result<(), B>` because of return type +... +LL | x + | ^ expected struct `B`, found struct `A` + | + = note: expected enum `Result<_, B>` + found enum `Result<_, A>` +help: you can rely on the implicit conversion that `?` does to transform the error type + | +LL | Ok(x?) + | +++ ++ + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0308`.