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)
This commit is contained in:
commit
ab5bec2553
@ -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); };
|
||||
//! },
|
||||
//! };
|
||||
|
@ -2170,11 +2170,13 @@ fn lower_expr(&mut self, e: &Expr) -> hir::Expr {
|
||||
// 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 @@ fn lower_expr(&mut self, e: &Expr) -> hir::Expr {
|
||||
|
||||
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 @@ fn lower_expr(&mut self, e: &Expr) -> hir::Expr {
|
||||
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 @@ fn lower_expr(&mut self, e: &Expr) -> hir::Expr {
|
||||
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 @@ fn expr(&mut self, span: Span, node: hir::Expr_, attrs: ThinVec<Attribute>) -> h
|
||||
|
||||
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 @@ fn stmt_let(&mut self, sp: Span, mutbl: bool, ident: Name, ex: P<hir::Expr>)
|
||||
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 {
|
||||
|
19
src/test/compile-fail/for-loop-unconstrained-element-type.rs
Normal file
19
src/test/compile-fail/for-loop-unconstrained-element-type.rs
Normal file
@ -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
|
||||
}
|
43
src/test/run-pass/for-loop-lifetime-of-unbound-values.rs
Normal file
43
src/test/run-pass/for-loop-lifetime-of-unbound-values.rs
Normal file
@ -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);
|
||||
}
|
15
src/test/run-pass/for-loop-mut-ref-element.rs
Normal file
15
src/test/run-pass/for-loop-mut-ref-element.rs
Normal file
@ -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) {}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user