@@ -530,7 +530,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
530530
531531 // When encountering a type error on the value of a `break`, try to point at the reason for the
532532 // expected type.
533- fn annotate_loop_expected_due_to_inference (
533+ pub fn annotate_loop_expected_due_to_inference (
534534 & self ,
535535 err : & mut Diagnostic ,
536536 expr : & hir:: Expr < ' _ > ,
@@ -540,16 +540,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
540540 return ;
541541 } ;
542542 let mut parent_id = self . tcx . hir ( ) . parent_id ( expr. hir_id ) ;
543- loop {
543+ let mut parent;
544+ ' outer: loop {
544545 // Climb the HIR tree to see if the current `Expr` is part of a `break;` statement.
545- let Some ( hir:: Node :: Expr ( parent) ) = self . tcx . hir ( ) . find ( parent_id) else {
546+ let Some (
547+ hir:: Node :: Stmt ( hir:: Stmt { kind : hir:: StmtKind :: Semi ( & ref p) , .. } )
548+ | hir:: Node :: Expr ( & ref p) ,
549+ ) = self . tcx . hir ( ) . find ( parent_id)
550+ else {
546551 break ;
547552 } ;
548- parent_id = self . tcx . hir ( ) . parent_id ( parent. hir_id ) ;
553+ parent = p;
554+ parent_id = self . tcx . hir ( ) . parent_id ( parent_id) ;
549555 let hir:: ExprKind :: Break ( destination, _) = parent. kind else {
550556 continue ;
551557 } ;
552- let mut parent_id = parent. hir_id ;
558+ let mut parent_id = parent_id;
559+ let mut direct = false ;
553560 loop {
554561 // Climb the HIR tree to find the (desugared) `loop` this `break` corresponds to.
555562 let parent = match self . tcx . hir ( ) . find ( parent_id) {
@@ -565,14 +572,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
565572 parent_id = self . tcx . hir ( ) . parent_id ( * hir_id) ;
566573 parent
567574 }
568- Some ( hir:: Node :: Block ( hir :: Block { .. } ) ) => {
575+ Some ( hir:: Node :: Block ( _ ) ) => {
569576 parent_id = self . tcx . hir ( ) . parent_id ( parent_id) ;
570577 parent
571578 }
572579 _ => break ,
573580 } ;
574- if let hir:: ExprKind :: Loop ( _, label, _, span) = parent. kind
575- && destination. label == label
581+ if let hir:: ExprKind :: Loop ( ..) = parent. kind {
582+ // When you have `'a: loop { break; }`, the `break` corresponds to the labeled
583+ // loop, so we need to account for that.
584+ direct = !direct;
585+ }
586+ if let hir:: ExprKind :: Loop ( block, label, _, span) = parent. kind
587+ && ( destination. label == label || direct)
576588 {
577589 if let Some ( ( reason_span, message) ) =
578590 self . maybe_get_coercion_reason ( parent_id, parent. span )
@@ -582,8 +594,64 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
582594 span,
583595 format ! ( "this loop is expected to be of type `{expected}`" ) ,
584596 ) ;
597+ break ' outer;
598+ } else {
599+ // Locate all other `break` statements within the same `loop` that might
600+ // have affected inference.
601+ struct FindBreaks < ' tcx > {
602+ label : Option < rustc_ast:: Label > ,
603+ uses : Vec < & ' tcx hir:: Expr < ' tcx > > ,
604+ nest_depth : usize ,
605+ }
606+ impl < ' tcx > Visitor < ' tcx > for FindBreaks < ' tcx > {
607+ fn visit_expr ( & mut self , ex : & ' tcx hir:: Expr < ' tcx > ) {
608+ let nest_depth = self . nest_depth ;
609+ if let hir:: ExprKind :: Loop ( _, label, _, _) = ex. kind {
610+ if label == self . label {
611+ // Account for `'a: loop { 'a: loop {...} }`.
612+ return ;
613+ }
614+ self . nest_depth += 1 ;
615+ }
616+ if let hir:: ExprKind :: Break ( destination, _) = ex. kind
617+ && ( self . label == destination. label
618+ // Account for `loop { 'a: loop { loop { break; } } }`.
619+ || destination. label . is_none ( ) && self . nest_depth == 0 )
620+ {
621+ self . uses . push ( ex) ;
622+ }
623+ hir:: intravisit:: walk_expr ( self , ex) ;
624+ self . nest_depth = nest_depth;
625+ }
626+ }
627+ let mut expr_finder = FindBreaks { label, uses : vec ! [ ] , nest_depth : 0 } ;
628+ expr_finder. visit_block ( block) ;
629+ let mut exit = false ;
630+ for ex in expr_finder. uses {
631+ let hir:: ExprKind :: Break ( _, val) = ex. kind else {
632+ continue ;
633+ } ;
634+ let ty = match val {
635+ Some ( val) => {
636+ match self . typeck_results . borrow ( ) . expr_ty_adjusted_opt ( val) {
637+ None => continue ,
638+ Some ( ty) => ty,
639+ }
640+ }
641+ None => self . tcx . types . unit ,
642+ } ;
643+ if self . can_eq ( self . param_env , ty, expected) {
644+ err. span_label (
645+ ex. span ,
646+ format ! ( "expected because of this `break`" ) ,
647+ ) ;
648+ exit = true ;
649+ }
650+ }
651+ if exit {
652+ break ' outer;
653+ }
585654 }
586- break ;
587655 }
588656 }
589657 }
0 commit comments