about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-06-22 15:24:58 +0000
committerbors <bors@rust-lang.org>2017-06-22 15:24:58 +0000
commitab5bec25530aac43dfd64384b405c909b6e405e3 (patch)
treecd10ff6cf849e2c3b92dee464495dbfc83c96503
parent74fa27928aceda1362a2266d9b9bf129999bc00a (diff)
parent1409e707a2e6b152ab66a9d836e5e367f6f066cc (diff)
downloadrust-ab5bec25530aac43dfd64384b405c909b6e405e3.tar.gz
rust-ab5bec25530aac43dfd64384b405c909b6e405e3.zip
Auto merge of #42634 - Zoxc:for-desugar2, r=nikomatsakis
Change the for-loop desugar so the `break` does not affect type inference. Fixes #42618

Rewrite the `for` loop desugaring to avoid contaminating the inference results. Under the older desugaring, `for x in vec![] { .. }` would erroneously type-check, even though the type of `vec![]` is unconstrained. (written by @nikomatsakis)
-rw-r--r--src/libcore/iter/mod.rs6
-rw-r--r--src/librustc/hir/lowering.rs46
-rw-r--r--src/test/compile-fail/for-loop-unconstrained-element-type.rs19
-rw-r--r--src/test/run-pass/for-loop-lifetime-of-unbound-values.rs43
-rw-r--r--src/test/run-pass/for-loop-mut-ref-element.rs15
-rw-r--r--src/test/run-pass/for-loop-unconstrained-element-type-i32-fallback.rs20
6 files changed, 137 insertions, 12 deletions
diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs
index ee811513487..c91fd16391a 100644
--- a/src/libcore/iter/mod.rs
+++ b/src/libcore/iter/mod.rs
@@ -191,10 +191,12 @@
 //! {
 //!     let result = match IntoIterator::into_iter(values) {
 //!         mut iter => loop {
-//!             let x = match iter.next() {
-//!                 Some(val) => val,
+//!             let next;
+//!             match iter.next() {
+//!                 Some(val) => next = val,
 //!                 None => break,
 //!             };
+//!             let x = next;
 //!             let () = { println!("{}", x); };
 //!         },
 //!     };
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 3d77381e2e9..62bedcdfcbe 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -2170,11 +2170,13 @@ impl<'a> LoweringContext<'a> {
                 //     let result = match ::std::iter::IntoIterator::into_iter(<head>) {
                 //       mut iter => {
                 //         [opt_ident]: loop {
-                //           let <pat> = match ::std::iter::Iterator::next(&mut iter) {
-                //             ::std::option::Option::Some(val) => val,
+                //           let mut _next;
+                //           match ::std::iter::Iterator::next(&mut iter) {
+                //             ::std::option::Option::Some(val) => _next = val,
                 //             ::std::option::Option::None => break
                 //           };
-                //           SemiExpr(<body>);
+                //           let <pat> = _next;
+                //           StmtExpr(<body>);
                 //         }
                 //       }
                 //     };
@@ -2186,13 +2188,22 @@ impl<'a> LoweringContext<'a> {
 
                 let iter = self.str_to_ident("iter");
 
-                // `::std::option::Option::Some(val) => val`
+                let next_ident = self.str_to_ident("_next");
+                let next_pat = self.pat_ident_binding_mode(e.span,
+                                                           next_ident,
+                                                           hir::BindByValue(hir::MutMutable));
+
+                // `::std::option::Option::Some(val) => next = val`
                 let pat_arm = {
                     let val_ident = self.str_to_ident("val");
                     let val_pat = self.pat_ident(e.span, val_ident);
                     let val_expr = P(self.expr_ident(e.span, val_ident, val_pat.id));
+                    let next_expr = P(self.expr_ident(e.span, next_ident, next_pat.id));
+                    let assign = P(self.expr(e.span,
+                                             hir::ExprAssign(next_expr, val_expr),
+                                             ThinVec::new()));
                     let some_pat = self.pat_some(e.span, val_pat);
-                    self.arm(hir_vec![some_pat], val_expr)
+                    self.arm(hir_vec![some_pat], assign)
                 };
 
                 // `::std::option::Option::None => break`
@@ -2222,10 +2233,20 @@ impl<'a> LoweringContext<'a> {
                                                hir::MatchSource::ForLoopDesugar),
                                 ThinVec::new()))
                 };
+                let match_stmt = respan(e.span, hir::StmtExpr(match_expr, self.next_id()));
+
+                let next_expr = P(self.expr_ident(e.span, next_ident, next_pat.id));
+
+                // `let mut _next`
+                let next_let = self.stmt_let_pat(e.span,
+                    None,
+                    next_pat,
+                    hir::LocalSource::ForLoopDesugar);
 
+                // `let <pat> = _next`
                 let pat = self.lower_pat(pat);
                 let pat_let = self.stmt_let_pat(e.span,
-                    match_expr,
+                    Some(next_expr),
                     pat,
                     hir::LocalSource::ForLoopDesugar);
 
