Handle move
generators
This commit is contained in:
parent
cece90c65f
commit
4ac25faf9f
7 changed files with 117 additions and 16 deletions
|
@ -2491,11 +2491,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
|
|
||||||
let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
|
let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
|
||||||
Ok(string) => {
|
Ok(string) => {
|
||||||
if string.starts_with("async ") {
|
let coro_prefix = if string.starts_with("async") {
|
||||||
let pos = args_span.lo() + BytePos(6);
|
// `async` is 5 chars long. Not using `.len()` to avoid the cast from `usize` to `u32`
|
||||||
(args_span.with_lo(pos).with_hi(pos), "move ")
|
Some(5)
|
||||||
} else if string.starts_with("async|") {
|
} else if string.starts_with("gen") {
|
||||||
let pos = args_span.lo() + BytePos(5);
|
// `gen` is 3 chars long
|
||||||
|
Some(3)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
if let Some(n) = coro_prefix {
|
||||||
|
let pos = args_span.lo() + BytePos(n);
|
||||||
(args_span.with_lo(pos).with_hi(pos), " move")
|
(args_span.with_lo(pos).with_hi(pos), " move")
|
||||||
} else {
|
} else {
|
||||||
(args_span.shrink_to_lo(), "move ")
|
(args_span.shrink_to_lo(), "move ")
|
||||||
|
|
|
@ -1441,7 +1441,7 @@ impl<'a> Parser<'a> {
|
||||||
} else if this.token.uninterpolated_span().at_least_rust_2018() {
|
} else if this.token.uninterpolated_span().at_least_rust_2018() {
|
||||||
// `Span:.at_least_rust_2018()` is somewhat expensive; don't get it repeatedly.
|
// `Span:.at_least_rust_2018()` is somewhat expensive; don't get it repeatedly.
|
||||||
if this.check_keyword(kw::Async) {
|
if this.check_keyword(kw::Async) {
|
||||||
if this.is_async_block() {
|
if this.is_gen_block(kw::Async) {
|
||||||
// Check for `async {` and `async move {`.
|
// Check for `async {` and `async move {`.
|
||||||
this.parse_gen_block()
|
this.parse_gen_block()
|
||||||
} else {
|
} else {
|
||||||
|
@ -1450,7 +1450,11 @@ impl<'a> Parser<'a> {
|
||||||
} else if this.eat_keyword(kw::Await) {
|
} else if this.eat_keyword(kw::Await) {
|
||||||
this.recover_incorrect_await_syntax(lo, this.prev_token.span)
|
this.recover_incorrect_await_syntax(lo, this.prev_token.span)
|
||||||
} else if this.token.uninterpolated_span().at_least_rust_2024() {
|
} else if this.token.uninterpolated_span().at_least_rust_2024() {
|
||||||
if this.is_gen_block() { this.parse_gen_block() } else { this.parse_expr_lit() }
|
if this.is_gen_block(kw::Gen) {
|
||||||
|
this.parse_gen_block()
|
||||||
|
} else {
|
||||||
|
this.parse_expr_lit()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.parse_expr_lit()
|
this.parse_expr_lit()
|
||||||
}
|
}
|
||||||
|
@ -3061,13 +3065,6 @@ impl<'a> Parser<'a> {
|
||||||
&& self.token.uninterpolated_span().at_least_rust_2018()
|
&& self.token.uninterpolated_span().at_least_rust_2018()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_gen_block(&self) -> bool {
|
|
||||||
self.token.is_keyword(kw::Gen)
|
|
||||||
&& self
|
|
||||||
.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block())
|
|
||||||
&& self.token.uninterpolated_span().at_least_rust_2024()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parses an `async move? {...}` or `gen move? {...}` expression.
|
/// Parses an `async move? {...}` or `gen move? {...}` expression.
|
||||||
fn parse_gen_block(&mut self) -> PResult<'a, P<Expr>> {
|
fn parse_gen_block(&mut self) -> PResult<'a, P<Expr>> {
|
||||||
let lo = self.token.span;
|
let lo = self.token.span;
|
||||||
|
@ -3084,8 +3081,8 @@ impl<'a> Parser<'a> {
|
||||||
Ok(self.mk_expr_with_attrs(lo.to(self.prev_token.span), kind, attrs))
|
Ok(self.mk_expr_with_attrs(lo.to(self.prev_token.span), kind, attrs))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_async_block(&self) -> bool {
|
fn is_gen_block(&self, kw: Symbol) -> bool {
|
||||||
self.token.is_keyword(kw::Async)
|
self.token.is_keyword(kw)
|
||||||
&& ((
|
&& ((
|
||||||
// `async move {`
|
// `async move {`
|
||||||
self.is_keyword_ahead(1, &[kw::Move])
|
self.is_keyword_ahead(1, &[kw::Move])
|
||||||
|
|
|
@ -8,6 +8,16 @@ fn foo() -> impl Iterator<Item = u32> {
|
||||||
gen { yield 42; for x in 3..6 { yield x } }
|
gen { yield 42; for x in 3..6 { yield x } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn moved() -> impl Iterator<Item = u32> {
|
||||||
|
let mut x = "foo".to_string();
|
||||||
|
gen move {
|
||||||
|
yield 42;
|
||||||
|
if x == "foo" { return }
|
||||||
|
x.clear();
|
||||||
|
for x in 3..6 { yield x }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut iter = foo();
|
let mut iter = foo();
|
||||||
assert_eq!(iter.next(), Some(42));
|
assert_eq!(iter.next(), Some(42));
|
||||||
|
@ -15,4 +25,9 @@ fn main() {
|
||||||
assert_eq!(iter.next(), Some(4));
|
assert_eq!(iter.next(), Some(4));
|
||||||
assert_eq!(iter.next(), Some(5));
|
assert_eq!(iter.next(), Some(5));
|
||||||
assert_eq!(iter.next(), None);
|
assert_eq!(iter.next(), None);
|
||||||
|
|
||||||
|
let mut iter = moved();
|
||||||
|
assert_eq!(iter.next(), Some(42));
|
||||||
|
assert_eq!(iter.next(), None);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
19
tests/ui/coroutine/gen_block_iterate_fused.rs
Normal file
19
tests/ui/coroutine/gen_block_iterate_fused.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
// revisions: next old
|
||||||
|
//compile-flags: --edition 2024 -Zunstable-options
|
||||||
|
//[next] compile-flags: -Ztrait-solver=next
|
||||||
|
// run-fail
|
||||||
|
#![feature(gen_blocks)]
|
||||||
|
|
||||||
|
fn foo() -> impl Iterator<Item = u32> {
|
||||||
|
gen { yield 42; for x in 3..6 { yield x } }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut iter = foo();
|
||||||
|
assert_eq!(iter.next(), Some(42));
|
||||||
|
assert_eq!(iter.next(), Some(3));
|
||||||
|
assert_eq!(iter.next(), Some(4));
|
||||||
|
assert_eq!(iter.next(), Some(5));
|
||||||
|
assert_eq!(iter.next(), None);
|
||||||
|
assert_eq!(iter.next(), None);
|
||||||
|
}
|
17
tests/ui/coroutine/gen_block_move.fixed
Normal file
17
tests/ui/coroutine/gen_block_move.fixed
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// compile-flags: --edition 2024 -Zunstable-options
|
||||||
|
// run-rustfix
|
||||||
|
#![feature(gen_blocks)]
|
||||||
|
|
||||||
|
fn moved() -> impl Iterator<Item = u32> {
|
||||||
|
let mut x = "foo".to_string();
|
||||||
|
gen move { //~ ERROR: gen block may outlive the current function
|
||||||
|
yield 42;
|
||||||
|
if x == "foo" { return }
|
||||||
|
x.clear();
|
||||||
|
for x in 3..6 { yield x }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
for _ in moved() {}
|
||||||
|
}
|
17
tests/ui/coroutine/gen_block_move.rs
Normal file
17
tests/ui/coroutine/gen_block_move.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// compile-flags: --edition 2024 -Zunstable-options
|
||||||
|
// run-rustfix
|
||||||
|
#![feature(gen_blocks)]
|
||||||
|
|
||||||
|
fn moved() -> impl Iterator<Item = u32> {
|
||||||
|
let mut x = "foo".to_string();
|
||||||
|
gen { //~ ERROR: gen block may outlive the current function
|
||||||
|
yield 42;
|
||||||
|
if x == "foo" { return }
|
||||||
|
x.clear();
|
||||||
|
for x in 3..6 { yield x }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
for _ in moved() {}
|
||||||
|
}
|
30
tests/ui/coroutine/gen_block_move.stderr
Normal file
30
tests/ui/coroutine/gen_block_move.stderr
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
error[E0373]: gen block may outlive the current function, but it borrows `x`, which is owned by the current function
|
||||||
|
--> $DIR/gen_block_move.rs:7:5
|
||||||
|
|
|
||||||
|
LL | / gen {
|
||||||
|
LL | | yield 42;
|
||||||
|
LL | | if x == "foo" { return }
|
||||||
|
LL | | x.clear();
|
||||||
|
| | - `x` is borrowed here
|
||||||
|
LL | | for x in 3..6 { yield x }
|
||||||
|
LL | | }
|
||||||
|
| |_____^ may outlive borrowed value `x`
|
||||||
|
|
|
||||||
|
note: gen block is returned here
|
||||||
|
--> $DIR/gen_block_move.rs:7:5
|
||||||
|
|
|
||||||
|
LL | / gen {
|
||||||
|
LL | | yield 42;
|
||||||
|
LL | | if x == "foo" { return }
|
||||||
|
LL | | x.clear();
|
||||||
|
LL | | for x in 3..6 { yield x }
|
||||||
|
LL | | }
|
||||||
|
| |_____^
|
||||||
|
help: to force the gen block to take ownership of `x` (and any other referenced variables), use the `move` keyword
|
||||||
|
|
|
||||||
|
LL | gen move {
|
||||||
|
| ++++
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0373`.
|
Loading…
Add table
Add a link
Reference in a new issue