about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2019-10-17 13:16:24 -0700
committerEsteban Küber <esteban@kuber.com.ar>2019-10-17 18:18:29 -0700
commit0585475efdd1cac3a1143f4a349f3de057635efc (patch)
tree3bc5c8749303fc6456300bd9fb27f00ac62d9c86
parent237d54ff6c4fb3577e02d4c5af02813c11b63d01 (diff)
downloadrust-0585475efdd1cac3a1143f4a349f3de057635efc.tar.gz
rust-0585475efdd1cac3a1143f4a349f3de057635efc.zip
Avoid ICE when checking `Destination` of `break` inside a closure
-rw-r--r--src/librustc_typeck/check/expr.rs14
-rw-r--r--src/librustc_typeck/check/mod.rs12
-rw-r--r--src/test/ui/break-outside-loop.rs8
-rw-r--r--src/test/ui/break-outside-loop.stderr10
4 files changed, 38 insertions, 6 deletions
diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs
index ad46a443b8f..a8ec2c393a5 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 7475b9cc3b3..6943e261209 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 c424c25c646..a6f9d0423d0 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 8b686356055..8e300fd848d 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`.