Change for-loop desugar to not borrow the iterator during the loop

This commit is contained in:
John Kåre Alsaker
2017-05-27 20:20:17 +02:00
parent a11c26f6ac
commit cfdbff7c12
9 changed files with 138 additions and 83 deletions

View File

@@ -191,10 +191,11 @@
//! { //! {
//! let result = match IntoIterator::into_iter(values) { //! let result = match IntoIterator::into_iter(values) {
//! mut iter => loop { //! mut iter => loop {
//! match iter.next() { //! let x = match iter.next() {
//! Some(x) => { println!("{}", x); }, //! Some(val) => val,
//! None => break, //! None => break,
//! } //! };
//! let () = { println!("{}", x); };
//! }, //! },
//! }; //! };
//! result //! result

View File

@@ -891,6 +891,7 @@ impl<'a> LoweringContext<'a> {
init: l.init.as_ref().map(|e| P(self.lower_expr(e))), init: l.init.as_ref().map(|e| P(self.lower_expr(e))),
span: l.span, span: l.span,
attrs: l.attrs.clone(), attrs: l.attrs.clone(),
source: hir::LocalSource::Normal,
}) })
} }
@@ -2167,10 +2168,11 @@ impl<'a> LoweringContext<'a> {
// let result = match ::std::iter::IntoIterator::into_iter(<head>) { // let result = match ::std::iter::IntoIterator::into_iter(<head>) {
// mut iter => { // mut iter => {
// [opt_ident]: loop { // [opt_ident]: loop {
// match ::std::iter::Iterator::next(&mut iter) { // let <pat> = match ::std::iter::Iterator::next(&mut iter) {
// ::std::option::Option::Some(<pat>) => <body>, // ::std::option::Option::Some(val) => val,
// ::std::option::Option::None => break // ::std::option::Option::None => break
// } // };
// SemiExpr(<body>);
// } // }
// } // }
// }; // };
@@ -2182,15 +2184,13 @@ impl<'a> LoweringContext<'a> {
let iter = self.str_to_ident("iter"); let iter = self.str_to_ident("iter");
// `::std::option::Option::Some(<pat>) => <body>` // `::std::option::Option::Some(val) => val`
let pat_arm = { let pat_arm = {
let body_block = self.with_loop_scope(e.id, let val_ident = self.str_to_ident("val");
|this| this.lower_block(body, false)); let val_pat = self.pat_ident(e.span, val_ident);
let body_expr = P(self.expr_block(body_block, ThinVec::new())); let val_expr = P(self.expr_ident(e.span, val_ident, val_pat.id));
let pat = self.lower_pat(pat); let some_pat = self.pat_some(e.span, val_pat);
let some_pat = self.pat_some(e.span, pat); self.arm(hir_vec![some_pat], val_expr)
self.arm(hir_vec![some_pat], body_expr)
}; };
// `::std::option::Option::None => break` // `::std::option::Option::None => break`
@@ -2221,8 +2221,20 @@ impl<'a> LoweringContext<'a> {
ThinVec::new())) ThinVec::new()))
}; };
let pat = self.lower_pat(pat);
let pat_let = self.stmt_let_pat(e.span,
match_expr,
pat,
hir::LocalSource::ForLoopDesugar);
let body_block = self.with_loop_scope(e.id,
|this| this.lower_block(body, false));
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));
// `[opt_ident]: loop { ... }` // `[opt_ident]: loop { ... }`
let loop_block = P(self.block_expr(match_expr));
let loop_expr = hir::ExprLoop(loop_block, self.lower_opt_sp_ident(opt_ident), let loop_expr = hir::ExprLoop(loop_block, self.lower_opt_sp_ident(opt_ident),
hir::LoopSource::ForLoop); hir::LoopSource::ForLoop);
let loop_expr = P(hir::Expr { let loop_expr = P(hir::Expr {
@@ -2585,6 +2597,25 @@ impl<'a> LoweringContext<'a> {
} }
} }
fn stmt_let_pat(&mut self,
sp: Span,
ex: P<hir::Expr>,
pat: P<hir::Pat>,
source: hir::LocalSource)
-> hir::Stmt {
let local = P(hir::Local {
pat: pat,
ty: None,
init: Some(ex),
id: self.next_id(),
span: sp,
attrs: ThinVec::new(),
source,
});
let decl = respan(sp, hir::DeclLocal(local));
respan(sp, hir::StmtDecl(P(decl), self.next_id()))
}
fn stmt_let(&mut self, sp: Span, mutbl: bool, ident: Name, ex: P<hir::Expr>) fn stmt_let(&mut self, sp: Span, mutbl: bool, ident: Name, ex: P<hir::Expr>)
-> (hir::Stmt, NodeId) { -> (hir::Stmt, NodeId) {
let pat = if mutbl { let pat = if mutbl {
@@ -2593,16 +2624,7 @@ impl<'a> LoweringContext<'a> {
self.pat_ident(sp, ident) self.pat_ident(sp, ident)
}; };
let pat_id = pat.id; let pat_id = pat.id;
let local = P(hir::Local { (self.stmt_let_pat(sp, ex, pat, hir::LocalSource::Normal), pat_id)
pat: pat,
ty: None,
init: Some(ex),
id: self.next_id(),
span: sp,
attrs: ThinVec::new(),
});
let decl = respan(sp, hir::DeclLocal(local));
(respan(sp, hir::StmtDecl(P(decl), self.next_id())), pat_id)
} }
fn block_expr(&mut self, expr: P<hir::Expr>) -> hir::Block { fn block_expr(&mut self, expr: P<hir::Expr>) -> hir::Block {

View File

@@ -872,6 +872,7 @@ pub struct Local {
pub id: NodeId, pub id: NodeId,
pub span: Span, pub span: Span,
pub attrs: ThinVec<Attribute>, pub attrs: ThinVec<Attribute>,
pub source: LocalSource,
} }
pub type Decl = Spanned<Decl_>; pub type Decl = Spanned<Decl_>;
@@ -1080,6 +1081,15 @@ pub enum QPath {
TypeRelative(P<Ty>, P<PathSegment>) TypeRelative(P<Ty>, P<PathSegment>)
} }
/// Hints at the original code for a let statement
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
pub enum LocalSource {
/// A `match _ { .. }`
Normal,
/// A desugared `for _ in _ { .. }` loop
ForLoopDesugar,
}
/// Hints at the original code for a `match _ { .. }` /// Hints at the original code for a `match _ { .. }`
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
pub enum MatchSource { pub enum MatchSource {

View File

@@ -490,7 +490,8 @@ impl_stable_hash_for!(struct hir::Local {
init, init,
id, id,
span, span,
attrs attrs,
source
}); });
impl_stable_hash_for_spanned!(hir::Decl_); impl_stable_hash_for_spanned!(hir::Decl_);
@@ -640,6 +641,11 @@ impl_stable_hash_for!(enum hir::Expr_ {
ExprRepeat(val, times) ExprRepeat(val, times)
}); });
impl_stable_hash_for!(enum hir::LocalSource {
Normal,
ForLoopDesugar
});
impl_stable_hash_for!(enum hir::LoopSource { impl_stable_hash_for!(enum hir::LoopSource {
Loop, Loop,
WhileLet, WhileLet,

View File

@@ -92,7 +92,10 @@ impl<'a, 'tcx> Visitor<'tcx> for MatchVisitor<'a, 'tcx> {
fn visit_local(&mut self, loc: &'tcx hir::Local) { fn visit_local(&mut self, loc: &'tcx hir::Local) {
intravisit::walk_local(self, loc); intravisit::walk_local(self, loc);
self.check_irrefutable(&loc.pat, false); self.check_irrefutable(&loc.pat, match loc.source {
hir::LocalSource::Normal => "local binding",
hir::LocalSource::ForLoopDesugar => "`for` loop binding",
});
// Check legality of move bindings and `@` patterns. // Check legality of move bindings and `@` patterns.
self.check_patterns(false, slice::ref_slice(&loc.pat)); self.check_patterns(false, slice::ref_slice(&loc.pat));
@@ -102,7 +105,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MatchVisitor<'a, 'tcx> {
intravisit::walk_body(self, body); intravisit::walk_body(self, body);
for arg in &body.arguments { for arg in &body.arguments {
self.check_irrefutable(&arg.pat, true); self.check_irrefutable(&arg.pat, "function argument");
self.check_patterns(false, slice::ref_slice(&arg.pat)); self.check_patterns(false, slice::ref_slice(&arg.pat));
} }
} }
@@ -211,7 +214,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
.map(|pat| vec![pat.0]) .map(|pat| vec![pat.0])
.collect(); .collect();
let scrut_ty = self.tables.node_id_to_type(scrut.id); let scrut_ty = self.tables.node_id_to_type(scrut.id);
check_exhaustive(cx, scrut_ty, scrut.span, &matrix, source); check_exhaustive(cx, scrut_ty, scrut.span, &matrix);
}) })
} }
@@ -224,13 +227,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
} }
} }
fn check_irrefutable(&self, pat: &Pat, is_fn_arg: bool) { fn check_irrefutable(&self, pat: &Pat, origin: &str) {
let origin = if is_fn_arg {
"function argument"
} else {
"local binding"
};
let module = self.tcx.hir.get_module_parent(pat.id); let module = self.tcx.hir.get_module_parent(pat.id);
MatchCheckCtxt::create_and_enter(self.tcx, module, |ref mut cx| { MatchCheckCtxt::create_and_enter(self.tcx, module, |ref mut cx| {
let mut patcx = PatternContext::new(self.tcx, self.tables); let mut patcx = PatternContext::new(self.tcx, self.tables);
@@ -396,8 +393,7 @@ fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
fn check_exhaustive<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>, fn check_exhaustive<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
scrut_ty: Ty<'tcx>, scrut_ty: Ty<'tcx>,
sp: Span, sp: Span,
matrix: &Matrix<'a, 'tcx>, matrix: &Matrix<'a, 'tcx>) {
source: hir::MatchSource) {
let wild_pattern = Pattern { let wild_pattern = Pattern {
ty: scrut_ty, ty: scrut_ty,
span: DUMMY_SP, span: DUMMY_SP,
@@ -410,52 +406,32 @@ fn check_exhaustive<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
} else { } else {
pats.iter().map(|w| w.single_pattern()).collect() pats.iter().map(|w| w.single_pattern()).collect()
}; };
match source {
hir::MatchSource::ForLoopDesugar => { const LIMIT: usize = 3;
// `witnesses[0]` has the form `Some(<head>)`, peel off the `Some` let joined_patterns = match witnesses.len() {
let witness = match *witnesses[0].kind { 0 => bug!(),
PatternKind::Variant { ref subpatterns, .. } => match &subpatterns[..] { 1 => format!("`{}`", witnesses[0]),
&[ref pat] => &pat.pattern, 2...LIMIT => {
_ => bug!(), let (tail, head) = witnesses.split_last().unwrap();
}, let head: Vec<_> = head.iter().map(|w| w.to_string()).collect();
_ => bug!(), format!("`{}` and `{}`", head.join("`, `"), tail)
};
let pattern_string = witness.to_string();
struct_span_err!(cx.tcx.sess, sp, E0297,
"refutable pattern in `for` loop binding: \
`{}` not covered",
pattern_string)
.span_label(sp, format!("pattern `{}` not covered", pattern_string))
.emit();
}, },
_ => { _ => {
const LIMIT: usize = 3; let (head, tail) = witnesses.split_at(LIMIT);
let joined_patterns = match witnesses.len() { let head: Vec<_> = head.iter().map(|w| w.to_string()).collect();
0 => bug!(), format!("`{}` and {} more", head.join("`, `"), tail.len())
1 => format!("`{}`", witnesses[0]), }
2...LIMIT => { };
let (tail, head) = witnesses.split_last().unwrap();
let head: Vec<_> = head.iter().map(|w| w.to_string()).collect();
format!("`{}` and `{}`", head.join("`, `"), tail)
},
_ => {
let (head, tail) = witnesses.split_at(LIMIT);
let head: Vec<_> = head.iter().map(|w| w.to_string()).collect();
format!("`{}` and {} more", head.join("`, `"), tail.len())
}
};
let label_text = match witnesses.len() { let label_text = match witnesses.len() {
1 => format!("pattern {} not covered", joined_patterns), 1 => format!("pattern {} not covered", joined_patterns),
_ => format!("patterns {} not covered", joined_patterns) _ => format!("patterns {} not covered", joined_patterns)
}; };
create_e0004(cx.tcx.sess, sp, create_e0004(cx.tcx.sess, sp,
format!("non-exhaustive patterns: {} not covered", format!("non-exhaustive patterns: {} not covered",
joined_patterns)) joined_patterns))
.span_label(sp, label_text) .span_label(sp, label_text)
.emit(); .emit();
},
}
} }
NotUseful => { NotUseful => {
// This is good, wildcard pattern isn't reachable // This is good, wildcard pattern isn't reachable

View File

@@ -452,12 +452,14 @@ enum Method { GET, POST }
E0297: r##" E0297: r##"
#### Note: this error code is no longer emitted by the compiler.
Patterns used to bind names must be irrefutable. That is, they must guarantee Patterns used to bind names must be irrefutable. That is, they must guarantee
that a name will be extracted in all cases. Instead of pattern matching the that a name will be extracted in all cases. Instead of pattern matching the
loop variable, consider using a `match` or `if let` inside the loop body. For loop variable, consider using a `match` or `if let` inside the loop body. For
instance: instance:
```compile_fail,E0297 ```compile_fail,E0005
let xs : Vec<Option<i32>> = vec![Some(1), None]; let xs : Vec<Option<i32>> = vec![Some(1), None];
// This fails because `None` is not covered. // This fails because `None` is not covered.

View File

@@ -12,6 +12,6 @@ fn main() {
let xs : Vec<Option<i32>> = vec![Some(1), None]; let xs : Vec<Option<i32>> = vec![Some(1), None];
for Some(x) in xs {} for Some(x) in xs {}
//~^ ERROR E0297 //~^ ERROR E0005
//~| NOTE pattern `None` not covered //~| NOTE pattern `None` not covered
} }

View File

@@ -0,0 +1,17 @@
// 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.
fn main() {
for x in 0..3 {
x //~ ERROR mismatched types
//~| NOTE expected ()
//~| NOTE expected type `()`
}
}

View File

@@ -0,0 +1,21 @@
// 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.
fn main() {
// Check that the tail statement in the body unifies with something
for _ in 0..3 {
unsafe { std::mem::uninitialized() }
}
// Check that the tail statement in the body can be unit
for _ in 0..3 {
()
}
}