diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index 8eb44458137ff..80deea1468531 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -1,7 +1,9 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +use rustc_errors::Applicability; use rustc_errors::{codes::*, struct_span_code_err, Diag, DiagCtxtHandle}; +use rustc_hir as hir; use rustc_middle::span_bug; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; @@ -382,13 +384,35 @@ impl<'infcx, 'tcx> crate::MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { yield_span: Span, ) -> Diag<'infcx> { let coroutine_kind = self.body.coroutine.as_ref().unwrap().coroutine_kind; - struct_span_code_err!( + let mut diag = struct_span_code_err!( self.dcx(), span, E0626, "borrow may still be in use when {coroutine_kind:#} yields", - ) - .with_span_label(yield_span, "possible yield occurs here") + ); + diag.span_label( + self.infcx.tcx.def_span(self.body.source.def_id()), + format!("within this {coroutine_kind:#}"), + ); + diag.span_label(yield_span, "possible yield occurs here"); + if matches!(coroutine_kind, hir::CoroutineKind::Coroutine(_)) { + let hir::Closure { capture_clause, fn_decl_span, .. } = self + .infcx + .tcx + .hir_node_by_def_id(self.body.source.def_id().expect_local()) + .expect_closure(); + let span = match capture_clause { + rustc_hir::CaptureBy::Value { move_kw } => move_kw.shrink_to_lo(), + rustc_hir::CaptureBy::Ref => fn_decl_span.shrink_to_lo(), + }; + diag.span_suggestion_verbose( + span, + "add `static` to mark this coroutine as unmovable", + "static ", + Applicability::MaybeIncorrect, + ); + } + diag } pub(crate) fn cannot_borrow_across_destructor(&self, borrow_span: Span) -> Diag<'infcx> { diff --git a/compiler/rustc_error_codes/src/error_codes/E0626.md b/compiler/rustc_error_codes/src/error_codes/E0626.md index 28d543350ffd8..71c1f811aa774 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0626.md +++ b/compiler/rustc_error_codes/src/error_codes/E0626.md @@ -1,4 +1,4 @@ -This error occurs because a borrow in a coroutine persists across a +This error occurs because a borrow in a movable coroutine persists across a yield point. Erroneous code example: @@ -15,19 +15,35 @@ let mut b = #[coroutine] || { Pin::new(&mut b).resume(()); ``` -At present, it is not permitted to have a yield that occurs while a -borrow is still in scope. To resolve this error, the borrow must -either be "contained" to a smaller scope that does not overlap the -yield or else eliminated in another way. So, for example, we might -resolve the previous example by removing the borrow and just storing -the integer by value: +Coroutines may be either unmarked, or marked with `static`. If it is unmarked, +then the coroutine is considered "movable". At present, it is not permitted to +have a yield in a movable coroutine that occurs while a borrow is still in +scope. To resolve this error, the coroutine may be marked `static`: + +``` +# #![feature(coroutines, coroutine_trait, stmt_expr_attributes)] +# use std::ops::Coroutine; +# use std::pin::Pin; +let mut b = #[coroutine] static || { // <-- note the static keyword + let a = &String::from("hello, world"); + yield (); + println!("{}", a); +}; +let mut b = std::pin::pin!(b); +b.as_mut().resume(()); +``` + +If the coroutine must remain movable, for example to be used as `Unpin` +without pinning it on the stack or in an allocation, we can alternatively +resolve the previous example by removing the borrow and just storing the +type by value: ``` # #![feature(coroutines, coroutine_trait, stmt_expr_attributes)] # use std::ops::Coroutine; # use std::pin::Pin; let mut b = #[coroutine] || { - let a = 3; + let a = String::from("hello, world"); yield (); println!("{}", a); }; diff --git a/tests/ui/coroutine/coroutine-with-nll.stderr b/tests/ui/coroutine/coroutine-with-nll.stderr index 3f3d51da31180..ee3a8f45f44a7 100644 --- a/tests/ui/coroutine/coroutine-with-nll.stderr +++ b/tests/ui/coroutine/coroutine-with-nll.stderr @@ -1,11 +1,19 @@ error[E0626]: borrow may still be in use when coroutine yields --> $DIR/coroutine-with-nll.rs:8:17 | +LL | || { + | -- within this coroutine +... LL | let b = &mut true; | ^^^^^^^^^ LL | LL | yield (); | -------- possible yield occurs here + | +help: add `static` to mark this coroutine as unmovable + | +LL | static || { + | ++++++ error: aborting due to 1 previous error diff --git a/tests/ui/coroutine/issue-48048.stderr b/tests/ui/coroutine/issue-48048.stderr index 199ecf4ca6aaf..8231dbef7f626 100644 --- a/tests/ui/coroutine/issue-48048.stderr +++ b/tests/ui/coroutine/issue-48048.stderr @@ -1,10 +1,18 @@ error[E0626]: borrow may still be in use when coroutine yields --> $DIR/issue-48048.rs:9:9 | +LL | #[coroutine] || { + | -- within this coroutine +... LL | x.0({ | ^^^ LL | yield; | ----- possible yield occurs here + | +help: add `static` to mark this coroutine as unmovable + | +LL | #[coroutine] static || { + | ++++++ error: aborting due to 1 previous error diff --git a/tests/ui/coroutine/pattern-borrow.stderr b/tests/ui/coroutine/pattern-borrow.stderr index 9e7b330d79de0..a3954b0b8ad68 100644 --- a/tests/ui/coroutine/pattern-borrow.stderr +++ b/tests/ui/coroutine/pattern-borrow.stderr @@ -1,10 +1,17 @@ error[E0626]: borrow may still be in use when coroutine yields --> $DIR/pattern-borrow.rs:9:24 | +LL | #[coroutine] move || { + | ------- within this coroutine LL | if let Test::A(ref _a) = test { | ^^^^^^ LL | yield (); | -------- possible yield occurs here + | +help: add `static` to mark this coroutine as unmovable + | +LL | #[coroutine] static move || { + | ++++++ error: aborting due to 1 previous error diff --git a/tests/ui/coroutine/self_referential_gen_block.stderr b/tests/ui/coroutine/self_referential_gen_block.stderr index e23d653d0d447..2f53e7c84a10b 100644 --- a/tests/ui/coroutine/self_referential_gen_block.stderr +++ b/tests/ui/coroutine/self_referential_gen_block.stderr @@ -1,6 +1,9 @@ error[E0626]: borrow may still be in use when `gen` block yields --> $DIR/self_referential_gen_block.rs:9:21 | +LL | let mut x = gen { + | --- within this `gen` block +LL | let y = 42; LL | let z = &y; | ^^ LL | yield 43; diff --git a/tests/ui/coroutine/yield-in-args.stderr b/tests/ui/coroutine/yield-in-args.stderr index 1d2c54f9bdbfa..1cc3c83deb3b8 100644 --- a/tests/ui/coroutine/yield-in-args.stderr +++ b/tests/ui/coroutine/yield-in-args.stderr @@ -1,8 +1,16 @@ error[E0626]: borrow may still be in use when coroutine yields --> $DIR/yield-in-args.rs:9:13 | +LL | || { + | -- within this coroutine +LL | let b = true; LL | foo(&b, yield); | ^^ ----- possible yield occurs here + | +help: add `static` to mark this coroutine as unmovable + | +LL | static || { + | ++++++ error: aborting due to 1 previous error diff --git a/tests/ui/coroutine/yield-while-iterating.stderr b/tests/ui/coroutine/yield-while-iterating.stderr index f81c914c4bd49..a92237e44c16b 100644 --- a/tests/ui/coroutine/yield-while-iterating.stderr +++ b/tests/ui/coroutine/yield-while-iterating.stderr @@ -1,10 +1,17 @@ error[E0626]: borrow may still be in use when coroutine yields --> $DIR/yield-while-iterating.rs:13:18 | +LL | let _b =#[coroutine] move || { + | ------- within this coroutine LL | for p in &x { | ^^ LL | yield(); | ------- possible yield occurs here + | +help: add `static` to mark this coroutine as unmovable + | +LL | let _b =#[coroutine] static move || { + | ++++++ error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable --> $DIR/yield-while-iterating.rs:58:20 diff --git a/tests/ui/coroutine/yield-while-local-borrowed.stderr b/tests/ui/coroutine/yield-while-local-borrowed.stderr index 8fe981de929a1..b42ca3ba46106 100644 --- a/tests/ui/coroutine/yield-while-local-borrowed.stderr +++ b/tests/ui/coroutine/yield-while-local-borrowed.stderr @@ -1,20 +1,35 @@ error[E0626]: borrow may still be in use when coroutine yields --> $DIR/yield-while-local-borrowed.rs:13:17 | +LL | let mut b = #[coroutine] move || { + | ------- within this coroutine LL | let a = &mut 3; | ^^^^^^ LL | LL | yield (); | -------- possible yield occurs here + | +help: add `static` to mark this coroutine as unmovable + | +LL | let mut b = #[coroutine] static move || { + | ++++++ error[E0626]: borrow may still be in use when coroutine yields --> $DIR/yield-while-local-borrowed.rs:40:21 | +LL | let mut b = #[coroutine] move || { + | ------- within this coroutine +... LL | let b = &a; | ^^ LL | LL | yield (); | -------- possible yield occurs here + | +help: add `static` to mark this coroutine as unmovable + | +LL | let mut b = #[coroutine] static move || { + | ++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/nll/issue-55850.stderr b/tests/ui/nll/issue-55850.stderr index 3d43817f4d8a2..5a9c779919790 100644 --- a/tests/ui/nll/issue-55850.stderr +++ b/tests/ui/nll/issue-55850.stderr @@ -10,8 +10,16 @@ LL | yield &s[..] error[E0626]: borrow may still be in use when coroutine yields --> $DIR/issue-55850.rs:28:16 | +LL | GenIter(#[coroutine] move || { + | ------- within this coroutine +LL | let mut s = String::new(); LL | yield &s[..] | -------^---- possible yield occurs here + | +help: add `static` to mark this coroutine as unmovable + | +LL | GenIter(#[coroutine] static move || { + | ++++++ error: aborting due to 2 previous errors