@@ -2234,7 +2255,12 @@ impl<'a> LoweringContext<'a> {
                 let body_expr = P(self.expr_block(body_block, ThinVec::new()));
                 let body_stmt = respan(e.span, hir::StmtExpr(body_expr, self.next_id()));
 
-                let loop_block = P(self.block_all(e.span, hir_vec![pat_let, body_stmt], None));
+                let loop_block = P(self.block_all(e.span,
+                                                  hir_vec![next_let,
+                                                           match_stmt,
+                                                           pat_let,
+                                                           body_stmt],
+                                                  None));
 
                 // `[opt_ident]: loop { ... }`
                 let loop_expr = hir::ExprLoop(loop_block, self.lower_opt_sp_ident(opt_ident),
@@ -2601,14 +2627,14 @@ impl<'a> LoweringContext<'a> {
 
     fn stmt_let_pat(&mut self,
                     sp: Span,
-                    ex: P<hir::Expr>,
+                    ex: Option<P<hir::Expr>>,
                     pat: P<hir::Pat>,
                     source: hir::LocalSource)
                     -> hir::Stmt {
         let local = P(hir::Local {
             pat: pat,
             ty: None,
-            init: Some(ex),
+            init: ex,
             id: self.next_id(),
             span: sp,
             attrs: ThinVec::new(),
@@ -2626,7 +2652,7 @@ impl<'a> LoweringContext<'a> {
             self.pat_ident(sp, ident)
         };
         let pat_id = pat.id;
-        (self.stmt_let_pat(sp, ex, pat, hir::LocalSource::Normal), pat_id)
+        (self.stmt_let_pat(sp, Some(ex), pat, hir::LocalSource::Normal), pat_id)
     }
 
     fn block_expr(&mut self, expr: P<hir::Expr>) -> hir::Block {
diff --git a/src/test/compile-fail/for-loop-unconstrained-element-type.rs b/src/test/compile-fail/for-loop-unconstrained-element-type.rs
new file mode 100644
index 00000000000..fb5553166ba
--- /dev/null
+++ b/src/test/compile-fail/for-loop-unconstrained-element-type.rs
@@ -0,0 +1,19 @@
+// Copyright 2017 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.
+
+// Test that `for` loops don't introduce artificial
+// constraints on the type of the binding (`i`).
+// Subtle changes in the desugaring can cause the
+// type of elements in the vector to (incorrectly)
+// fallback to `!` or `()`.
+
+fn main() {
+    for i in Vec::new() { } //~ ERROR type annotations needed
+}
diff --git a/src/test/run-pass/for-loop-lifetime-of-unbound-values.rs b/src/test/run-pass/for-loop-lifetime-of-unbound-values.rs
new file mode 100644
index 00000000000..7a088b51334
--- /dev/null
+++ b/src/test/run-pass/for-loop-lifetime-of-unbound-values.rs
@@ -0,0 +1,43 @@
+// Copyright 2017 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.
+
+// Test when destructors run in a for loop. The intention is
+// that the value for each iteration is dropped *after* the loop
+// body has executed. This is true even when the value is assigned
+// to a `_` pattern (and hence ignored).
+
+use std::cell::Cell;
+
+struct Flag<'a>(&'a Cell<bool>);
+
+impl<'a> Drop for Flag<'a> {
+    fn drop(&mut self) {
+        self.0.set(false)
+    }
+}
+
+fn main() {
+    let alive2 = Cell::new(true);
+    for _i in std::iter::once(Flag(&alive2)) {
+        // The Flag value should be alive in the for loop body
+        assert_eq!(alive2.get(), true);
+    }
+    // The Flag value should be dead outside of the loop
+    assert_eq!(alive2.get(), false);
+
+    let alive = Cell::new(true);
+    for _ in std::iter::once(Flag(&alive)) {
+        // The Flag value should be alive in the for loop body even if it wasn't
+        // bound by the for loop
+        assert_eq!(alive.get(), true);
+    }
+    // The Flag value should be dead outside of the loop
+    assert_eq!(alive.get(), false);
+}
diff --git a/src/test/run-pass/for-loop-mut-ref-element.rs b/src/test/run-pass/for-loop-mut-ref-element.rs
new file mode 100644
index 00000000000..14ce23b0724
--- /dev/null
+++ b/src/test/run-pass/for-loop-mut-ref-element.rs
@@ -0,0 +1,15 @@
+// Copyright 2014 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.
+
+// Tests that for loops can bind elements as mutable references
+
+fn main() {
+    for ref mut _a in std::iter::once(true) {}
+}
\ No newline at end of file
diff --git a/src/test/run-pass/for-loop-unconstrained-element-type-i32-fallback.rs b/src/test/run-pass/for-loop-unconstrained-element-type-i32-fallback.rs
new file mode 100644
index 00000000000..b36afcf87b3
--- /dev/null
+++ b/src/test/run-pass/for-loop-unconstrained-element-type-i32-fallback.rs
@@ -0,0 +1,20 @@
+// Copyright 2017 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.
+
+// Test that the type of `sum` falls back to `i32` here,
+// and that the for loop desugaring doesn't inferfere with
+// that.
+
+fn main() {
+    let mut sum = 0;
+    for i in Vec::new() {
+        sum += i;
+    }
+}