From 378ec2841bf344c05fe6119c0013edeabcf33a35 Mon Sep 17 00:00:00 2001
From: Lukas Wirth <lukastw97@gmail.com>
Date: Sat, 12 Dec 2020 00:03:36 +0100
Subject: Infer labeled blocks

---
 crates/hir_ty/src/infer/expr.rs   | 22 ++++++++++++---
 crates/hir_ty/src/tests/simple.rs | 56 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 74 insertions(+), 4 deletions(-)

diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index 605951b10..d7ad198b3 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -137,10 +137,24 @@ impl<'a> InferenceContext<'a> {
 
                 self.coerce_merge_branch(&then_ty, &else_ty)
             }
-            Expr::Block { statements, tail, .. } => {
-                // FIXME: Breakable block inference
-                self.infer_block(statements, *tail, expected)
-            }
+            Expr::Block { statements, tail, label } => match label {
+                Some(_) => {
+                    let break_ty = self.table.new_type_var();
+                    self.breakables.push(BreakableContext {
+                        may_break: false,
+                        break_ty: break_ty.clone(),
+                        label: label.clone(),
+                    });
+                    let ty = self.infer_block(statements, *tail, &Expectation::has_type(break_ty));
+                    let ctxt = self.breakables.pop().expect("breakable stack broken");
+                    if ctxt.may_break {
+                        ctxt.break_ty
+                    } else {
+                        ty
+                    }
+                }
+                None => self.infer_block(statements, *tail, expected),
+            },
             Expr::Unsafe { body } => self.infer_expr(*body, expected),
             Expr::TryBlock { body } => {
                 let _inner = self.infer_expr(*body, expected);
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index 4f72582b6..a569223b4 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -2074,6 +2074,62 @@ fn infer_labelled_break_with_val() {
     );
 }
 
+#[test]
+fn infer_labelled_block_break_with_val() {
+    check_infer(
+        r#"
+        fn default<T>() -> T { loop {} }
+        fn foo() {
+            let _x = 'outer: {
+                let inner = 'inner: {
+                    let i = default();
+                    if (break 'outer i) {
+                        break 'inner 5i8;
+                    } else if true {
+                        break 'inner 6;
+                    }
+                    break 'inner 'innermost: { 0 };
+                    42
+                };
+                break 'outer inner < 8;
+            };
+        }
+        "#,
+        expect![[r#"
+            21..32 '{ loop {} }': T
+            23..30 'loop {}': !
+            28..30 '{}': ()
+            42..381 '{     ...  }; }': ()
+            52..54 '_x': bool
+            65..378 '{     ...     }': bool
+            79..84 'inner': i8
+            95..339 '{     ...     }': i8
+            113..114 'i': bool
+            117..124 'default': fn default<bool>() -> bool
+            117..126 'default()': bool
+            140..270 'if (br...     }': ()
+            144..158 'break 'outer i': !
+            157..158 'i': bool
+            160..209 '{     ...     }': ()
+            178..194 'break ...er 5i8': !
+            191..194 '5i8': i8
+            215..270 'if tru...     }': ()
+            218..222 'true': bool
+            223..270 '{     ...     }': ()
+            241..255 'break 'inner 6': !
+            254..255 '6': i8
+            283..313 'break ... { 0 }': !
+            308..313 '{ 0 }': i8
+            310..311 '0': i8
+            327..329 '42': i8
+            349..371 'break ...er < 8': !
+            362..367 'inner': i8
+            362..371 'inner < 8': bool
+            370..371 '8': i8
+        "#]],
+    );
+}
+
 #[test]
 fn generic_default() {
     check_infer(
-- 
cgit v1.2.3