Skip to content

Commit 6bb7449

Browse files
authored
Rollup merge of rust-lang#65518 - estebank:i-want-to-break-free, r=eddyb
Avoid ICE when checking `Destination` of `break` inside a closure Fix rust-lang#65383, fix rust-lang#62480. This is a `[regression-from-stable-to-stable]` and a fairly small change to avoid the ICE by properly handling this case.
2 parents a1514b4 + 0585475 commit 6bb7449

File tree

4 files changed

+38
-6
lines changed

4 files changed

+38
-6
lines changed

src/librustc_typeck/check/expr.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
566566
// the `enclosing_loops` field and let's coerce the
567567
// type of `expr_opt` into what is expected.
568568
let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
569-
let ctxt = enclosing_breakables.find_breakable(target_id);
569+
let ctxt = match enclosing_breakables.opt_find_breakable(target_id) {
570+
Some(ctxt) => ctxt,
571+
None => { // Avoid ICE when `break` is inside a closure (#65383).
572+
self.tcx.sess.delay_span_bug(
573+
expr.span,
574+
"break was outside loop, but no error was emitted",
575+
);
576+
return tcx.types.err;
577+
}
578+
};
579+
570580
if let Some(ref mut coerce) = ctxt.coerce {
571581
if let Some(ref e) = expr_opt {
572582
coerce.coerce(self, &cause, e, e_ty);
@@ -592,7 +602,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
592602
}
593603
} else {
594604
// If `ctxt.coerce` is `None`, we can just ignore
595-
// the type of the expresison. This is because
605+
// the type of the expression. This is because
596606
// either this was a break *without* a value, in
597607
// which case it is always a legal type (`()`), or
598608
// else an error would have been flagged by the

src/librustc_typeck/check/mod.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -536,10 +536,16 @@ pub struct EnclosingBreakables<'tcx> {
536536

537537
impl<'tcx> EnclosingBreakables<'tcx> {
538538
fn find_breakable(&mut self, target_id: hir::HirId) -> &mut BreakableCtxt<'tcx> {
539-
let ix = *self.by_id.get(&target_id).unwrap_or_else(|| {
539+
self.opt_find_breakable(target_id).unwrap_or_else(|| {
540540
bug!("could not find enclosing breakable with id {}", target_id);
541-
});
542-
&mut self.stack[ix]
541+
})
542+
}
543+
544+
fn opt_find_breakable(&mut self, target_id: hir::HirId) -> Option<&mut BreakableCtxt<'tcx>> {
545+
match self.by_id.get(&target_id) {
546+
Some(ix) => Some(&mut self.stack[*ix]),
547+
None => None,
548+
}
543549
}
544550
}
545551

src/test/ui/break-outside-loop.rs

+8
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,12 @@ fn main() {
2222
let rs: Foo = Foo{t: pth};
2323

2424
let unconstrained = break; //~ ERROR: `break` outside of a loop
25+
26+
// This used to ICE because `target_id` passed to `check_expr_break` would be the closure and
27+
// not the `loop`, which failed in the call to `find_breakable`. (#65383)
28+
'lab: loop {
29+
|| {
30+
break 'lab; //~ ERROR `break` inside of a closure
31+
};
32+
}
2533
}

src/test/ui/break-outside-loop.stderr

+9-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,15 @@ error[E0268]: `break` outside of a loop
3333
LL | let unconstrained = break;
3434
| ^^^^^ cannot `break` outside of a loop
3535

36-
error: aborting due to 5 previous errors
36+
error[E0267]: `break` inside of a closure
37+
--> $DIR/break-outside-loop.rs:30:13
38+
|
39+
LL | || {
40+
| -- enclosing closure
41+
LL | break 'lab;
42+
| ^^^^^^^^^^ cannot `break` inside of a closure
43+
44+
error: aborting due to 6 previous errors
3745

3846
Some errors have detailed explanations: E0267, E0268.
3947
For more information about an error, try `rustc --explain E0267`.

0 commit comments

Comments
 (0)