diff options
| author | Esteban Küber <esteban@kuber.com.ar> | 2023-09-25 18:21:52 +0000 |
|---|---|---|
| committer | Esteban Küber <esteban@kuber.com.ar> | 2023-09-25 18:21:52 +0000 |
| commit | 58adfd84e29c268214928373ba61f8afbce75acf (patch) | |
| tree | eed2becfcfb59d5cdf68ebe41b172e76fc876cef | |
| parent | 2e0ad2025fbae45f58149c321d2492a36808fea4 (diff) | |
| download | rust-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.rs | 30 | ||||
| -rw-r--r-- | compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs | 4 | ||||
| -rw-r--r-- | tests/ui/loops/loop-break-value.rs | 24 | ||||
| -rw-r--r-- | tests/ui/loops/loop-break-value.stderr | 35 | ||||
| -rw-r--r-- | tests/ui/type/type-error-break-tail.stderr | 3 |
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; } | ^^^^^ | | |
