Repository: rust-lang/rust-analyzer
Pull request #13856 (2022-12-28 21:46:50)

fix: Type check unstable `try{}` blocks
![Peek 2022-12-29 01-40](https://user-images.githubusercontent.com/38225716/209875594-8bf9c9e2-9998-40b0-8820-28c7f2d9bae4.gif)

Fixes https://github.com/rust-lang/rust-analyzer/issues/11843


Patch:
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 7cf4fb1050..18e45511a4 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -688,7 +688,7 @@ impl<'a> InferenceContext<'a> {
         }
     }
 
-    /// Replaces Ty::Unknown by a new type var, so we can maybe still infer it.
+    /// Replaces `Ty::Error` by a new type var, so we can maybe still infer it.
     fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
         match ty.kind(Interner) {
             TyKind::Error => self.table.new_type_var(),
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index b1f4de8260..d4050f9af4 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -152,11 +152,20 @@ impl<'a> InferenceContext<'a> {
                 .1
             }
             Expr::TryBlock { body } => {
-                self.with_breakable_ctx(BreakableKind::Border, self.err_ty(), None, |this| {
-                    let _inner = this.infer_expr(*body, expected);
+                // The type that is returned from the try block
+                let try_ty = self.table.new_type_var();
+                if let Some(ty) = expected.only_has_type(&mut self.table) {
+                    self.unify(&try_ty, &ty);
+                }
+
+                // The ok-ish type that is expected from the last expression
+                let ok_ty = self.resolve_associated_type(try_ty.clone(), self.resolve_ops_try_ok());
+
+                self.with_breakable_ctx(BreakableKind::Block, ok_ty.clone(), None, |this| {
+                    this.infer_expr(*body, &Expectation::has_type(ok_ty));
                 });
-                // FIXME should be std::result::Result<{inner}, _>
-                self.err_ty()
+
+                try_ty
             }
             Expr::Async { body } => {
                 let ret_ty = self.table.new_type_var();
diff --git a/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs b/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
index 0c92e706b3..10e637979f 100644
--- a/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
+++ b/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
@@ -38,12 +38,12 @@ fn foo() {
     }
 
     #[test]
-    fn try_blocks_are_borders() {
+    fn async_blocks_are_borders() {
         check_diagnostics(
             r#"
 fn foo() {
     'a: loop {
-        try {
+        async {
                 break;
               //^^^^^ error: break outside of loop
                 break 'a;
@@ -60,12 +60,12 @@ fn foo() {
     }
 
     #[test]
-    fn async_blocks_are_borders() {
+    fn closures_are_borders() {
         check_diagnostics(
             r#"
 fn foo() {
     'a: loop {
-        try {
+        || {
                 break;
               //^^^^^ error: break outside of loop
                 break 'a;
@@ -82,21 +82,17 @@ fn foo() {
     }
 
     #[test]
-    fn closures_are_borders() {
+    fn blocks_pass_through() {
         check_diagnostics(
             r#"
 fn foo() {
     'a: loop {
-        try {
-                break;
-              //^^^^^ error: break outside of loop
-                break 'a;
-              //^^^^^^^^ error: break outside of loop
-                continue;
-              //^^^^^^^^ error: continue outside of loop
-                continue 'a;
-              //^^^^^^^^^^^ error: continue outside of loop
-        };
+        {
+            break;
+            break 'a;
+            continue;
+            continue 'a;
+        }
     }
 }
 "#,
@@ -104,17 +100,17 @@ fn foo() {
     }
 
     #[test]
-    fn blocks_pass_through() {
+    fn try_blocks_pass_through() {
         check_diagnostics(
             r#"
 fn foo() {
     'a: loop {
-        {
-            break;
-            break 'a;
-            continue;
-            continue 'a;
-        }
+        try {
+                break;
+                break 'a;
+                continue;
+                continue 'a;
+        };
     }
 }
 "#,


Test patch:
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index d7431443b8..146145523b 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/crates/hir-ty/src/tests/simple.rs
@@ -2064,17 +2064,17 @@ fn fn_pointer_return() {
 fn block_modifiers_smoke_test() {
     check_infer(
         r#"
-//- minicore: future
+//- minicore: future, try
 async fn main() {
     let x = unsafe { 92 };
     let y = async { async { () }.await };
-    let z = try { () };
+    let z: core::ops::ControlFlow<(), _> = try { () };
     let w = const { 92 };
     let t = 'a: { 92 };
 }
         "#,
         expect![[r#"
-            16..162 '{     ...2 }; }': ()
+            16..193 '{     ...2 }; }': ()
             26..27 'x': i32
             30..43 'unsafe { 92 }': i32
             30..43 'unsafe { 92 }': i32
@@ -2086,17 +2086,17 @@ async fn main() {
             65..77 'async { () }': impl Future<Output = ()>
             65..83 'async ....await': ()
             73..75 '()': ()
-            95..96 'z': {unknown}
-            99..109 'try { () }': ()
-            99..109 'try { () }': {unknown}
-            105..107 '()': ()
-            119..120 'w': i32
-            123..135 'const { 92 }': i32
-            123..135 'const { 92 }': i32
-            131..133 '92': i32
-            145..146 't': i32
-            149..159 ''a: { 92 }': i32
-            155..157 '92': i32
+            95..96 'z': ControlFlow<(), ()>
+            130..140 'try { () }': ()
+            130..140 'try { () }': ControlFlow<(), ()>
+            136..138 '()': ()
+            150..151 'w': i32
+            154..166 'const { 92 }': i32
+            154..166 'const { 92 }': i32
+            162..164 '92': i32
+            176..177 't': i32
+            180..190 ''a: { 92 }': i32
+            186..188 '92': i32
         "#]],
     )
 }