Skip to content

Commit 4ac25fa

Browse files
committed
Handle move generators
1 parent cece90c commit 4ac25fa

File tree

7 files changed

+117
-16
lines changed

7 files changed

+117
-16
lines changed

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2491,11 +2491,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
24912491

24922492
let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
24932493
Ok(string) => {
2494-
if string.starts_with("async ") {
2495-
let pos = args_span.lo() + BytePos(6);
2496-
(args_span.with_lo(pos).with_hi(pos), "move ")
2497-
} else if string.starts_with("async|") {
2498-
let pos = args_span.lo() + BytePos(5);
2494+
let coro_prefix = if string.starts_with("async") {
2495+
// `async` is 5 chars long. Not using `.len()` to avoid the cast from `usize` to `u32`
2496+
Some(5)
2497+
} else if string.starts_with("gen") {
2498+
// `gen` is 3 chars long
2499+
Some(3)
2500+
} else {
2501+
None
2502+
};
2503+
if let Some(n) = coro_prefix {
2504+
let pos = args_span.lo() + BytePos(n);
24992505
(args_span.with_lo(pos).with_hi(pos), " move")
25002506
} else {
25012507
(args_span.shrink_to_lo(), "move ")

compiler/rustc_parse/src/parser/expr.rs

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1441,7 +1441,7 @@ impl<'a> Parser<'a> {
14411441
} else if this.token.uninterpolated_span().at_least_rust_2018() {
14421442
// `Span:.at_least_rust_2018()` is somewhat expensive; don't get it repeatedly.
14431443
if this.check_keyword(kw::Async) {
1444-
if this.is_async_block() {
1444+
if this.is_gen_block(kw::Async) {
14451445
// Check for `async {` and `async move {`.
14461446
this.parse_gen_block()
14471447
} else {
@@ -1450,7 +1450,11 @@ impl<'a> Parser<'a> {
14501450
} else if this.eat_keyword(kw::Await) {
14511451
this.recover_incorrect_await_syntax(lo, this.prev_token.span)
14521452
} else if this.token.uninterpolated_span().at_least_rust_2024() {
1453-
if this.is_gen_block() { this.parse_gen_block() } else { this.parse_expr_lit() }
1453+
if this.is_gen_block(kw::Gen) {
1454+
this.parse_gen_block()
1455+
} else {
1456+
this.parse_expr_lit()
1457+
}
14541458
} else {
14551459
this.parse_expr_lit()
14561460
}
@@ -3061,13 +3065,6 @@ impl<'a> Parser<'a> {
30613065
&& self.token.uninterpolated_span().at_least_rust_2018()
30623066
}
30633067

3064-
fn is_gen_block(&self) -> bool {
3065-
self.token.is_keyword(kw::Gen)
3066-
&& self
3067-
.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block())
3068-
&& self.token.uninterpolated_span().at_least_rust_2024()
3069-
}
3070-
30713068
/// Parses an `async move? {...}` or `gen move? {...}` expression.
30723069
fn parse_gen_block(&mut self) -> PResult<'a, P<Expr>> {
30733070
let lo = self.token.span;
@@ -3084,8 +3081,8 @@ impl<'a> Parser<'a> {
30843081
Ok(self.mk_expr_with_attrs(lo.to(self.prev_token.span), kind, attrs))
30853082
}
30863083

3087-
fn is_async_block(&self) -> bool {
3088-
self.token.is_keyword(kw::Async)
3084+
fn is_gen_block(&self, kw: Symbol) -> bool {
3085+
self.token.is_keyword(kw)
30893086
&& ((
30903087
// `async move {`
30913088
self.is_keyword_ahead(1, &[kw::Move])

tests/ui/coroutine/gen_block_iterate.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,26 @@ fn foo() -> impl Iterator<Item = u32> {
88
gen { yield 42; for x in 3..6 { yield x } }
99
}
1010

11+
fn moved() -> impl Iterator<Item = u32> {
12+
let mut x = "foo".to_string();
13+
gen move {
14+
yield 42;
15+
if x == "foo" { return }
16+
x.clear();
17+
for x in 3..6 { yield x }
18+
}
19+
}
20+
1121
fn main() {
1222
let mut iter = foo();
1323
assert_eq!(iter.next(), Some(42));
1424
assert_eq!(iter.next(), Some(3));
1525
assert_eq!(iter.next(), Some(4));
1626
assert_eq!(iter.next(), Some(5));
1727
assert_eq!(iter.next(), None);
28+
29+
let mut iter = moved();
30+
assert_eq!(iter.next(), Some(42));
31+
assert_eq!(iter.next(), None);
32+
1833
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// revisions: next old
2+
//compile-flags: --edition 2024 -Zunstable-options
3+
//[next] compile-flags: -Ztrait-solver=next
4+
// run-fail
5+
#![feature(gen_blocks)]
6+
7+
fn foo() -> impl Iterator<Item = u32> {
8+
gen { yield 42; for x in 3..6 { yield x } }
9+
}
10+
11+
fn main() {
12+
let mut iter = foo();
13+
assert_eq!(iter.next(), Some(42));
14+
assert_eq!(iter.next(), Some(3));
15+
assert_eq!(iter.next(), Some(4));
16+
assert_eq!(iter.next(), Some(5));
17+
assert_eq!(iter.next(), None);
18+
assert_eq!(iter.next(), None);
19+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// compile-flags: --edition 2024 -Zunstable-options
2+
// run-rustfix
3+
#![feature(gen_blocks)]
4+
5+
fn moved() -> impl Iterator<Item = u32> {
6+
let mut x = "foo".to_string();
7+
gen move { //~ ERROR: gen block may outlive the current function
8+
yield 42;
9+
if x == "foo" { return }
10+
x.clear();
11+
for x in 3..6 { yield x }
12+
}
13+
}
14+
15+
fn main() {
16+
for _ in moved() {}
17+
}

tests/ui/coroutine/gen_block_move.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// compile-flags: --edition 2024 -Zunstable-options
2+
// run-rustfix
3+
#![feature(gen_blocks)]
4+
5+
fn moved() -> impl Iterator<Item = u32> {
6+
let mut x = "foo".to_string();
7+
gen { //~ ERROR: gen block may outlive the current function
8+
yield 42;
9+
if x == "foo" { return }
10+
x.clear();
11+
for x in 3..6 { yield x }
12+
}
13+
}
14+
15+
fn main() {
16+
for _ in moved() {}
17+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
error[E0373]: gen block may outlive the current function, but it borrows `x`, which is owned by the current function
2+
--> $DIR/gen_block_move.rs:7:5
3+
|
4+
LL | / gen {
5+
LL | | yield 42;
6+
LL | | if x == "foo" { return }
7+
LL | | x.clear();
8+
| | - `x` is borrowed here
9+
LL | | for x in 3..6 { yield x }
10+
LL | | }
11+
| |_____^ may outlive borrowed value `x`
12+
|
13+
note: gen block is returned here
14+
--> $DIR/gen_block_move.rs:7:5
15+
|
16+
LL | / gen {
17+
LL | | yield 42;
18+
LL | | if x == "foo" { return }
19+
LL | | x.clear();
20+
LL | | for x in 3..6 { yield x }
21+
LL | | }
22+
| |_____^
23+
help: to force the gen block to take ownership of `x` (and any other referenced variables), use the `move` keyword
24+
|
25+
LL | gen move {
26+
| ++++
27+
28+
error: aborting due to previous error
29+
30+
For more information about this error, try `rustc --explain E0373`.

0 commit comments

Comments
 (0)