From aaebbe196b16aa3006377ad4dbbb8755fa62ece8 Mon Sep 17 00:00:00 2001 From: Alex Aktsipetrov Date: Tue, 7 Apr 2020 23:57:26 +0200 Subject: [PATCH] Suggest move for closures and async blocks in more cases. --- .../diagnostics/conflict_errors.rs | 58 +++++++------------ src/librustc_mir/lib.rs | 1 + src/librustc_mir/util/borrowck_errors.rs | 4 +- .../async-borrowck-escaping-block-error.fixed | 10 +++- .../async-borrowck-escaping-block-error.rs | 10 +++- ...async-borrowck-escaping-block-error.stderr | 31 ++++++++-- .../async-borrowck-escaping-closure-error.rs | 2 +- .../impl-trait/does-not-live-long-enough.rs | 2 +- .../does-not-live-long-enough.stderr | 26 ++++----- 9 files changed, 80 insertions(+), 64 deletions(-) diff --git a/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs b/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs index 9df5760563129..65561e224dbfd 100644 --- a/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs @@ -760,47 +760,26 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ( Some(ref name), BorrowExplanation::MustBeValidFor { - category: category @ ConstraintCategory::Return, + category: + category + @ + (ConstraintCategory::Return + | ConstraintCategory::CallArgument + | ConstraintCategory::OpaqueType), from_closure: false, ref region_name, span, .. }, - ) - | ( - Some(ref name), - BorrowExplanation::MustBeValidFor { - category: category @ ConstraintCategory::CallArgument, - from_closure: false, - ref region_name, - span, - .. - }, - ) if borrow_spans.for_closure() => self.report_escaping_closure_capture( - borrow_spans, - borrow_span, - region_name, - category, - span, - &format!("`{}`", name), - ), - ( - Some(ref name), - BorrowExplanation::MustBeValidFor { - category: category @ ConstraintCategory::OpaqueType, - from_closure: false, - ref region_name, + ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self + .report_escaping_closure_capture( + borrow_spans, + borrow_span, + region_name, + category, span, - .. - }, - ) if borrow_spans.for_generator() => self.report_escaping_closure_capture( - borrow_spans, - borrow_span, - region_name, - category, - span, - &format!("`{}`", name), - ), + &format!("`{}`", name), + ), ( ref name, BorrowExplanation::MustBeValidFor { @@ -1187,7 +1166,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ) -> DiagnosticBuilder<'cx> { let tcx = self.infcx.tcx; let args_span = use_span.args_or_use(); - let mut err = self.cannot_capture_in_long_lived_closure(args_span, captured_var, var_span); let suggestion = match tcx.sess.source_map().span_to_snippet(args_span) { Ok(mut string) => { @@ -1213,6 +1191,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { }, None => "closure", }; + + let mut err = + self.cannot_capture_in_long_lived_closure(args_span, kind, captured_var, var_span); err.span_suggestion( args_span, &format!( @@ -1225,8 +1206,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); let msg = match category { - ConstraintCategory::Return => "closure is returned here".to_string(), - ConstraintCategory::OpaqueType => "generator is returned here".to_string(), + ConstraintCategory::Return | ConstraintCategory::OpaqueType => { + format!("{} is returned here", kind) + } ConstraintCategory::CallArgument => { fr_name.highlight_region_name(&mut err); format!("function requires argument type to outlive `{}`", fr_name) diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index e07b8535b9009..07822f865d2bd 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -25,6 +25,7 @@ Rust MIR: a lowered representation of Rust. #![feature(stmt_expr_attributes)] #![feature(trait_alias)] #![feature(option_expect_none)] +#![feature(or_patterns)] #![recursion_limit = "256"] #[macro_use] diff --git a/src/librustc_mir/util/borrowck_errors.rs b/src/librustc_mir/util/borrowck_errors.rs index 808dd833774e4..f8bb7e7a85d11 100644 --- a/src/librustc_mir/util/borrowck_errors.rs +++ b/src/librustc_mir/util/borrowck_errors.rs @@ -431,6 +431,7 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> { crate fn cannot_capture_in_long_lived_closure( &self, closure_span: Span, + closure_kind: &str, borrowed_path: &str, capture_span: Span, ) -> DiagnosticBuilder<'cx> { @@ -438,9 +439,10 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> { self, closure_span, E0373, - "closure may outlive the current function, \ + "{} may outlive the current function, \ but it borrows {}, \ which is owned by the current function", + closure_kind, borrowed_path, ); err.span_label(capture_span, format!("{} is borrowed here", borrowed_path)) diff --git a/src/test/ui/async-await/async-borrowck-escaping-block-error.fixed b/src/test/ui/async-await/async-borrowck-escaping-block-error.fixed index f004b4180ddc9..605cfdfe747a3 100644 --- a/src/test/ui/async-await/async-borrowck-escaping-block-error.fixed +++ b/src/test/ui/async-await/async-borrowck-escaping-block-error.fixed @@ -1,12 +1,18 @@ // edition:2018 // run-rustfix -fn foo() -> Box> { +fn test_boxed() -> Box> { let x = 0u32; Box::new(async move { x } ) //~^ ERROR E0373 } +fn test_ref(x: &u32) -> impl std::future::Future + '_ { + async move { *x } + //~^ ERROR E0373 +} + fn main() { - let _foo = foo(); + let _ = test_boxed(); + let _ = test_ref(&0u32); } diff --git a/src/test/ui/async-await/async-borrowck-escaping-block-error.rs b/src/test/ui/async-await/async-borrowck-escaping-block-error.rs index 4f35fd52ca39b..ec752c15fa284 100644 --- a/src/test/ui/async-await/async-borrowck-escaping-block-error.rs +++ b/src/test/ui/async-await/async-borrowck-escaping-block-error.rs @@ -1,12 +1,18 @@ // edition:2018 // run-rustfix -fn foo() -> Box> { +fn test_boxed() -> Box> { let x = 0u32; Box::new(async { x } ) //~^ ERROR E0373 } +fn test_ref(x: &u32) -> impl std::future::Future + '_ { + async { *x } + //~^ ERROR E0373 +} + fn main() { - let _foo = foo(); + let _ = test_boxed(); + let _ = test_ref(&0u32); } diff --git a/src/test/ui/async-await/async-borrowck-escaping-block-error.stderr b/src/test/ui/async-await/async-borrowck-escaping-block-error.stderr index 0eb3971d14a38..193026541d073 100644 --- a/src/test/ui/async-await/async-borrowck-escaping-block-error.stderr +++ b/src/test/ui/async-await/async-borrowck-escaping-block-error.stderr @@ -1,4 +1,4 @@ -error[E0373]: closure may outlive the current function, but it borrows `x`, which is owned by the current function +error[E0373]: async block may outlive the current function, but it borrows `x`, which is owned by the current function --> $DIR/async-borrowck-escaping-block-error.rs:6:20 | LL | Box::new(async { x } ) @@ -7,16 +7,35 @@ LL | Box::new(async { x } ) | | `x` is borrowed here | may outlive borrowed value `x` | -note: generator is returned here - --> $DIR/async-borrowck-escaping-block-error.rs:4:13 +note: async block is returned here + --> $DIR/async-borrowck-escaping-block-error.rs:4:20 | -LL | fn foo() -> Box> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn test_boxed() -> Box> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: to force the async block to take ownership of `x` (and any other referenced variables), use the `move` keyword | LL | Box::new(async move { x } ) | ^^^^^^^^^^ -error: aborting due to previous error +error[E0373]: async block may outlive the current function, but it borrows `x`, which is owned by the current function + --> $DIR/async-borrowck-escaping-block-error.rs:11:11 + | +LL | async { *x } + | ^^^-^^ + | | | + | | `x` is borrowed here + | may outlive borrowed value `x` + | +note: async block is returned here + --> $DIR/async-borrowck-escaping-block-error.rs:11:5 + | +LL | async { *x } + | ^^^^^^^^^^^^ +help: to force the async block to take ownership of `x` (and any other referenced variables), use the `move` keyword + | +LL | async move { *x } + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0373`. diff --git a/src/test/ui/async-await/async-borrowck-escaping-closure-error.rs b/src/test/ui/async-await/async-borrowck-escaping-closure-error.rs index d2fa5d0a3d0f1..e667b72aee530 100644 --- a/src/test/ui/async-await/async-borrowck-escaping-closure-error.rs +++ b/src/test/ui/async-await/async-borrowck-escaping-closure-error.rs @@ -1,5 +1,5 @@ // edition:2018 -#![feature(async_closure,async_await)] +#![feature(async_closure)] fn foo() -> Box> { let x = 0u32; Box::new((async || x)()) diff --git a/src/test/ui/impl-trait/does-not-live-long-enough.rs b/src/test/ui/impl-trait/does-not-live-long-enough.rs index 6179132b3f608..d2a345231eb43 100644 --- a/src/test/ui/impl-trait/does-not-live-long-enough.rs +++ b/src/test/ui/impl-trait/does-not-live-long-enough.rs @@ -4,7 +4,7 @@ struct List { impl List { fn started_with<'a>(&'a self, prefix: &'a str) -> impl Iterator { self.data.iter().filter(|s| s.starts_with(prefix)).map(|s| s.as_ref()) - //~^ ERROR does not live long enough + //~^ ERROR E0373 } } diff --git a/src/test/ui/impl-trait/does-not-live-long-enough.stderr b/src/test/ui/impl-trait/does-not-live-long-enough.stderr index 9cff4bcd8b579..468c2f366299c 100644 --- a/src/test/ui/impl-trait/does-not-live-long-enough.stderr +++ b/src/test/ui/impl-trait/does-not-live-long-enough.stderr @@ -1,21 +1,21 @@ -error[E0597]: `prefix` does not live long enough - --> $DIR/does-not-live-long-enough.rs:6:51 +error[E0373]: closure may outlive the current function, but it borrows `prefix`, which is owned by the current function + --> $DIR/does-not-live-long-enough.rs:6:33 | -LL | fn started_with<'a>(&'a self, prefix: &'a str) -> impl Iterator { - | -- lifetime `'a` defined here --------------------------- opaque type requires that `prefix` is borrowed for `'a` LL | self.data.iter().filter(|s| s.starts_with(prefix)).map(|s| s.as_ref()) - | --- ^^^^^^ borrowed value does not live long enough + | ^^^ ------ `prefix` is borrowed here | | - | value captured here -LL | -LL | } - | - `prefix` dropped here while still borrowed + | may outlive borrowed value `prefix` + | +note: closure is returned here + --> $DIR/does-not-live-long-enough.rs:5:55 | -help: you can add a bound to the opaque type to make it last less than `'static` and match `'a` +LL | fn started_with<'a>(&'a self, prefix: &'a str) -> impl Iterator { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: to force the closure to take ownership of `prefix` (and any other referenced variables), use the `move` keyword | -LL | fn started_with<'a>(&'a self, prefix: &'a str) -> impl Iterator + 'a { - | ^^^^ +LL | self.data.iter().filter(move |s| s.starts_with(prefix)).map(|s| s.as_ref()) + | ^^^^^^^^ error: aborting due to previous error -For more information about this error, try `rustc --explain E0597`. +For more information about this error, try `rustc --explain E0373`.