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, 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`.