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 @@ fn visit_expr(&mut self, ex: &'tcx hir::Expr<'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 @@ fn annotate_loop_expected_due_to_inference( 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 @@ fn annotate_loop_expected_due_to_inference( 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