From 020f6895e51c54be77cb2568cd27e93e04c27851 Mon Sep 17 00:00:00 2001
From: Lukas Wirth <lukastw97@gmail.com>
Date: Sat, 3 Sep 2022 17:08:18 +0200
Subject: [PATCH] Fix nested break expressions, expecting unknown types

---
 crates/hir-ty/src/infer/expr.rs   | 43 +++++++++++++++----------------
 crates/hir-ty/src/tests/simple.rs | 14 ++++++++++
 2 files changed, 35 insertions(+), 22 deletions(-)

diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index f3f4ee65bb2..2d04a864a2c 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -382,36 +382,35 @@ impl<'a> InferenceContext<'a> {
                 TyKind::Never.intern(Interner)
             }
             Expr::Break { expr, label } => {
-                let mut coerce = match find_breakable(&mut self.breakables, label.as_ref()) {
-                    Some(ctxt) => {
-                        // avoiding the borrowck
-                        mem::replace(
-                            &mut ctxt.coerce,
-                            CoerceMany::new(self.result.standard_types.unknown.clone()),
-                        )
-                    }
-                    None => CoerceMany::new(self.result.standard_types.unknown.clone()),
-                };
-
                 let val_ty = if let Some(expr) = *expr {
                     self.infer_expr(expr, &Expectation::none())
                 } else {
                     TyBuilder::unit()
                 };
 
-                // FIXME: create a synthetic `()` during lowering so we have something to refer to here?
-                coerce.coerce(self, *expr, &val_ty);
+                match find_breakable(&mut self.breakables, label.as_ref()) {
+                    Some(ctxt) => {
+                        // avoiding the borrowck
+                        let mut coerce = mem::replace(
+                            &mut ctxt.coerce,
+                            CoerceMany::new(self.result.standard_types.unknown.clone()),
+                        );
 
-                if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) {
-                    ctxt.coerce = coerce;
-                    ctxt.may_break = true;
-                } else {
-                    self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
-                        expr: tgt_expr,
-                        is_break: true,
-                    });
-                };
+                        // FIXME: create a synthetic `()` during lowering so we have something to refer to here?
+                        coerce.coerce(self, *expr, &val_ty);
 
+                        let ctxt = find_breakable(&mut self.breakables, label.as_ref())
+                            .expect("breakable stack changed during coercion");
+                        ctxt.coerce = coerce;
+                        ctxt.may_break = true;
+                    }
+                    None => {
+                        self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
+                            expr: tgt_expr,
+                            is_break: true,
+                        });
+                    }
+                }
                 TyKind::Never.intern(Interner)
             }
             Expr::Return { expr } => {
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index 707e9e84506..4ea103e5d9e 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/crates/hir-ty/src/tests/simple.rs
@@ -3069,3 +3069,17 @@ fn main() {
         "#,
     );
 }
+
+#[test]
+fn nested_break() {
+    check_no_mismatches(
+        r#"
+fn func() {
+    let int = loop {
+        break 0;
+        break (break 0);
+    };
+}
+    "#,
+    );
+}