From 0585475efdd1cac3a1143f4a349f3de057635efc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 17 Oct 2019 13:16:24 -0700 Subject: [PATCH] Avoid ICE when checking `Destination` of `break` inside a closure --- src/librustc_typeck/check/expr.rs | 14 ++++++++++++-- src/librustc_typeck/check/mod.rs | 12 +++++++++--- src/test/ui/break-outside-loop.rs | 8 ++++++++ src/test/ui/break-outside-loop.stderr | 10 +++++++++- 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index ad46a443b8ffa..a8ec2c393a59c 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -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); @@ -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 diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 7475b9cc3b327..6943e261209a2 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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, + } } } diff --git a/src/test/ui/break-outside-loop.rs b/src/test/ui/break-outside-loop.rs index c424c25c646bd..a6f9d0423d082 100644 --- a/src/test/ui/break-outside-loop.rs +++ b/src/test/ui/break-outside-loop.rs @@ -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 + }; + } } diff --git a/src/test/ui/break-outside-loop.stderr b/src/test/ui/break-outside-loop.stderr index 8b686356055a3..8e300fd848dab 100644 --- a/src/test/ui/break-outside-loop.stderr +++ b/src/test/ui/break-outside-loop.stderr @@ -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`.