Skip to content

Commit ab5bec2

Browse files
committed
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)
2 parents 74fa279 + 1409e70 commit ab5bec2

6 files changed

+137
-12
lines changed

Diff for: src/libcore/iter/mod.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -191,10 +191,12 @@
191191
//! {
192192
//! let result = match IntoIterator::into_iter(values) {
193193
//! mut iter => loop {
194-
//! let x = match iter.next() {
195-
//! Some(val) => val,
194+
//! let next;
195+
//! match iter.next() {
196+
//! Some(val) => next = val,
196197
//! None => break,
197198
//! };
199+
//! let x = next;
198200
//! let () = { println!("{}", x); };
199201
//! },
200202
//! };

Diff for: src/librustc/hir/lowering.rs

+36-10
Original file line numberDiff line numberDiff line change
@@ -2170,11 +2170,13 @@ impl<'a> LoweringContext<'a> {
21702170
// let result = match ::std::iter::IntoIterator::into_iter(<head>) {
21712171
// mut iter => {
21722172
// [opt_ident]: loop {
2173-
// let <pat> = match ::std::iter::Iterator::next(&mut iter) {
2174-
// ::std::option::Option::Some(val) => val,
2173+
// let mut _next;
2174+
// match ::std::iter::Iterator::next(&mut iter) {
2175+
// ::std::option::Option::Some(val) => _next = val,
21752176
// ::std::option::Option::None => break
21762177
// };
2177-
// SemiExpr(<body>);
2178+
// let <pat> = _next;
2179+
// StmtExpr(<body>);
21782180
// }
21792181
// }
21802182
// };
@@ -2186,13 +2188,22 @@ impl<'a> LoweringContext<'a> {
21862188

21872189
let iter = self.str_to_ident("iter");
21882190

2189-
// `::std::option::Option::Some(val) => val`
2191+
let next_ident = self.str_to_ident("_next");
2192+
let next_pat = self.pat_ident_binding_mode(e.span,
2193+
next_ident,
2194+
hir::BindByValue(hir::MutMutable));
2195+
2196+
// `::std::option::Option::Some(val) => next = val`
21902197
let pat_arm = {
21912198
let val_ident = self.str_to_ident("val");
21922199
let val_pat = self.pat_ident(e.span, val_ident);
21932200
let val_expr = P(self.expr_ident(e.span, val_ident, val_pat.id));
2201+
let next_expr = P(self.expr_ident(e.span, next_ident, next_pat.id));
2202+
let assign = P(self.expr(e.span,
2203+
hir::ExprAssign(next_expr, val_expr),
2204+
ThinVec::new()));
21942205
let some_pat = self.pat_some(e.span, val_pat);
2195-
self.arm(hir_vec![some_pat], val_expr)
2206+
self.arm(hir_vec![some_pat], assign)
21962207
};
21972208

21982209
// `::std::option::Option::None => break`
@@ -2222,10 +2233,20 @@ impl<'a> LoweringContext<'a> {
22222233
hir::MatchSource::ForLoopDesugar),
22232234
ThinVec::new()))
22242235
};
2236+
let match_stmt = respan(e.span, hir::StmtExpr(match_expr, self.next_id()));
2237+
2238+
let next_expr = P(self.expr_ident(e.span, next_ident, next_pat.id));
2239+
2240+
// `let mut _next`
2241+
let next_let = self.stmt_let_pat(e.span,
2242+
None,
2243+
next_pat,
2244+
hir::LocalSource::ForLoopDesugar);
22252245

2246+
// `let <pat> = _next`
22262247
let pat = self.lower_pat(pat);
22272248
let pat_let = self.stmt_let_pat(e.span,
2228-
match_expr,
2249+
Some(next_expr),
22292250
pat,
22302251
hir::LocalSource::ForLoopDesugar);
22312252

