about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2023-09-25 18:21:52 +0000
committerEsteban Küber <esteban@kuber.com.ar>2023-09-25 18:21:52 +0000
commit58adfd84e29c268214928373ba61f8afbce75acf (patch)
treeeed2becfcfb59d5cdf68ebe41b172e76fc876cef
parent2e0ad2025fbae45f58149c321d2492a36808fea4 (diff)
downloadrust-58adfd84e29c268214928373ba61f8afbce75acf.tar.gz
rust-58adfd84e29c268214928373ba61f8afbce75acf.zip
Account for more cases of nested `loop`s for `break` type mismatches
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs30
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs4
-rw-r--r--tests/ui/loops/loop-break-value.rs24
-rw-r--r--tests/ui/loops/loop-break-value.stderr35
-rw-r--r--tests/ui/type/type-error-break-tail.stderr3
5 files changed, 78 insertions, 18 deletions
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index 6047461e0a6..0ff4bd4f5e4 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -541,17 +541,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         };
         let mut parent_id = self.tcx.hir().parent_id(expr.hir_id);
         let mut parent;
-        loop {
+        'outer: loop {
             // Climb the HIR tree to see if the current `Expr` is part of a `break;` statement.
-            let Some(hir::Node::Expr(p)) = 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 = p;
-            parent_id = self.tcx.hir().parent_id(parent.hir_id);
+            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) {
@@ -567,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(..) = 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(_, label, _, span) = parent.kind
-                    && destination.label == label
+                    && (destination.label == label || direct)
                 {
                     if let Some((reason_span, message)) =
                         self.maybe_get_coercion_reason(parent_id, parent.span)
@@ -588,20 +598,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         // Locate all other `break` statements within the same `loop` that might
                         // have affected inference.
                         struct FindBreaks<'tcx> {
-                            destination: hir::Destination,
+                            label: Option<rustc_ast::Label>,
                             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.label == destination.label
                                 {
                                     self.uses.push(ex);
                                 }
                                 hir::intravisit::walk_expr(self, ex);
                             }
                         }
-                        let mut expr_finder = FindBreaks { destination, uses: vec![] };
+                        let mut expr_finder = FindBreaks { label, uses: vec![] };
                         expr_finder.visit_expr(parent);
                         for ex in expr_finder.uses {
                             let hir::ExprKind::Break(_, val) = ex.kind else {
@@ -624,7 +634,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             }
                         }
                     }
-                    break;
+                    break 'outer;
                 }
             }
         }
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,
diff --git a/tests/ui/loops/loop-break-value.rs b/tests/ui/loops/loop-break-value.rs
index f334f9d464a..e38a5aa29eb 100644
--- a/tests/ui/loops/loop-break-value.rs
+++ b/tests/ui/loops/loop-break-value.rs
@@ -95,6 +95,30 @@ fn main() {
         break LOOP;
         //~^ ERROR cannot find value `LOOP` in this scope
     }
+
+    let _ = 'a: loop {
+        loop {
+            break; // This doesn't affect the expected break type of the 'a loop
+            loop {
+                loop {
+                    break 'a 1;
+                }
+            }
+        }
+        break; //~ ERROR mismatched types
+    };
+    let _ = 'a: loop {
+        loop {
+            break; // This doesn't affect the expected break type of the 'a loop
+            loop {
+                loop {
+                    break 'a 1;
+                }
+            }
+        }
+        break 'a; //~ ERROR mismatched types
+    };
+
     loop { // point at the return type
         break 2; //~ ERROR mismatched types
     }
diff --git a/tests/ui/loops/loop-break-value.stderr b/tests/ui/loops/loop-break-value.stderr
index 9c76266a9de..39d75ad1490 100644
--- a/tests/ui/loops/loop-break-value.stderr
+++ b/tests/ui/loops/loop-break-value.stderr
@@ -164,11 +164,6 @@ LL |         break "asdf";
 error[E0308]: mismatched types
   --> $DIR/loop-break-value.rs:21:31
    |
-LL |     let _: i32 = 'outer_loop: loop {
-   |         -                     ---- this loop is expected to be of type `i32`
-   |         |
-   |         expected because of this assignment
-LL |         loop {
 LL |             break 'outer_loop "nope";
    |                               ^^^^^^ expected `i32`, found `&str`
 
@@ -202,6 +197,8 @@ LL |         break 2;
 error[E0308]: mismatched types
   --> $DIR/loop-break-value.rs:90:9
    |
+LL |         break 2;
+   |         ------- expected because of this `break`
 LL |         break;
    |         ^^^^^
    |         |
@@ -209,7 +206,31 @@ LL |         break;
    |         help: give it a value of the expected type: `break value`
 
 error[E0308]: mismatched types
-  --> $DIR/loop-break-value.rs:99:15
+  --> $DIR/loop-break-value.rs:108:9
+   |
+LL |                     break 'a 1;
+   |                     ---------- expected because of this `break`
+...
+LL |         break;
+   |         ^^^^^
+   |         |
+   |         expected integer, found `()`
+   |         help: give it a value of the expected type: `break value`
+
+error[E0308]: mismatched types
+  --> $DIR/loop-break-value.rs:119:9
+   |
+LL |                     break 'a 1;
+   |                     ---------- expected because of this `break`
+...
+LL |         break 'a;
+   |         ^^^^^^^^
+   |         |
+   |         expected integer, found `()`
+   |         help: give it a value of the expected type: `break 'a value`
+
+error[E0308]: mismatched types
+  --> $DIR/loop-break-value.rs:123:15
    |
 LL | fn main() {
    |           - expected `()` because of this return type
@@ -219,7 +240,7 @@ LL |     loop { // point at the return type
 LL |         break 2;
    |               ^ expected `()`, found integer
 
-error: aborting due to 18 previous errors; 1 warning emitted
+error: aborting due to 20 previous errors; 1 warning emitted
 
 Some errors have detailed explanations: E0308, E0425, E0571.
 For more information about an error, try `rustc --explain E0308`.
diff --git a/tests/ui/type/type-error-break-tail.stderr b/tests/ui/type/type-error-break-tail.stderr
index 16dc6475c6f..4bae046de86 100644
--- a/tests/ui/type/type-error-break-tail.stderr
+++ b/tests/ui/type/type-error-break-tail.stderr
@@ -2,8 +2,9 @@ error[E0308]: mismatched types
   --> $DIR/type-error-break-tail.rs:3:20
    |
 LL | fn loop_ending() -> i32 {
-   |                     --- expected `i32` because of return type
+   |                     --- expected `i32` because of this return type
 LL |     loop {
+   |     ---- this loop is expected to be of type `i32`
 LL |         if false { break; }
    |                    ^^^^^
    |                    |