diff options
| author | bors <bors@rust-lang.org> | 2017-06-22 15:24:58 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2017-06-22 15:24:58 +0000 |
| commit | ab5bec25530aac43dfd64384b405c909b6e405e3 (patch) | |
| tree | cd10ff6cf849e2c3b92dee464495dbfc83c96503 | |
| parent | 74fa27928aceda1362a2266d9b9bf129999bc00a (diff) | |
| parent | 1409e707a2e6b152ab66a9d836e5e367f6f066cc (diff) | |
| download | rust-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.rs | 6 | ||||
| -rw-r--r-- | src/librustc/hir/lowering.rs | 46 | ||||
| -rw-r--r-- | src/test/compile-fail/for-loop-unconstrained-element-type.rs | 19 | ||||
| -rw-r--r-- | src/test/run-pass/for-loop-lifetime-of-unbound-values.rs | 43 | ||||
| -rw-r--r-- | src/test/run-pass/for-loop-mut-ref-element.rs | 15 | ||||
| -rw-r--r-- | src/test/run-pass/for-loop-unconstrained-element-type-i32-fallback.rs | 20 |
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; + } +} |
