Skip to content

Commit 58adfd8

Browse files
committed
Account for more cases of nested loops for break type mismatches
1 parent 2e0ad20 commit 58adfd8

File tree

5 files changed

+78
-18
lines changed

5 files changed

+78
-18
lines changed

compiler/rustc_hir_typeck/src/demand.rs

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -541,17 +541,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
541541
};
542542
let mut parent_id = self.tcx.hir().parent_id(expr.hir_id);
543543
let mut parent;
544-
loop {
544+
'outer: loop {
545545
// Climb the HIR tree to see if the current `Expr` is part of a `break;` statement.
546-
let Some(hir::Node::Expr(p)) = self.tcx.hir().find(parent_id) else {
546+
let Some(
547+
hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Semi(&ref p), .. })
548+
| hir::Node::Expr(&ref p),
549+
) = self.tcx.hir().find(parent_id)
550+
else {
547551
break;
548552
};
549553
parent = p;
550-
parent_id = self.tcx.hir().parent_id(parent.hir_id);
554+
parent_id = self.tcx.hir().parent_id(parent_id);
551555
let hir::ExprKind::Break(destination, _) = parent.kind else {
552556
continue;
553557
};
554-
let mut parent_id = parent.hir_id;
558+
let mut parent_id = parent_id;
559+
let mut direct = false;
555560
loop {
556561
// Climb the HIR tree to find the (desugared) `loop` this `break` corresponds to.
557562
let parent = match self.tcx.hir().find(parent_id) {
@@ -567,14 +572,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
567572
parent_id = self.tcx.hir().parent_id(*hir_id);
568573
parent
569574
}
570-
Some(hir::Node::Block(hir::Block { .. })) => {
575+
Some(hir::Node::Block(_)) => {
571576
parent_id = self.tcx.hir().parent_id(parent_id);
572577
parent
573578
}
574579
_ => break,
575580
};
581+
if let hir::ExprKind::Loop(..) = parent.kind {
582+
// When you have `'a: loop { break; }`, the `break` corresponds to the labeled
583+
// loop, so we need to account for that.
584+
direct = !direct;
585+
}
576586
if let hir::ExprKind::Loop(_, label, _, span) = parent.kind
577-
&& destination.label == label
587+
&& (destination.label == label || direct)
578588
{
579589
if let Some((reason_span, message)) =
580590
self.maybe_get_coercion_reason(parent_id, parent.span)
@@ -588,20 +598,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
588598
// Locate all other `break` statements within the same `loop` that might
589599
// have affected inference.
590600
struct FindBreaks<'tcx> {
591-
destination: hir::Destination,
601+
label: Option<rustc_ast::Label>,
592602
uses: Vec<&'tcx hir::Expr<'tcx>>,
593603
}
594604
impl<'tcx> Visitor<'tcx> for FindBreaks<'tcx> {
595605
fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
596606
if let hir::ExprKind::Break(destination, _) = ex.kind
597-
&& self.destination.label == destination.label
607+
&& self.label == destination.label
598608
{
599609
self.uses.push(ex);
600610
}
601611
hir::intravisit::walk_expr(self, ex);
602612
}
603613
}
604-
let mut expr_finder = FindBreaks { destination, uses: vec![] };
614+
let mut expr_finder = FindBreaks { label, uses: vec![] };
605615
expr_finder.visit_expr(parent);
606616
for ex in expr_finder.uses {
607617
let hir::ExprKind::Break(_, val) = ex.kind else {
@@ -624,7 +634,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
624634
}
625635
}
626636
}
627-
break;
637+
break 'outer;
628638
}
629639
}
630640
}

compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
6565
let expr = expr.peel_drop_temps();
6666
self.suggest_missing_semicolon(err, expr, expected, false);
6767
let mut pointing_at_return_type = false;
68+
if let hir::ExprKind::Break(..) = expr.kind {
69+
// `break` type mismatches provide better context for tail `loop` expressions.
70+
return false;
71+
}
6872
if let Some((fn_id, fn_decl, can_suggest)) = self.get_fn_decl(blk_id) {
6973
pointing_at_return_type = self.suggest_missing_return_type(
7074
err,

tests/ui/loops/loop-break-value.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,30 @@ fn main() {
9595
break LOOP;
9696
//~^ ERROR cannot find value `LOOP` in this scope
9797
}
98+
99+
let _ = 'a: loop {
100+
loop {
101+
break; // This doesn't affect the expected break type of the 'a loop
102+
loop {
103+
loop {
104+
break 'a 1;
105+
}
106+
}
107+
}
108+
break; //~ ERROR mismatched types
109+
};
110+
let _ = 'a: loop {
111+
loop {
112+
break; // This doesn't affect the expected break type of the 'a loop
113+
loop {
114+
loop {
115+
break 'a 1;
116+
}
117+
}
118+
}
119+
break 'a; //~ ERROR mismatched types
120+
};
121+
98122
loop { // point at the return type
99123
break 2; //~ ERROR mismatched types
100124
}

tests/ui/loops/loop-break-value.stderr

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,6 @@ LL | break "asdf";
164164
error[E0308]: mismatched types
165165
--> $DIR/loop-break-value.rs:21:31
166166
|
167-
LL | let _: i32 = 'outer_loop: loop {
168-
| - ---- this loop is expected to be of type `i32`
169-
| |
170-
| expected because of this assignment
171-
LL | loop {
172167
LL | break 'outer_loop "nope";
173168
| ^^^^^^ expected `i32`, found `&str`
174169

@@ -202,14 +197,40 @@ LL | break 2;
202197
error[E0308]: mismatched types
203198
--> $DIR/loop-break-value.rs:90:9
204199
|
200+
LL | break 2;
201+
| ------- expected because of this `break`
205202
LL | break;
206203
| ^^^^^
207204
| |
208205
| expected integer, found `()`
209206
| help: give it a value of the expected type: `break value`
210207

211208
error[E0308]: mismatched types
212-
--> $DIR/loop-break-value.rs:99:15
209+
--> $DIR/loop-break-value.rs:108:9
210+
|
211+
LL | break 'a 1;
212+
| ---------- expected because of this `break`
213+
...
214+
LL | break;
215+
| ^^^^^
216+
| |
217+
| expected integer, found `()`
218+
| help: give it a value of the expected type: `break value`
219+
220+
error[E0308]: mismatched types
221+
--> $DIR/loop-break-value.rs:119:9
222+
|
223+
LL | break 'a 1;
224+
| ---------- expected because of this `break`
225+
...
226+
LL | break 'a;
227+
| ^^^^^^^^
228+
| |
229+
| expected integer, found `()`
230+
| help: give it a value of the expected type: `break 'a value`
231+
232+
error[E0308]: mismatched types
233+
--> $DIR/loop-break-value.rs:123:15
213234
|
214235
LL | fn main() {
215236
| - expected `()` because of this return type
@@ -219,7 +240,7 @@ LL | loop { // point at the return type
219240
LL | break 2;
220241
| ^ expected `()`, found integer
221242

222-
error: aborting due to 18 previous errors; 1 warning emitted
243+
error: aborting due to 20 previous errors; 1 warning emitted
223244

224245
Some errors have detailed explanations: E0308, E0425, E0571.
225246
For more information about an error, try `rustc --explain E0308`.

tests/ui/type/type-error-break-tail.stderr

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ error[E0308]: mismatched types
22
--> $DIR/type-error-break-tail.rs:3:20
33
|
44
LL | fn loop_ending() -> i32 {
5-
| --- expected `i32` because of return type
5+
| --- expected `i32` because of this return type
66
LL | loop {
7+
| ---- this loop is expected to be of type `i32`
78
LL | if false { break; }
89
| ^^^^^
910
| |

0 commit comments

Comments
 (0)