diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index e34a2c6f61c2..bd936d48225c 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -548,7 +548,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); @@ -574,7 +584,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 dde86b0b6483..49e6ee10bdc1 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -533,10 +533,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 c424c25c646b..a6f9d0423d08 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 8b686356055a..8e300fd848da 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`.