about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPietro Albini <pietro@pietroalbini.org>2018-06-26 11:35:38 +0200
committerGitHub <noreply@github.com>2018-06-26 11:35:38 +0200
commit7262824128f880a86e2af19ede5607dd58603526 (patch)
tree66dee72eb64d71a55c041f9d24f8131153ed827d
parentb71f6df5dd74804682a500ca7eb251e34e58b1cb (diff)
parentc3d6ee9e7b652546b892bc2eac56896a8a39415a (diff)
downloadrust-7262824128f880a86e2af19ede5607dd58603526.tar.gz
rust-7262824128f880a86e2af19ede5607dd58603526.zip
Rollup merge of #51731 - varkor:closure-array-break-length, r=estebank
Fix ICEs when using continue as an array length inside closures (inside loop conditions)

Fixes #51707.
Fixes #51708.

r? @estebank
-rw-r--r--src/librustc/hir/lowering.rs40
-rw-r--r--src/librustc_mir/build/scope.rs6
-rw-r--r--src/librustc_typeck/check/mod.rs9
-rw-r--r--src/test/mir-opt/end_region_6.rs6
-rw-r--r--src/test/mir-opt/end_region_7.rs6
-rw-r--r--src/test/mir-opt/validate_1.rs6
-rw-r--r--src/test/mir-opt/validate_5.rs6
-rw-r--r--src/test/ui/closure-array-break-length.rs17
-rw-r--r--src/test/ui/closure-array-break-length.stderr22
9 files changed, 87 insertions, 31 deletions
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 02046edd218..484c41b3a79 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -3536,12 +3536,22 @@ impl<'a> LoweringContext<'a> {
                         this.expr_block(block, ThinVec::new())
                     })
                 })
