Skip to content

Commit 0b17b4c

Browse files
committed
Auto merge of #42265 - Zoxc:for-sugar, r=eddyb
Change for-loop desugar to not borrow the iterator during the loop This is enables the use of suspend points inside for-loops in movable generators. This is illegal in the current desugaring as `iter` is borrowed across the body.
2 parents 1b5a923 + cfdbff7 commit 0b17b4c

File tree

9 files changed

+138
-83
lines changed

9 files changed

+138
-83
lines changed

src/libcore/iter/mod.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -191,10 +191,11 @@
191191
//! {
192192
//! let result = match IntoIterator::into_iter(values) {
193193
//! mut iter => loop {
194-
//! match iter.next() {
195-
//! Some(x) => { println!("{}", x); },
194+
//! let x = match iter.next() {
195+
//! Some(val) => val,
196196
//! None => break,
197-
//! }
197+
//! };
198+
//! let () = { println!("{}", x); };
198199
//! },
199200
//! };
200201
//! result

src/librustc/hir/lowering.rs

+43-21
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,7 @@ impl<'a> LoweringContext<'a> {
891891
init: l.init.as_ref().map(|e| P(self.lower_expr(e))),
892892
span: l.span,
893893
attrs: l.attrs.clone(),
894+
source: hir::LocalSource::Normal,
894895
})
895896
}
896897

@@ -2167,10 +2168,11 @@ impl<'a> LoweringContext<'a> {
21672168
// let result = match ::std::iter::IntoIterator::into_iter(<head>) {
21682169
// mut iter => {
21692170
// [opt_ident]: loop {
2170-
// match ::std::iter::Iterator::next(&mut iter) {
2171-
// ::std::option::Option::Some(<pat>) => <body>,
2171+
// let <pat> = match ::std::iter::Iterator::next(&mut iter) {
2172+
// ::std::option::Option::Some(val) => val,
21722173
// ::std::option::Option::None => break
2173-
// }
2174+
// };
2175+
// SemiExpr(<body>);
21742176
// }
21752177
// }
21762178
// };
@@ -2182,15 +2184,13 @@ impl<'a> LoweringContext<'a> {
21822184

21832185
let iter = self.str_to_ident("iter");
21842186

2185-
// `::std::option::Option::Some(<pat>) => <body>`
2187+
// `::std::option::Option::Some(val) => val`
21862188
let pat_arm = {
2187-
let body_block = self.with_loop_scope(e.id,
2188-
|this| this.lower_block(body, false));
2189-
let body_expr = P(self.expr_block(body_block, ThinVec::new()));
2190-
let pat = self.lower_pat(pat);
2191-
let some_pat = self.pat_some(e.span, pat);
2192-
2193-
self.arm(hir_vec![some_pat], body_expr)
2189+
let val_ident = self.str_to_ident("val");
2190+
let val_pat = self.pat_ident(e.span, val_ident);
2191+
let val_expr = P(self.expr_ident(e.span, val_ident, val_pat.id));
2192+
let some_pat = self.pat_some(e.span, val_pat);
2193+
self.arm(hir_vec![some_pat], val_expr)
21942194
};
21952195

21962196
// `::std::option::Option::None => break`
@@ -2221,8 +2221,20 @@ impl<'a> LoweringContext<'a> {
22212221
ThinVec::new()))
22222222
};
22232223

