about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs86
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs19
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs4
3 files changed, 91 insertions, 18 deletions
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index c08629a5269..31e773585d1 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,16 +540,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return;
         };
         let mut parent_id = self.tcx.hir().parent_id(expr.hir_id);
-        loop {
+        let mut parent;
+        'outer: 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::Stmt(hir::Stmt { kind: hir::StmtKind::Semi(&ref p), .. })
+                | hir::Node::Expr(&ref p),
+            ) = self.tcx.hir().find(parent_id)
+            else {
                 break;
             };
-            parent_id = self.tcx.hir().parent_id(parent.hir_id);
+            parent = p;
+            parent_id = self.tcx.hir().parent_id(parent_id);
             let hir::ExprKind::Break(destination, _) = parent.kind else {
                 continue;
             };
-            let mut parent_id = parent.hir_id;
+            let mut parent_id = parent_id;
+            let mut direct = false;
             loop {
                 // Climb the HIR tree to find the (desugared) `loop` this `break` corresponds to.
                 let parent = match self.tcx.hir().find(parent_id) {
@@ -565,14 +572,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         parent_id = self.tcx.hir().parent_id(*hir_id);
                         parent
                     }
-                    Some(hir::Node::Block(hir::Block { .. })) => {
+                    Some(hir::Node::Block(_)) => {
                         parent_id = self.tcx.hir().parent_id(parent_id);
                         parent
                     }
                     _ => break,
                 };
-                if let hir::ExprKind::Loop(_, label, _, span) = parent.kind
-                    && destination.label == label
+                if let hir::ExprKind::Loop(..) = parent.kind {
+                    // When you have `'a: loop { break; }`, the `break` corresponds to the labeled
+                    // loop, so we need to account for that.
+                    direct = !direct;
+                }
+                if let hir::ExprKind::Loop(block, label, _, span) = parent.kind
+                    && (destination.label == label || direct)
                 {
                     if let Some((reason_span, message)) =
                         self.maybe_get_coercion_reason(parent_id, parent.span)
@@ -582,8 +594,64 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             span,
                             format!("this loop is expected to be of type `{expected}`"),
                         );
+                        break 'outer;
+                    } else {
+                        // Locate all other `break` statements within the same `loop` that might
+                        // have affected inference.
+                        struct FindBreaks<'tcx> {
+                            label: Option<rustc_ast::Label>,
+                            uses: Vec<&'tcx hir::Expr<'tcx>>,
+                            nest_depth: usize,
+                        }
+                        impl<'tcx> Visitor<'tcx> for FindBreaks<'tcx> {
+                            fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
+                                let nest_depth = self.nest_depth;
+                                if let hir::ExprKind::Loop(_, label, _, _) = ex.kind {
+                                    if label == self.label {
+                                        // Account for `'a: loop { 'a: loop {...} }`.
+                                        return;
+                                    }
+                                    self.nest_depth += 1;
+                                }
+                                if let hir::ExprKind::Break(destination, _) = ex.kind
+                                    && (self.label == destination.label
+                                        // Account for `loop { 'a: loop { loop { break; } } }`.
+                                        || destination.label.is_none() && self.nest_depth == 0)
+                                {
+                                    self.uses.push(ex);
+                                }
+                                hir::intravisit::walk_expr(self, ex);
+                                self.nest_depth = nest_depth;
+                            }
+                        }
+                        let mut expr_finder = FindBreaks { label, uses: vec![], nest_depth: 0 };
+                        expr_finder.visit_block(block);
+                        let mut exit = false;
+                        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`"),
+                                );
+                                exit = true;
+                            }
+                        }
+                        if exit {
+                            break 'outer;
+                        }
                     }
-                    break;
                 }
             }
         }
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 4ad14ce3059..eead4da5e3e 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -43,7 +43,10 @@ use rustc_infer::traits::query::NoSolution;
 use rustc_infer::traits::ObligationCause;
 use rustc_middle::middle::stability;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
-use rustc_middle::ty::error::TypeError::FieldMisMatch;
+use rustc_middle::ty::error::{
+    ExpectedFound,
+    TypeError::{FieldMisMatch, Sorts},
+};
 use rustc_middle::ty::GenericArgsRef;
 use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitableExt};
 use rustc_session::errors::ExprParenthesesNeeded;
@@ -664,15 +667,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             self.suggest_mismatched_types_on_tail(
                                 &mut err, expr, ty, e_ty, target_id,
                             );
+                            let error = Some(Sorts(ExpectedFound { expected: ty, found: e_ty }));
+                            self.annotate_loop_expected_due_to_inference(&mut err, expr, error);
                             if let Some(val) = ty_kind_suggestion(ty) {
-                                let label = destination
-                                    .label
-                                    .map(|l| format!(" {}", l.ident))
-                                    .unwrap_or_else(String::new);
-                                err.span_suggestion(
-                                    expr.span,
+                                err.span_suggestion_verbose(
+                                    expr.span.shrink_to_hi(),
                                     "give it a value of the expected type",
-                                    format!("break{label} {val}"),
+                                    format!(" {val}"),
                                     Applicability::HasPlaceholders,
                                 );
                             }
@@ -717,7 +718,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // ... except when we try to 'break rust;'.
                 // ICE this expression in particular (see #43162).
                 if let ExprKind::Path(QPath::Resolved(_, path)) = e.kind {
-                    if path.segments.len() == 1 && path.segments[0].ident.name == sym::rust {
+                    if let [segment] = path.segments && segment.ident.name == sym::rust {
                         fatally_break_rust(self.tcx);
                     }
                 }
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 41f815a812a..abb68989218 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -65,6 +65,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let expr = expr.peel_drop_temps();
         self.suggest_missing_semicolon(err, expr, expected, false);
         let mut pointing_at_return_type = false;
+        if let hir::ExprKind::Break(..) = expr.kind {
+            // `break` type mismatches provide better context for tail `loop` expressions.
+            return false;
+        }
         if let Some((fn_id, fn_decl, can_suggest)) = self.get_fn_decl(blk_id) {
             pointing_at_return_type = self.suggest_missing_return_type(
                 err,