about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs45
-rw-r--r--tests/ui/loops/loop-break-value.stderr11
2 files changed, 53 insertions, 3 deletions
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index c08629a5269..6047461e0a6 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -530,7 +530,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     // When encountering a type error on the value of a `break`, try to point at the reason for the
     // expected type.
-    fn annotate_loop_expected_due_to_inference(
+    pub fn annotate_loop_expected_due_to_inference(
         &self,
         err: &mut Diagnostic,
         expr: &hir::Expr<'_>,
@@ -540,11 +540,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return;
         };
         let mut parent_id = self.tcx.hir().parent_id(expr.hir_id);
+        let mut parent;
         loop {
             // Climb the HIR tree to see if the current `Expr` is part of a `break;` statement.
-            let Some(hir::Node::Expr(parent)) = self.tcx.hir().find(parent_id) else {
+            let Some(hir::Node::Expr(p)) = self.tcx.hir().find(parent_id) else {
                 break;
             };
+            parent = p;
             parent_id = self.tcx.hir().parent_id(parent.hir_id);
             let hir::ExprKind::Break(destination, _) = parent.kind else {
                 continue;
@@ -582,6 +584,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             span,
                             format!("this loop is expected to be of type `{expected}`"),
                         );
+                    } else {
+                        // Locate all other `break` statements within the same `loop` that might
+                        // have affected inference.
+                        struct FindBreaks<'tcx> {
+                            destination: hir::Destination,
+                            uses: Vec<&'tcx hir::Expr<'tcx>>,
+                        }
+                        impl<'tcx> Visitor<'tcx> for FindBreaks<'tcx> {
+                            fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
+                                if let hir::ExprKind::Break(destination, _) = ex.kind
+                                    && self.destination.label == destination.label
+                                {
+                                    self.uses.push(ex);
+                                }
+                                hir::intravisit::walk_expr(self, ex);
+                            }
+                        }
+                        let mut expr_finder = FindBreaks { destination, uses: vec![] };
+                        expr_finder.visit_expr(parent);
+                        for ex in expr_finder.uses {
+                            let hir::ExprKind::Break(_, val) = ex.kind else {
+                                continue;
+                            };
+                            let ty = match val {
+                                Some(val) => {
+                                    match self.typeck_results.borrow().expr_ty_adjusted_opt(val) {
+                                        None => continue,
+                                        Some(ty) => ty,
+                                    }
+                                }
+                                None => self.tcx.types.unit,
+                            };
+                            if self.can_eq(self.param_env, ty, expected) {
+                                err.span_label(
+                                    ex.span,
+                                    format!("expected because of this `break`"),
+                                );
+                            }
+                        }
                     }
                     break;
                 }
diff --git a/tests/ui/loops/loop-break-value.stderr b/tests/ui/loops/loop-break-value.stderr
index 76d12b7c1b3..dfea1f53d0d 100644
--- a/tests/ui/loops/loop-break-value.stderr
+++ b/tests/ui/loops/loop-break-value.stderr
@@ -142,6 +142,9 @@ LL |     let val: ! = loop { break break; };
 error[E0308]: mismatched types
   --> $DIR/loop-break-value.rs:11:19
    |
+LL |             break "asdf";
+   |             ------------ expected because of this `break`
+LL |         } else {
 LL |             break 123;
    |                   ^^^ expected `&str`, found integer
 
@@ -176,7 +179,11 @@ error[E0308]: mismatched types
   --> $DIR/loop-break-value.rs:80:15
    |
 LL |         break (break, break);
-   |               ^^^^^^^^^^^^^^ expected `()`, found `(!, !)`
+   |               ^-----^^-----^
+   |               ||      |
+   |               ||      expected because of this `break`
+   |               |expected because of this `break`
+   |               expected `()`, found `(!, !)`
    |
    = note: expected unit type `()`
                   found tuple `(!, !)`
@@ -184,6 +191,8 @@ LL |         break (break, break);
 error[E0308]: mismatched types
   --> $DIR/loop-break-value.rs:85:15
    |
+LL |         break;
+   |         ----- expected because of this `break`
 LL |         break 2;
    |               ^ expected `()`, found integer