2224+
let pat = self.lower_pat(pat);
2225+
let pat_let = self.stmt_let_pat(e.span,
2226+
match_expr,
2227+
pat,
2228+
hir::LocalSource::ForLoopDesugar);
2229+
2230+
let body_block = self.with_loop_scope(e.id,
2231+
|this| this.lower_block(body, false));
2232+
let body_expr = P(self.expr_block(body_block, ThinVec::new()));
2233+
let body_stmt = respan(e.span, hir::StmtExpr(body_expr, self.next_id()));
2234+
2235+
let loop_block = P(self.block_all(e.span, hir_vec![pat_let, body_stmt], None));
2236+
22242237
// `[opt_ident]: loop { ... }`
2225-
let loop_block = P(self.block_expr(match_expr));
22262238
let loop_expr = hir::ExprLoop(loop_block, self.lower_opt_sp_ident(opt_ident),
22272239
hir::LoopSource::ForLoop);
22282240
let loop_expr = P(hir::Expr {
@@ -2585,24 +2597,34 @@ impl<'a> LoweringContext<'a> {
25852597
}
25862598
}
25872599

2588-
fn stmt_let(&mut self, sp: Span, mutbl: bool, ident: Name, ex: P<hir::Expr>)
2589-
-> (hir::Stmt, NodeId) {
2590-
let pat = if mutbl {
2591-
self.pat_ident_binding_mode(sp, ident, hir::BindByValue(hir::MutMutable))
2592-
} else {
2593-
self.pat_ident(sp, ident)
2594-
};
2595-
let pat_id = pat.id;
2600+
fn stmt_let_pat(&mut self,
2601+
sp: Span,
2602+
ex: P<hir::Expr>,
2603+
pat: P<hir::Pat>,
2604+
source: hir::LocalSource)
2605+
-> hir::Stmt {
25962606
let local = P(hir::Local {
25972607
pat: pat,
25982608
ty: None,
25992609
init: Some(ex),
26002610
id: self.next_id(),
26012611
span: sp,
26022612
attrs: ThinVec::new(),
2613+
source,
26032614
});
26042615
let decl = respan(sp, hir::DeclLocal(local));
2605-
(respan(sp, hir::StmtDecl(P(decl), self.next_id())), pat_id)
2616+
respan(sp, hir::StmtDecl(P(decl), self.next_id()))
2617+
}
2618+
2619+
fn stmt_let(&mut self, sp: Span, mutbl: bool, ident: Name, ex: P<hir::Expr>)
2620+
-> (hir::Stmt, NodeId) {
2621+
let pat = if mutbl {
2622+
self.pat_ident_binding_mode(sp, ident, hir::BindByValue(hir::MutMutable))
2623+
} else {
2624+
self.pat_ident(sp, ident)
2625+
};
2626+
let pat_id = pat.id;
2627+
(self.stmt_let_pat(sp, ex, pat, hir::LocalSource::Normal), pat_id)
26062628
}
26072629

26082630
fn block_expr(&mut self, expr: P<hir::Expr>) -> hir::Block {

src/librustc/hir/mod.rs

+10
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,7 @@ pub struct Local {
872872
pub id: NodeId,
873873
pub span: Span,
874874
pub attrs: ThinVec<Attribute>,
875+
pub source: LocalSource,
875876
}
876877

877878
pub type Decl = Spanned<Decl_>;
@@ -1080,6 +1081,15 @@ pub enum QPath {
10801081
TypeRelative(P<Ty>, P<PathSegment>)
10811082
}
10821083

1084+
/// Hints at the original code for a let statement
1085+
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
1086+
pub enum LocalSource {
1087+
/// A `match _ { .. }`
1088+
Normal,
1089+
/// A desugared `for _ in _ { .. }` loop
1090+
ForLoopDesugar,
1091+
}
1092+
10831093
/// Hints at the original code for a `match _ { .. }`
10841094
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
10851095
pub enum MatchSource {

src/librustc/ich/impls_hir.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,8 @@ impl_stable_hash_for!(struct hir::Local {
490490
init,
491491
id,
492492
span,
493-
attrs
493+
attrs,
494+
source
494495
});
495496

496497
impl_stable_hash_for_spanned!(hir::Decl_);
@@ -640,6 +641,11 @@ impl_stable_hash_for!(enum hir::Expr_ {
640641
ExprRepeat(val, times)
641642
});
642643

644+
impl_stable_hash_for!(enum hir::LocalSource {
645+
Normal,
646+
ForLoopDesugar
647+
});
648+
643649
impl_stable_hash_for!(enum hir::LoopSource {
644650
Loop,
645651
WhileLet,

src/librustc_const_eval/check_match.rs

+32-56
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,10 @@ impl<'a, 'tcx> Visitor<'tcx> for MatchVisitor<'a, 'tcx> {
9191
fn visit_local(&mut self, loc: &'tcx hir::Local) {
9292
intravisit::walk_local(self, loc);
9393

94-
self.check_irrefutable(&loc.pat, false);
94+
self.check_irrefutable(&loc.pat, match loc.source {
95+
hir::LocalSource::Normal => "local binding",
96+
hir::LocalSource::ForLoopDesugar => "`for` loop binding",
97+
});
9598

9699
// Check legality of move bindings and `@` patterns.
97100
self.check_patterns(false, slice::ref_slice(&loc.pat));
@@ -101,7 +104,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MatchVisitor<'a, 'tcx> {
101104
intravisit::walk_body(self, body);
102105

103106
for arg in &body.arguments {
104-
self.check_irrefutable(&arg.pat, true);
107+
self.check_irrefutable(&arg.pat, "function argument");
105108
self.check_patterns(false, slice::ref_slice(&arg.pat));
106109
}
107110
}
@@ -210,7 +213,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
210213
.map(|pat| vec![pat.0])
211214
.collect();
212215
let scrut_ty = self.tables.node_id_to_type(scrut.id);
213-
check_exhaustive(cx, scrut_ty, scrut.span, &matrix, source);
216+
check_exhaustive(cx, scrut_ty, scrut.span, &matrix);
214217
})
215218
}
216219

@@ -223,13 +226,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
223226
}
224227
}
225228

226-
fn check_irrefutable(&self, pat: &Pat, is_fn_arg: bool) {
227-
let origin = if is_fn_arg {
228-
"function argument"
229-
} else {
230-
"local binding"
231-
};
232-
229+
fn check_irrefutable(&self, pat: &Pat, origin: &str) {
233230
let module = self.tcx.hir.get_module_parent(pat.id);
234231
MatchCheckCtxt::create_and_enter(self.tcx, module, |ref mut cx| {
235232
let mut patcx = PatternContext::new(self.tcx, self.tables);
@@ -395,8 +392,7 @@ fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
395392
fn check_exhaustive<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
396393
scrut_ty: Ty<'tcx>,
397394
sp: Span,
398-
matrix: &Matrix<'a, 'tcx>,
399-
source: hir::MatchSource) {
395+
matrix: &Matrix<'a, 'tcx>) {
400396
let wild_pattern = Pattern {
401397
ty: scrut_ty,
402398
span: DUMMY_SP,
@@ -409,52 +405,32 @@ fn check_exhaustive<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
409405
} else {
410406
pats.iter().map(|w| w.single_pattern()).collect()
411407
};
412-
match source {
413-
hir::MatchSource::ForLoopDesugar => {
414-
// `witnesses[0]` has the form `Some(<head>)`, peel off the `Some`
415-
let witness = match *witnesses[0].kind {
416-
PatternKind::Variant { ref subpatterns, .. } => match &subpatterns[..] {
417-
&[ref pat] => &pat.pattern,
418-
_ => bug!(),
419-
},
420-
_ => bug!(),
421-
};
422-
let pattern_string = witness.to_string();
423-
struct_span_err!(cx.tcx.sess, sp, E0297,
424-
"refutable pattern in `for` loop binding: \
425-
`{}` not covered",
426-
pattern_string)
427-
.span_label(sp, format!("pattern `{}` not covered", pattern_string))
428-
.emit();
408+
409+
const LIMIT: usize = 3;
410+
let joined_patterns = match witnesses.len() {
411+
0 => bug!(),
412+
1 => format!("`{}`", witnesses[0]),
413+
2...LIMIT => {
414+
let (tail, head) = witnesses.split_last().unwrap();
415+
let head: Vec<_> = head.iter().map(|w| w.to_string()).collect();
416+
format!("`{}` and `{}`", head.join("`, `"), tail)
429417
},
430418
_ => {
431-
const LIMIT: usize = 3;
432-
let joined_patterns = match witnesses.len() {
433-
0 => bug!(),
434-
1 => format!("`{}`", witnesses[0]),
435-
2...LIMIT => {
436-
let (tail, head) = witnesses.split_last().unwrap();
437-
let head: Vec<_> = head.iter().map(|w| w.to_string()).collect();
438-
format!("`{}` and `{}`", head.join("`, `"), tail)
439-
},
440-
_ => {
441-
let (head, tail) = witnesses.split_at(LIMIT);
442-
let head: Vec<_> = head.iter().map(|w| w.to_string()).collect();
443-
format!("`{}` and {} more", head.join("`, `"), tail.len())
444-
}
445-
};
446-
447-
let label_text = match witnesses.len() {
448-
1 => format!("pattern {} not covered", joined_patterns),
449-
_ => format!("patterns {} not covered", joined_patterns)
450-
};
451-
create_e0004(cx.tcx.sess, sp,
452-
format!("non-exhaustive patterns: {} not covered",
453-
joined_patterns))
454-
.span_label(sp, label_text)
455-
.emit();
456-
},
457-
}
419+
let (head, tail) = witnesses.split_at(LIMIT);
420+
let head: Vec<_> = head.iter().map(|w| w.to_string()).collect();
421+
format!("`{}` and {} more", head.join("`, `"), tail.len())
422+
}
423+
};
424+
425+
let label_text = match witnesses.len() {
426+
1 => format!("pattern {} not covered", joined_patterns),
427+
_ => format!("patterns {} not covered", joined_patterns)
428+
};
429+
create_e0004(cx.tcx.sess, sp,
430+
format!("non-exhaustive patterns: {} not covered",
431+
joined_patterns))
432+
.span_label(sp, label_text)
433+
.emit();
458434
}
459435
NotUseful => {
460436
// This is good, wildcard pattern isn't reachable

src/librustc_const_eval/diagnostics.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -452,12 +452,14 @@ enum Method { GET, POST }
452452

453453

454454
E0297: r##"
455+
#### Note: this error code is no longer emitted by the compiler.
456+
455457
Patterns used to bind names must be irrefutable. That is, they must guarantee
456458
that a name will be extracted in all cases. Instead of pattern matching the
457459
loop variable, consider using a `match` or `if let` inside the loop body. For
458460
instance:
459461
460-
```compile_fail,E0297
462+
```compile_fail,E0005
461463
let xs : Vec<Option<i32>> = vec![Some(1), None];
462464
463465
// This fails because `None` is not covered.

src/test/compile-fail/E0297.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ fn main() {
1212
let xs : Vec<Option<i32>> = vec![Some(1), None];
1313

1414
for Some(x) in xs {}
15-
//~^ ERROR E0297
15+
//~^ ERROR E0005
1616
//~| NOTE pattern `None` not covered
1717
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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+
fn main() {
12+
for x in 0..3 {
13+
x //~ ERROR mismatched types
14+
//~| NOTE expected ()
15+
//~| NOTE expected type `()`
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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+
fn main() {
12+
// Check that the tail statement in the body unifies with something
13+
for _ in 0..3 {
14+
unsafe { std::mem::uninitialized() }
15+
}
16+
17+
// Check that the tail statement in the body can be unit
18+
for _ in 0..3 {
19+
()
20+
}
21+
}

0 commit comments

Comments
 (0)