about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2023-09-25 21:57:22 +0000
committerEsteban Küber <esteban@kuber.com.ar>2023-09-25 21:57:22 +0000
commit3747ef5d6f66525fd42d38b068fd81f163adb482 (patch)
tree6006275413d0a7472809227bf4f502400b53bd47
parent58adfd84e29c268214928373ba61f8afbce75acf (diff)
downloadrust-3747ef5d6f66525fd42d38b068fd81f163adb482.tar.gz
rust-3747ef5d6f66525fd42d38b068fd81f163adb482.zip
Handle all arbitrary `loop` nesting in `break` type errors
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs27
-rw-r--r--tests/ui/loops/loop-break-value.rs36
-rw-r--r--tests/ui/loops/loop-break-value.stderr82
3 files changed, 137 insertions, 8 deletions
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index 0ff4bd4f5e4..31e773585d1 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -583,7 +583,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     // loop, so we need to account for that.
                     direct = !direct;
                 }
-                if let hir::ExprKind::Loop(_, label, _, span) = parent.kind
+                if let hir::ExprKind::Loop(block, label, _, span) = parent.kind
                     && (destination.label == label || direct)
                 {
                     if let Some((reason_span, message)) =
@@ -594,25 +594,39 @@ 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
+                                    && (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![] };
-                        expr_finder.visit_expr(parent);
+                        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;
@@ -631,10 +645,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                     ex.span,
                                     format!("expected because of this `break`"),
                                 );
+                                exit = true;
                             }
                         }
+                        if exit {
+                            break 'outer;
+                        }
                     }
-                    break 'outer;
                 }
             }
         }
diff --git a/tests/ui/loops/loop-break-value.rs b/tests/ui/loops/loop-break-value.rs
index e38a5aa29eb..c35200520cb 100644
--- a/tests/ui/loops/loop-break-value.rs
+++ b/tests/ui/loops/loop-break-value.rs
@@ -107,6 +107,7 @@ fn main() {
         }
         break; //~ ERROR mismatched types
     };
+
     let _ = 'a: loop {
         loop {
             break; // This doesn't affect the expected break type of the 'a loop
@@ -119,6 +120,41 @@ fn main() {
         break 'a; //~ ERROR mismatched types
     };
 
+    loop {
+        break;
+        let _ = loop {
+            break 2;
+            loop {
+                break;
+            }
+        };
+        break 2; //~ ERROR mismatched types
+    }
+
+    'a: loop {
+        break;
+        let _ = 'a: loop {
+            //~^ WARNING label name `'a` shadows a label name that is already in scope
+            break 2;
+            loop {
+                break 'a; //~ ERROR mismatched types
+            }
+        };
+        break 2; //~ ERROR mismatched types
+    }
+
+    'a: loop {
+        break;
+        let _ = 'a: loop {
+            //~^ WARNING label name `'a` shadows a label name that is already in scope
+            break 'a 2;
+            loop {
+                break 'a; //~ ERROR mismatched types
+            }
+        };
+        break 2; //~ 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 39d75ad1490..4c312c2fe38 100644
--- a/tests/ui/loops/loop-break-value.stderr
+++ b/tests/ui/loops/loop-break-value.stderr
@@ -1,3 +1,21 @@
+warning: label name `'a` shadows a label name that is already in scope
+  --> $DIR/loop-break-value.rs:136:17
+   |
+LL |     'a: loop {
+   |     -- first declared here
+LL |         break;
+LL |         let _ = 'a: loop {
+   |                 ^^ label `'a` already in scope
+
+warning: label name `'a` shadows a label name that is already in scope
+  --> $DIR/loop-break-value.rs:148:17
+   |
+LL |     'a: loop {
+   |     -- first declared here
+LL |         break;
+LL |         let _ = 'a: loop {
+   |                 ^^ label `'a` already in scope
+
 error[E0425]: cannot find value `LOOP` in this scope
   --> $DIR/loop-break-value.rs:95:15
    |
@@ -164,12 +182,19 @@ 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`
 
 error[E0308]: mismatched types
   --> $DIR/loop-break-value.rs:73:26
    |
+LL |                 break;
+   |                 ----- expected because of this `break`
 LL |                 break 'c 123;
    |                          ^^^ expected `()`, found integer
 
@@ -218,7 +243,7 @@ LL |         break;
    |         help: give it a value of the expected type: `break value`
 
 error[E0308]: mismatched types
-  --> $DIR/loop-break-value.rs:119:9
+  --> $DIR/loop-break-value.rs:120:9
    |
 LL |                     break 'a 1;
    |                     ---------- expected because of this `break`
@@ -230,7 +255,58 @@ LL |         break 'a;
    |         help: give it a value of the expected type: `break 'a value`
 
 error[E0308]: mismatched types
-  --> $DIR/loop-break-value.rs:123:15
+  --> $DIR/loop-break-value.rs:131:15
+   |
+LL |         break;
+   |         ----- expected because of this `break`
+...
+LL |         break 2;
+   |               ^ expected `()`, found integer
+
+error[E0308]: mismatched types
+  --> $DIR/loop-break-value.rs:140:17
+   |
+LL |             break 2;
+   |             ------- expected because of this `break`
+LL |             loop {
+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:143:15
+   |
+LL |         break;
+   |         ----- expected because of this `break`
+...
+LL |         break 2;
+   |               ^ expected `()`, found integer
+
+error[E0308]: mismatched types
+  --> $DIR/loop-break-value.rs:152:17
+   |
+LL |             break 'a 2;
+   |             ---------- expected because of this `break`
+LL |             loop {
+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:155:15
+   |
+LL |         break;
+   |         ----- expected because of this `break`
+...
+LL |         break 2;
+   |               ^ expected `()`, found integer
+
+error[E0308]: mismatched types
+  --> $DIR/loop-break-value.rs:159:15
    |
 LL | fn main() {
    |           - expected `()` because of this return type
@@ -240,7 +316,7 @@ LL |     loop { // point at the return type
 LL |         break 2;
    |               ^ expected `()`, found integer
 
-error: aborting due to 20 previous errors; 1 warning emitted
+error: aborting due to 25 previous errors; 3 warnings emitted
 
 Some errors have detailed explanations: E0308, E0425, E0571.
 For more information about an error, try `rustc --explain E0308`.