diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 9db7dec1cf8..247200dcd26 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -2491,11 +2491,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) { Ok(string) => { - if string.starts_with("async ") { - let pos = args_span.lo() + BytePos(6); - (args_span.with_lo(pos).with_hi(pos), "move ") - } else if string.starts_with("async|") { - let pos = args_span.lo() + BytePos(5); + let coro_prefix = if string.starts_with("async") { + // `async` is 5 chars long. Not using `.len()` to avoid the cast from `usize` to `u32` + Some(5) + } else if string.starts_with("gen") { + // `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") } else { (args_span.shrink_to_lo(), "move ") diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index c6c7f025418..0b5ec9b59ea 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1441,7 +1441,7 @@ impl<'a> Parser<'a> { } else if this.token.uninterpolated_span().at_least_rust_2018() { // `Span:.at_least_rust_2018()` is somewhat expensive; don't get it repeatedly. if this.check_keyword(kw::Async) { - if this.is_async_block() { + if this.is_gen_block(kw::Async) { // Check for `async {` and `async move {`. this.parse_gen_block() } else { @@ -1450,7 +1450,11 @@ impl<'a> Parser<'a> { } else if this.eat_keyword(kw::Await) { this.recover_incorrect_await_syntax(lo, this.prev_token.span) } 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 { this.parse_expr_lit() } @@ -3061,13 +3065,6 @@ impl<'a> Parser<'a> { && 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. fn parse_gen_block(&mut self) -> PResult<'a, P> { 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)) } - fn is_async_block(&self) -> bool { - self.token.is_keyword(kw::Async) + fn is_gen_block(&self, kw: Symbol) -> bool { + self.token.is_keyword(kw) && (( // `async move {` self.is_keyword_ahead(1, &[kw::Move]) diff --git a/tests/ui/coroutine/gen_block_iterate.rs b/tests/ui/coroutine/gen_block_iterate.rs index 04c44d58e52..131dd687936 100644 --- a/tests/ui/coroutine/gen_block_iterate.rs +++ b/tests/ui/coroutine/gen_block_iterate.rs @@ -8,6 +8,16 @@ fn foo() -> impl Iterator { gen { yield 42; for x in 3..6 { yield x } } } +fn moved() -> impl Iterator { + 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() { let mut iter = foo(); assert_eq!(iter.next(), Some(42)); @@ -15,4 +25,9 @@ fn main() { assert_eq!(iter.next(), Some(4)); assert_eq!(iter.next(), Some(5)); assert_eq!(iter.next(), None); + + let mut iter = moved(); + assert_eq!(iter.next(), Some(42)); + assert_eq!(iter.next(), None); + } diff --git a/tests/ui/coroutine/gen_block_iterate_fused.rs b/tests/ui/coroutine/gen_block_iterate_fused.rs new file mode 100644 index 00000000000..8ee6baf1060 --- /dev/null +++ b/tests/ui/coroutine/gen_block_iterate_fused.rs @@ -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 { + 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); +} diff --git a/tests/ui/coroutine/gen_block_move.fixed b/tests/ui/coroutine/gen_block_move.fixed new file mode 100644 index 00000000000..5c6c8062322 --- /dev/null +++ b/tests/ui/coroutine/gen_block_move.fixed @@ -0,0 +1,17 @@ +// compile-flags: --edition 2024 -Zunstable-options +// run-rustfix +#![feature(gen_blocks)] + +fn moved() -> impl Iterator { + 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() {} +} diff --git a/tests/ui/coroutine/gen_block_move.rs b/tests/ui/coroutine/gen_block_move.rs new file mode 100644 index 00000000000..abbf8132476 --- /dev/null +++ b/tests/ui/coroutine/gen_block_move.rs @@ -0,0 +1,17 @@ +// compile-flags: --edition 2024 -Zunstable-options +// run-rustfix +#![feature(gen_blocks)] + +fn moved() -> impl Iterator { + 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() {} +} diff --git a/tests/ui/coroutine/gen_block_move.stderr b/tests/ui/coroutine/gen_block_move.stderr new file mode 100644 index 00000000000..b93ac65f5e7 --- /dev/null +++ b/tests/ui/coroutine/gen_block_move.stderr @@ -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`.