@@ -2234,7 +2255,12 @@ impl<'a> LoweringContext<'a> {
22342255
let body_expr = P(self.expr_block(body_block, ThinVec::new()));
22352256
let body_stmt = respan(e.span, hir::StmtExpr(body_expr, self.next_id()));
22362257

2237-
let loop_block = P(self.block_all(e.span, hir_vec![pat_let, body_stmt], None));
2258+
let loop_block = P(self.block_all(e.span,
2259+
hir_vec![next_let,
2260+
match_stmt,
2261+
pat_let,
2262+
body_stmt],
2263+
None));
22382264

22392265
// `[opt_ident]: loop { ... }`
22402266
let loop_expr = hir::ExprLoop(loop_block, self.lower_opt_sp_ident(opt_ident),
@@ -2601,14 +2627,14 @@ impl<'a> LoweringContext<'a> {
26012627

26022628
fn stmt_let_pat(&mut self,
26032629
sp: Span,
2604-
ex: P<hir::Expr>,
2630+
ex: Option<P<hir::Expr>>,
26052631
pat: P<hir::Pat>,
26062632
source: hir::LocalSource)
26072633
-> hir::Stmt {
26082634
let local = P(hir::Local {
26092635
pat: pat,
26102636
ty: None,
2611-
init: Some(ex),
2637+
init: ex,
26122638
id: self.next_id(),
26132639
span: sp,
26142640
attrs: ThinVec::new(),
@@ -2626,7 +2652,7 @@ impl<'a> LoweringContext<'a> {
26262652
self.pat_ident(sp, ident)
26272653
};
26282654
let pat_id = pat.id;
2629-
(self.stmt_let_pat(sp, ex, pat, hir::LocalSource::Normal), pat_id)
2655+
(self.stmt_let_pat(sp, Some(ex), pat, hir::LocalSource::Normal), pat_id)
26302656
}
26312657

26322658
fn block_expr(&mut self, expr: P<hir::Expr>) -> hir::Block {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that `for` loops don't introduce artificial
12+
// constraints on the type of the binding (`i`).
13+
// Subtle changes in the desugaring can cause the
14+
// type of elements in the vector to (incorrectly)
15+
// fallback to `!` or `()`.
16+
17+
fn main() {
18+
for i in Vec::new() { } //~ ERROR type annotations needed
19+
}
+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test when destructors run in a for loop. The intention is
12+
// that the value for each iteration is dropped *after* the loop
13+
// body has executed. This is true even when the value is assigned
14+
// to a `_` pattern (and hence ignored).
15+
16+
use std::cell::Cell;
17+
18+
struct Flag<'a>(&'a Cell<bool>);
19+
20+
impl<'a> Drop for Flag<'a> {
21+
fn drop(&mut self) {
22+
self.0.set(false)
23+
}
24+
}
25+
26+
fn main() {
27+
let alive2 = Cell::new(true);
28+
for _i in std::iter::once(Flag(&alive2)) {
29+
// The Flag value should be alive in the for loop body
30+
assert_eq!(alive2.get(), true);
31+
}
32+
// The Flag value should be dead outside of the loop
33+
assert_eq!(alive2.get(), false);
34+
35+
let alive = Cell::new(true);
36+
for _ in std::iter::once(Flag(&alive)) {
37+
// The Flag value should be alive in the for loop body even if it wasn't
38+
// bound by the for loop
39+
assert_eq!(alive.get(), true);
40+
}
41+
// The Flag value should be dead outside of the loop
42+
assert_eq!(alive.get(), false);
43+
}

Diff for: src/test/run-pass/for-loop-mut-ref-element.rs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Tests that for loops can bind elements as mutable references
12+
13+
fn main() {
14+
for ref mut _a in std::iter::once(true) {}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that the type of `sum` falls back to `i32` here,
12+
// and that the for loop desugaring doesn't inferfere with
13+
// that.
14+
15+
fn main() {
16+
let mut sum = 0;
17+
for i in Vec::new() {
18+
sum += i;
19+
}
20+
}

0 commit comments

Comments
 (0)