-            },
+            }
             ExprKind::Closure(
-                capture_clause, asyncness, movability, ref decl, ref body, fn_decl_span) =>
-            {
-                self.with_new_scopes(|this| {
-                    if let IsAsync::Async(async_closure_node_id) = asyncness {
+                capture_clause, asyncness, movability, ref decl, ref body, fn_decl_span
+            ) => {
+                if let IsAsync::Async(async_closure_node_id) = asyncness {
+                    let outer_decl = FnDecl {
+                        inputs: decl.inputs.clone(),
+                        output: FunctionRetTy::Default(fn_decl_span),
+                        variadic: false,
+                    };
+                    // We need to lower the declaration outside the new scope, because we
+                    // have to conserve the state of being inside a loop condition for the
+                    // closure argument types.
+                    let fn_decl = self.lower_fn_decl(&outer_decl, None, false, false);
+
+                    self.with_new_scopes(|this| {
                         // FIXME(cramertj) allow `async` non-`move` closures with
                         if capture_clause == CaptureBy::Ref &&
                             !decl.inputs.is_empty()
@@ -3561,11 +3571,6 @@ impl<'a> LoweringContext<'a> {
 
                         // Transform `async |x: u8| -> X { ... }` into
                         // `|x: u8| future_from_generator(|| -> X { ... })`
-                        let outer_decl = FnDecl {
-                            inputs: decl.inputs.clone(),
-                            output: FunctionRetTy::Default(fn_decl_span),
-                            variadic: false,
-                        };
                         let body_id = this.lower_body(Some(&outer_decl), |this| {
                             let async_ret_ty = if let FunctionRetTy::Ty(ty) = &decl.output {
                                 Some(&**ty)
@@ -3579,12 +3584,17 @@ impl<'a> LoweringContext<'a> {
                         });
                         hir::ExprClosure(
                             this.lower_capture_clause(capture_clause),
-                            this.lower_fn_decl(&outer_decl, None, false, false),
+                            fn_decl,
                             body_id,
                             fn_decl_span,
                             None,
                         )
-                    } else {
+                    })
+                } else {
+                    // Lower outside new scope to preserve `is_in_loop_condition`.
+                    let fn_decl = self.lower_fn_decl(decl, None, false, false);
+
+                    self.with_new_scopes(|this| {
                         let mut is_generator = false;
                         let body_id = this.lower_body(Some(decl), |this| {
                             let e = this.lower_expr(body);
@@ -3618,13 +3628,13 @@ impl<'a> LoweringContext<'a> {
                         };
                         hir::ExprClosure(
                             this.lower_capture_clause(capture_clause),
-                            this.lower_fn_decl(decl, None, false, false),
+                            fn_decl,
                             body_id,
                             fn_decl_span,
                             generator_option,
                         )
-                    }
-                })
+                    })
+                }
             }
             ExprKind::Block(ref blk, opt_label) => {
                 hir::ExprBlock(self.lower_block(blk,
diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs
index ee0a087a1dd..b9d6486d917 100644
--- a/src/librustc_mir/build/scope.rs
+++ b/src/librustc_mir/build/scope.rs
@@ -540,12 +540,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     // ==============
     /// Finds the breakable scope for a given label. This is used for
     /// resolving `break` and `continue`.
-    pub fn find_breakable_scope(&mut self,
+    pub fn find_breakable_scope(&self,
                            span: Span,
                            label: region::Scope)
-                           -> &mut BreakableScope<'tcx> {
+                           -> &BreakableScope<'tcx> {
         // find the loop-scope with the correct id
-        self.breakable_scopes.iter_mut()
+        self.breakable_scopes.iter()
             .rev()
             .filter(|breakable_scope| breakable_scope.region_scope == label)
             .next()
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index e84586520b1..889073f6b4c 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -3846,7 +3846,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 }
 
             }
-            hir::ExprContinue(_) => { tcx.types.never }
+            hir::ExprContinue(destination) => {
+                if let Ok(_) = destination.target_id {
+                    tcx.types.never
+                } else {
+                    // There was an error, make typecheck fail
+                    tcx.types.err
+                }
+            }
             hir::ExprRet(ref expr_opt) => {
                 if self.ret_coercion.is_none() {
                     struct_span_err!(self.tcx.sess, expr.span, E0572,
diff --git a/src/test/mir-opt/end_region_6.rs b/src/test/mir-opt/end_region_6.rs
index dadc755eb8c..30ba14c1bab 100644
--- a/src/test/mir-opt/end_region_6.rs
+++ b/src/test/mir-opt/end_region_6.rs
@@ -68,17 +68,17 @@ fn foo<F>(f: F) where F: FnOnce() -> i32 {
 // fn main::{{closure}}(_1: [closure@NodeId(22) d:&'19s D]) -> i32 {
 //     let mut _0: i32;
 //     ...
-//     let _2: &'15_0rs D;
+//     let _2: &'16_0rs D;
 //     ...
 //     let mut _3: i32;
 //     bb0: {
 //         StorageLive(_2);
-//         _2 = &'15_0rs (*(_1.0: &'19s D));
+//         _2 = &'16_0rs (*(_1.0: &'19s D));
 //         StorageLive(_3);
 //         _3 = ((*_2).0: i32);
 //         _0 = move _3;
 //         StorageDead(_3);
-//         EndRegion('15_0rs);
+//         EndRegion('16_0rs);
 //         StorageDead(_2);
 //         return;
 //     }
diff --git a/src/test/mir-opt/end_region_7.rs b/src/test/mir-opt/end_region_7.rs
index 1426174b482..6d6afa25ae3 100644
--- a/src/test/mir-opt/end_region_7.rs
+++ b/src/test/mir-opt/end_region_7.rs
@@ -76,17 +76,17 @@ fn foo<F>(f: F) where F: FnOnce() -> i32 {
 // fn main::{{closure}}(_1: [closure@NodeId(22) d:D]) -> i32 {
 //     let mut _0: i32;
 //     ...
-//     let _2: &'15_0rs D;
+//     let _2: &'16_0rs D;
 //     ...
 //     let mut _3: i32;
 //     bb0: {
 //         StorageLive(_2);
-//         _2 = &'15_0rs (_1.0: D);
+//         _2 = &'16_0rs (_1.0: D);
 //         StorageLive(_3);
 //         _3 = ((*_2).0: i32);
 //         _0 = move _3;
 //         StorageDead(_3);
-//         EndRegion('15_0rs);
+//         EndRegion('16_0rs);
 //         StorageDead(_2);
 //         drop(_1) -> [return: bb2, unwind: bb1];
 //     }
diff --git a/src/test/mir-opt/validate_1.rs b/src/test/mir-opt/validate_1.rs
index e6cd5355000..c331276aade 100644
--- a/src/test/mir-opt/validate_1.rs
+++ b/src/test/mir-opt/validate_1.rs
@@ -64,14 +64,14 @@ fn main() {
 //     bb0: {
 //         Validate(Acquire, [_1: &ReFree(DefId(0/1:11 ~ validate_1[317d]::main[0]::{{closure}}[0]), BrEnv) [closure@NodeId(50)], _2: &ReFree(DefId(0/1:11 ~ validate_1[317d]::main[0]::{{closure}}[0]), BrAnon(0)) mut i32]);
 //         StorageLive(_3);
-//         Validate(Suspend(ReScope(Remainder(BlockRemainder { block: ItemLocalId(22), first_statement_index: 0 }))), [(*_2): i32]);
+//         Validate(Suspend(ReScope(Remainder(BlockRemainder { block: ItemLocalId(25), first_statement_index: 0 }))), [(*_2): i32]);
 //         _3 = &ReErased (*_2);
-//         Validate(Acquire, [(*_3): i32/ReScope(Remainder(BlockRemainder { block: ItemLocalId(22), first_statement_index: 0 })) (imm)]);
+//         Validate(Acquire, [(*_3): i32/ReScope(Remainder(BlockRemainder { block: ItemLocalId(25), first_statement_index: 0 })) (imm)]);
 //         StorageLive(_4);
 //         _4 = (*_3);
 //         _0 = move _4;
 //         StorageDead(_4);
-//         EndRegion(ReScope(Remainder(BlockRemainder { block: ItemLocalId(22), first_statement_index: 0 })));
+//         EndRegion(ReScope(Remainder(BlockRemainder { block: ItemLocalId(25), first_statement_index: 0 })));
 //         StorageDead(_3);
 //         return;
 //     }
diff --git a/src/test/mir-opt/validate_5.rs b/src/test/mir-opt/validate_5.rs
index d8d83fb5b45..b4d4479bab9 100644
--- a/src/test/mir-opt/validate_5.rs
+++ b/src/test/mir-opt/validate_5.rs
@@ -53,12 +53,12 @@ fn main() {
 //         StorageLive(_3);
 //         StorageLive(_4);
 //         StorageLive(_5);
-//         Validate(Suspend(ReScope(Node(ItemLocalId(9)))), [(*_2): i32]);
+//         Validate(Suspend(ReScope(Node(ItemLocalId(12)))), [(*_2): i32]);
 //         _5 = &ReErased mut (*_2);
-//         Validate(Acquire, [(*_5): i32/ReScope(Node(ItemLocalId(9)))]);
+//         Validate(Acquire, [(*_5): i32/ReScope(Node(ItemLocalId(12)))]);
 //         _4 = move _5 as *mut i32 (Misc);
 //         _3 = move _4;
-//         EndRegion(ReScope(Node(ItemLocalId(9))));
+//         EndRegion(ReScope(Node(ItemLocalId(12))));
 //         StorageDead(_4);
 //         StorageDead(_5);
 //         Validate(Release, [_0: bool, _3: *mut i32]);
diff --git a/src/test/ui/closure-array-break-length.rs b/src/test/ui/closure-array-break-length.rs
new file mode 100644
index 00000000000..2e99921956a
--- /dev/null
+++ b/src/test/ui/closure-array-break-length.rs
@@ -0,0 +1,17 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    |_: [_; continue]| {}; //~ ERROR: `continue` outside of loop
+
+    while |_: [_; continue]| {} {} //~ ERROR: `break` or `continue` with no label
+
+    while |_: [_; break]| {} {} //~ ERROR: `break` or `continue` with no label
+}
diff --git a/src/test/ui/closure-array-break-length.stderr b/src/test/ui/closure-array-break-length.stderr
new file mode 100644
index 00000000000..139153992e2
--- /dev/null
+++ b/src/test/ui/closure-array-break-length.stderr
@@ -0,0 +1,22 @@
+error[E0268]: `continue` outside of loop
+  --> $DIR/closure-array-break-length.rs:12:13
+   |
+LL |     |_: [_; continue]| {}; //~ ERROR: `continue` outside of loop
+   |             ^^^^^^^^ cannot break outside of a loop
+
+error[E0590]: `break` or `continue` with no label in the condition of a `while` loop
+  --> $DIR/closure-array-break-length.rs:14:19
+   |
+LL |     while |_: [_; continue]| {} {} //~ ERROR: `break` or `continue` with no label
+   |                   ^^^^^^^^ unlabeled `continue` in the condition of a `while` loop
+
+error[E0590]: `break` or `continue` with no label in the condition of a `while` loop
+  --> $DIR/closure-array-break-length.rs:16:19
+   |
+LL |     while |_: [_; break]| {} {} //~ ERROR: `break` or `continue` with no label
+   |                   ^^^^^ unlabeled `break` in the condition of a `while` loop
+
+error: aborting due to 3 previous errors
+
+Some errors occurred: E0268, E0590.
+For more information about an error, try `rustc --explain E0268`.