about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs16
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs19
-rw-r--r--tests/ui/coroutine/gen_block_iterate.rs15
-rw-r--r--tests/ui/coroutine/gen_block_iterate_fused.rs19
-rw-r--r--tests/ui/coroutine/gen_block_move.fixed17
-rw-r--r--tests/ui/coroutine/gen_block_move.rs17
-rw-r--r--tests/ui/coroutine/gen_block_move.stderr30
7 files changed, 117 insertions, 16 deletions
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<Expr>> {
         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<Item = u32> {
     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() {
     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<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);
+}
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<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() {}
+}
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<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() {}
+}
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`.