Avoid ICE when checking Destination of break inside a closure

This commit is contained in:
Esteban Küber 2019-10-17 13:16:24 -07:00
parent 237d54ff6c
commit 0585475efd
4 changed files with 38 additions and 6 deletions

View File

@ -566,7 +566,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// the `enclosing_loops` field and let's coerce the // the `enclosing_loops` field and let's coerce the
// type of `expr_opt` into what is expected. // type of `expr_opt` into what is expected.
let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); 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 mut coerce) = ctxt.coerce {
if let Some(ref e) = expr_opt { if let Some(ref e) = expr_opt {
coerce.coerce(self, &cause, e, e_ty); coerce.coerce(self, &cause, e, e_ty);
@ -592,7 +602,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} }
} else { } else {
// If `ctxt.coerce` is `None`, we can just ignore // 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 // either this was a break *without* a value, in
// which case it is always a legal type (`()`), or // which case it is always a legal type (`()`), or
// else an error would have been flagged by the // else an error would have been flagged by the

View File

@ -536,10 +536,16 @@ pub struct EnclosingBreakables<'tcx> {
impl<'tcx> EnclosingBreakables<'tcx> { impl<'tcx> EnclosingBreakables<'tcx> {
fn find_breakable(&mut self, target_id: hir::HirId) -> &mut BreakableCtxt<'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); 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,
}
} }
} }

View File

@ -22,4 +22,12 @@ fn main() {
let rs: Foo = Foo{t: pth}; let rs: Foo = Foo{t: pth};
let unconstrained = break; //~ ERROR: `break` outside of a loop 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
};
}
} }

View File

@ -33,7 +33,15 @@ error[E0268]: `break` outside of a loop
LL | let unconstrained = break; LL | let unconstrained = break;
| ^^^^^ cannot `break` outside of a loop | ^^^^^ 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. Some errors have detailed explanations: E0267, E0268.
For more information about an error, try `rustc --explain E0267`. For more information about an error, try `rustc --explain E0267`.