Skip to content

Commit 3747ef5

Browse files
committed
Handle all arbitrary loop nesting in break type errors
1 parent 58adfd8 commit 3747ef5

File tree

3 files changed

+137
-8
lines changed

3 files changed

+137
-8
lines changed

compiler/rustc_hir_typeck/src/demand.rs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
583583
// loop, so we need to account for that.
584584
direct = !direct;
585585
}
586-
if let hir::ExprKind::Loop(_, label, _, span) = parent.kind
586+
if let hir::ExprKind::Loop(block, label, _, span) = parent.kind
587587
&& (destination.label == label || direct)
588588
{
589589
if let Some((reason_span, message)) =
@@ -594,25 +594,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
594594
span,
595595
format!("this loop is expected to be of type `{expected}`"),
596596
);
597+
break 'outer;
597598
} else {
598599
// Locate all other `break` statements within the same `loop` that might
599600
// have affected inference.
600601
struct FindBreaks<'tcx> {
601602
label: Option<rustc_ast::Label>,
602603
uses: Vec<&'tcx hir::Expr<'tcx>>,
604+
nest_depth: usize,
603605
}
604606
impl<'tcx> Visitor<'tcx> for FindBreaks<'tcx> {
605607
fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
608+
let nest_depth = self.nest_depth;
609+
if let hir::ExprKind::Loop(_, label, _, _) = ex.kind {
610+
if label == self.label {
611+
// Account for `'a: loop { 'a: loop {...} }`.
612+
return;
613+
}
614+
self.nest_depth += 1;
615+
}
606616
if let hir::ExprKind::Break(destination, _) = ex.kind
607-
&& self.label == destination.label
617+
&& (self.label == destination.label
618+
// Account for `loop { 'a: loop { loop { break; } } }`.
619+
|| destination.label.is_none() && self.nest_depth == 0)
608620
{
609621
self.uses.push(ex);
610622
}
611623
hir::intravisit::walk_expr(self, ex);
624+
self.nest_depth = nest_depth;
612625
}
613626
}
614-
let mut expr_finder = FindBreaks { label, uses: vec![] };
615-
expr_finder.visit_expr(parent);
627+
let mut expr_finder = FindBreaks { label, uses: vec![], nest_depth: 0 };
628+
expr_finder.visit_block(block);
629+
let mut exit = false;
616630
for ex in expr_finder.uses {
617631
let hir::ExprKind::Break(_, val) = ex.kind else {
618632
continue;
@@ -631,10 +645,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
631645
ex.span,
632646
format!("expected because of this `break`"),
633647
);
648+
exit = true;
634649
}
635650
}
651+
if exit {
652+
break 'outer;
653+
}
636654
}
637-
break 'outer;
638655
}
639656
}
640657
}

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ fn main() {
107107
}
108108
break; //~ ERROR mismatched types
109109
};
110+
110111
let _ = 'a: loop {
111112
loop {
112113
break; // This doesn't affect the expected break type of the 'a loop
@@ -119,6 +120,41 @@ fn main() {
119120
break 'a; //~ ERROR mismatched types
120121
};
121122

123+
loop {
124+
break;
125+
let _ = loop {
126+
break 2;
127+
loop {
128+
break;
129+
}
130+
};
131+
break 2; //~ ERROR mismatched types
132+
}
133+
134+
'a: loop {
135+
break;
136+
let _ = 'a: loop {
137+
//~^ WARNING label name `'a` shadows a label name that is already in scope
138+
break 2;
139+
loop {
140+
break 'a; //~ ERROR mismatched types
141+
}
142+
};
143+
break 2; //~ ERROR mismatched types
144+
}
145+
146+
'a: loop {
147+
break;
148+
let _ = 'a: loop {
149+
//~^ WARNING label name `'a` shadows a label name that is already in scope
150+
break 'a 2;
151+
loop {
152+
break 'a; //~ ERROR mismatched types
153+
}
154+
};
155+
break 2; //~ ERROR mismatched types
156+
};
157+
122158
loop { // point at the return type
123159
break 2; //~ ERROR mismatched types
124160
}

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

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1+
warning: label name `'a` shadows a label name that is already in scope
2+
--> $DIR/loop-break-value.rs:136:17
3+
|
4+
LL | 'a: loop {
5+
| -- first declared here
6+
LL | break;
7+
LL | let _ = 'a: loop {
8+
| ^^ label `'a` already in scope
9+
10+
warning: label name `'a` shadows a label name that is already in scope
11+
--> $DIR/loop-break-value.rs:148:17
12+
|
13+
LL | 'a: loop {
14+
| -- first declared here
15+
LL | break;
16+
LL | let _ = 'a: loop {
17+
| ^^ label `'a` already in scope
18+
119
error[E0425]: cannot find value `LOOP` in this scope
220
--> $DIR/loop-break-value.rs:95:15
321
|
@@ -164,12 +182,19 @@ LL | break "asdf";
164182
error[E0308]: mismatched types
165183
--> $DIR/loop-break-value.rs:21:31
166184
|
185+
LL | let _: i32 = 'outer_loop: loop {
186+
| - ---- this loop is expected to be of type `i32`
187+
| |
188+
| expected because of this assignment
189+
LL | loop {
167190
LL | break 'outer_loop "nope";
168191
| ^^^^^^ expected `i32`, found `&str`
169192

170193
error[E0308]: mismatched types
171194
--> $DIR/loop-break-value.rs:73:26
172195
|
196+
LL | break;
197+
| ----- expected because of this `break`
173198
LL | break 'c 123;
174199
| ^^^ expected `()`, found integer
175200

@@ -218,7 +243,7 @@ LL | break;
218243
| help: give it a value of the expected type: `break value`
219244

220245
error[E0308]: mismatched types
221-
--> $DIR/loop-break-value.rs:119:9
246+
--> $DIR/loop-break-value.rs:120:9
222247
|
223248
LL | break 'a 1;
224249
| ---------- expected because of this `break`
@@ -230,7 +255,58 @@ LL | break 'a;
230255
| help: give it a value of the expected type: `break 'a value`
231256

232257
error[E0308]: mismatched types
233-
--> $DIR/loop-break-value.rs:123:15
258+
--> $DIR/loop-break-value.rs:131:15
259+
|
260+
LL | break;
261+
| ----- expected because of this `break`
262+
...
263+
LL | break 2;
264+
| ^ expected `()`, found integer
265+
266+
error[E0308]: mismatched types
267+
--> $DIR/loop-break-value.rs:140:17
268+
|
269+
LL | break 2;
270+
| ------- expected because of this `break`
271+
LL | loop {
272+
LL | break 'a;
273+
| ^^^^^^^^
274+
| |
275+
| expected integer, found `()`
276+
| help: give it a value of the expected type: `break 'a value`
277+
278+
error[E0308]: mismatched types
279+
--> $DIR/loop-break-value.rs:143:15
280+
|
281+
LL | break;
282+
| ----- expected because of this `break`
283+
...
284+
LL | break 2;
285+
| ^ expected `()`, found integer
286+
287+
error[E0308]: mismatched types
288+
--> $DIR/loop-break-value.rs:152:17
289+
|
290+
LL | break 'a 2;
291+
| ---------- expected because of this `break`
292+
LL | loop {
293+
LL | break 'a;
294+
| ^^^^^^^^
295+
| |
296+
| expected integer, found `()`
297+
| help: give it a value of the expected type: `break 'a value`
298+
299+
error[E0308]: mismatched types
300+
--> $DIR/loop-break-value.rs:155:15
301+
|
302+
LL | break;
303+
| ----- expected because of this `break`
304+
...
305+
LL | break 2;
306+
| ^ expected `()`, found integer
307+
308+
error[E0308]: mismatched types
309+
--> $DIR/loop-break-value.rs:159:15
234310
|
235311
LL | fn main() {
236312
| - expected `()` because of this return type
@@ -240,7 +316,7 @@ LL | loop { // point at the return type
240316
LL | break 2;
241317
| ^ expected `()`, found integer
242318

243-
error: aborting due to 20 previous errors; 1 warning emitted
319+
error: aborting due to 25 previous errors; 3 warnings emitted
244320

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

0 commit comments

Comments
 (0)