Skip to content

Commit

Permalink
Rollup merge of #65518 - estebank:i-want-to-break-free, r=eddyb
Browse files Browse the repository at this point in the history
Avoid ICE when checking `Destination` of `break` inside a closure

Fix #65383, fix #62480. This is a `[regression-from-stable-to-stable]` and a fairly small change to avoid the ICE by properly handling this case.
  • Loading branch information
Centril authored Oct 23, 2019
2 parents a1514b4 + 0585475 commit 6bb7449
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 6 deletions.
14 changes: 12 additions & 2 deletions src/librustc_typeck/check/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// the `enclosing_loops` field and let's coerce the
// type of `expr_opt` into what is expected.
let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
let ctxt = enclosing_breakables.find_breakable(target_id);
let ctxt = match enclosing_breakables.opt_find_breakable(target_id) {
Some(ctxt) => ctxt,
None => { // Avoid ICE when `break` is inside a closure (#65383).
self.tcx.sess.delay_span_bug(
expr.span,
"break was outside loop, but no error was emitted",
);
return tcx.types.err;
}
};

if let Some(ref mut coerce) = ctxt.coerce {
if let Some(ref e) = expr_opt {
coerce.coerce(self, &cause, e, e_ty);
Expand All @@ -592,7 +602,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
} else {
// If `ctxt.coerce` is `None`, we can just ignore
// the type of the expresison. This is because
// the type of the expression. This is because
// either this was a break *without* a value, in
// which case it is always a legal type (`()`), or
// else an error would have been flagged by the
Expand Down
12 changes: 9 additions & 3 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -536,10 +536,16 @@ pub struct EnclosingBreakables<'tcx> {

impl<'tcx> EnclosingBreakables<'tcx> {
fn find_breakable(&mut self, target_id: hir::HirId) -> &mut BreakableCtxt<'tcx> {
let ix = *self.by_id.get(&target_id).unwrap_or_else(|| {
self.opt_find_breakable(target_id).unwrap_or_else(|| {
bug!("could not find enclosing breakable with id {}", target_id);
});
&mut self.stack[ix]
})
}

fn opt_find_breakable(&mut self, target_id: hir::HirId) -> Option<&mut BreakableCtxt<'tcx>> {
match self.by_id.get(&target_id) {
Some(ix) => Some(&mut self.stack[*ix]),
None => None,
}
}
}

Expand Down
8 changes: 8 additions & 0 deletions src/test/ui/break-outside-loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,12 @@ fn main() {
let rs: Foo = Foo{t: pth};

let unconstrained = break; //~ ERROR: `break` outside of a loop

// This used to ICE because `target_id` passed to `check_expr_break` would be the closure and
// not the `loop`, which failed in the call to `find_breakable`. (#65383)
'lab: loop {
|| {
break 'lab; //~ ERROR `break` inside of a closure
};
}
}
10 changes: 9 additions & 1 deletion src/test/ui/break-outside-loop.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,15 @@ error[E0268]: `break` outside of a loop
LL | let unconstrained = break;
| ^^^^^ cannot `break` outside of a loop

error: aborting due to 5 previous errors
error[E0267]: `break` inside of a closure
--> $DIR/break-outside-loop.rs:30:13
|
LL | || {
| -- enclosing closure
LL | break 'lab;
| ^^^^^^^^^^ cannot `break` inside of a closure

error: aborting due to 6 previous errors

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

0 comments on commit 6bb7449

Please sign in to comment.