about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs10
-rw-r--r--tests/ui/type/type-check/coerce-result-return-value-2.rs24
-rw-r--r--tests/ui/type/type-check/coerce-result-return-value-2.stderr47
-rw-r--r--tests/ui/type/type-check/coerce-result-return-value.fixed8
-rw-r--r--tests/ui/type/type-check/coerce-result-return-value.rs8
-rw-r--r--tests/ui/type/type-check/coerce-result-return-value.stderr34
6 files changed, 130 insertions, 1 deletions
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index d55ea52d16b..97490194e25 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 00000000000..23bafa6c5c9
--- /dev/null
+++ b/tests/ui/type/type-check/coerce-result-return-value-2.rs
@@ -0,0 +1,24 @@
+struct A;
+struct B;
+impl From<A> 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 00000000000..64a8c779fce
--- /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 91066262303..8a05407070d 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 9a71376f462..442203addb7 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 7aebc9dcc7a..18993b3cef1 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`.