Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

async/await: improve not-send errors, part 2 #65345

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/libcore/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use crate::hash::Hasher;
/// [arc]: ../../std/sync/struct.Arc.html
/// [ub]: ../../reference/behavior-considered-undefined.html
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "send_trait")]
#[rustc_on_unimplemented(
message="`{Self}` cannot be sent between threads safely",
label="`{Self}` cannot be sent between threads safely"
Expand Down Expand Up @@ -440,6 +441,7 @@ pub macro Copy($item:item) { /* compiler built-in */ }
/// [ub]: ../../reference/behavior-considered-undefined.html
/// [transmute]: ../../std/mem/fn.transmute.html
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "sync_trait")]
#[lang = "sync"]
#[rustc_on_unimplemented(
message="`{Self}` cannot be shared between threads safely",
Expand Down
306 changes: 223 additions & 83 deletions src/librustc/traits/error_reporting.rs

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions src/librustc_errors/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -498,10 +498,20 @@ impl Diagnostic {
self
}

pub fn clear_code(&mut self) -> &mut Self {
self.code = None;
self
}

pub fn get_code(&self) -> Option<DiagnosticId> {
self.code.clone()
}

pub fn set_primary_message<M: Into<String>>(&mut self, msg: M) -> &mut Self {
self.message[0] = (msg.into(), Style::NoStyle);
self
}

pub fn message(&self) -> String {
self.message.iter().map(|i| i.0.as_str()).collect::<String>()
}
Expand Down
1 change: 0 additions & 1 deletion src/libstd/future.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ pub fn from_generator<T: Generator<Yield = ()>>(x: T) -> impl Future<Output = T:
#[doc(hidden)]
#[unstable(feature = "gen_future", issue = "50547")]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(not(test), rustc_diagnostic_item = "gen_future")]
struct GenFuture<T: Generator<Yield = ()>>(T);

// We rely on the fact that async/await futures are immovable in order to create
Expand Down
2 changes: 2 additions & 0 deletions src/libsyntax_pos/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,7 @@ symbols! {
_Self,
self_in_typedefs,
self_struct_ctor,
send_trait,
should_panic,
simd,
simd_extract,
Expand Down Expand Up @@ -697,6 +698,7 @@ symbols! {
sty,
sub_with_overflow,
suggestion,
sync_trait,
target_feature,
target_has_atomic,
target_has_atomic_load_store,
Expand Down
8 changes: 4 additions & 4 deletions src/test/ui/async-await/async-fn-nonsend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ fn assert_send(_: impl Send) {}

pub fn pass_assert() {
assert_send(local_dropped_before_await());
//~^ ERROR `std::rc::Rc<()>` cannot be sent between threads safely
//~^ ERROR future cannot be sent between threads safely
assert_send(non_send_temporary_in_match());
//~^ ERROR `std::rc::Rc<()>` cannot be sent between threads safely
//~^ ERROR future cannot be sent between threads safely
assert_send(non_sync_with_method_call());
//~^ ERROR `dyn std::fmt::Write` cannot be sent between threads safely
//~^^ ERROR `*mut (dyn std::ops::Fn() + 'static)` cannot be shared between threads safely
//~^ ERROR future cannot be sent between threads safely
//~^^ ERROR future cannot be sent between threads safely
}
91 changes: 50 additions & 41 deletions src/test/ui/async-await/async-fn-nonsend.stderr
Original file line number Diff line number Diff line change
@@ -1,79 +1,88 @@
error[E0277]: `std::rc::Rc<()>` cannot be sent between threads safely
error: future cannot be sent between threads safely
--> $DIR/async-fn-nonsend.rs:50:5
|
LL | fn assert_send(_: impl Send) {}
| ----------- ---- required by this bound in `assert_send`
...
LL | assert_send(local_dropped_before_await());
| ^^^^^^^^^^^ `std::rc::Rc<()>` cannot be sent between threads safely
| ^^^^^^^^^^^ future returned by `local_dropped_before_await` is not `Send`
|
= help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>`
= note: required because it appears within the type `impl std::fmt::Debug`
= note: required because it appears within the type `{impl std::fmt::Debug, impl std::future::Future, impl std::future::Future, ()}`
= note: required because it appears within the type `[static generator@$DIR/async-fn-nonsend.rs:21:39: 26:2 {impl std::fmt::Debug, impl std::future::Future, impl std::future::Future, ()}]`
= note: required because it appears within the type `std::future::GenFuture<[static generator@$DIR/async-fn-nonsend.rs:21:39: 26:2 {impl std::fmt::Debug, impl std::future::Future, impl std::future::Future, ()}]>`
= note: required because it appears within the type `impl std::future::Future`
= note: required because it appears within the type `impl std::future::Future`
note: future is not `Send` as this value is used across an await
--> $DIR/async-fn-nonsend.rs:25:5
|
LL | let x = non_send();
| - has type `impl std::fmt::Debug`
LL | drop(x);
LL | fut().await;
| ^^^^^^^^^^^ await occurs here, with `x` maybe used later
LL | }
| - `x` is later dropped here
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This diagnostic is misleading.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, because of the drop(x) that comes before? I suppose that's true, though it'd ordinarily be quite helpful. Perhaps we can just weaken the language to "x may be dropped here"?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think we could adjust the wording to better explain why the drop call isn’t enough to avoid the type being held over the await point.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I consider this a bit of a bug, also -- it'd be nice if we were more precise around particularly this case)


error[E0277]: `std::rc::Rc<()>` cannot be sent between threads safely
error: future cannot be sent between threads safely
--> $DIR/async-fn-nonsend.rs:52:5
|
LL | fn assert_send(_: impl Send) {}
| ----------- ---- required by this bound in `assert_send`
...
LL | assert_send(non_send_temporary_in_match());
| ^^^^^^^^^^^ `std::rc::Rc<()>` cannot be sent between threads safely
| ^^^^^^^^^^^ future returned by `non_send_temporary_in_match` is not `Send`
|
= help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>`
= note: required because it appears within the type `impl std::fmt::Debug`
= note: required because it appears within the type `{impl std::fmt::Debug, std::option::Option<impl std::fmt::Debug>, impl std::future::Future, impl std::future::Future, ()}`
= note: required because it appears within the type `[static generator@$DIR/async-fn-nonsend.rs:28:40: 37:2 {impl std::fmt::Debug, std::option::Option<impl std::fmt::Debug>, impl std::future::Future, impl std::future::Future, ()}]`
= note: required because it appears within the type `std::future::GenFuture<[static generator@$DIR/async-fn-nonsend.rs:28:40: 37:2 {impl std::fmt::Debug, std::option::Option<impl std::fmt::Debug>, impl std::future::Future, impl std::future::Future, ()}]>`
= note: required because it appears within the type `impl std::future::Future`
= note: required because it appears within the type `impl std::future::Future`
note: future is not `Send` as this value is used across an await
--> $DIR/async-fn-nonsend.rs:34:20
|
LL | match Some(non_send()) {
| ---------- has type `impl std::fmt::Debug`
LL | Some(_) => fut().await,
| ^^^^^^^^^^^ await occurs here, with `non_send()` maybe used later
...
LL | }
| - `non_send()` is later dropped here

error[E0277]: `dyn std::fmt::Write` cannot be sent between threads safely
error: future cannot be sent between threads safely
--> $DIR/async-fn-nonsend.rs:54:5
|
LL | fn assert_send(_: impl Send) {}
| ----------- ---- required by this bound in `assert_send`
...
LL | assert_send(non_sync_with_method_call());
| ^^^^^^^^^^^ `dyn std::fmt::Write` cannot be sent between threads safely
| ^^^^^^^^^^^ future returned by `non_sync_with_method_call` is not `Send`
|
= help: the trait `std::marker::Send` is not implemented for `dyn std::fmt::Write`
= note: required because of the requirements on the impl of `std::marker::Send` for `&mut dyn std::fmt::Write`
= note: required because it appears within the type `std::fmt::Formatter<'_>`
= note: required because of the requirements on the impl of `std::marker::Send` for `&mut std::fmt::Formatter<'_>`
= note: required because it appears within the type `for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, bool, impl std::future::Future, impl std::future::Future, ()}`
= note: required because it appears within the type `[static generator@$DIR/async-fn-nonsend.rs:39:38: 45:2 for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, bool, impl std::future::Future, impl std::future::Future, ()}]`
= note: required because it appears within the type `std::future::GenFuture<[static generator@$DIR/async-fn-nonsend.rs:39:38: 45:2 for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, bool, impl std::future::Future, impl std::future::Future, ()}]>`
= note: required because it appears within the type `impl std::future::Future`
= note: required because it appears within the type `impl std::future::Future`
note: future is not `Send` as this value is used across an await
--> $DIR/async-fn-nonsend.rs:43:9
|
LL | let f: &mut std::fmt::Formatter = panic!();
| - has type `&mut std::fmt::Formatter<'_>`
LL | if non_sync().fmt(f).unwrap() == () {
LL | fut().await;
| ^^^^^^^^^^^ await occurs here, with `f` maybe used later
LL | }
LL | }
| - `f` is later dropped here

error[E0277]: `*mut (dyn std::ops::Fn() + 'static)` cannot be shared between threads safely
error: future cannot be sent between threads safely
--> $DIR/async-fn-nonsend.rs:54:5
|
LL | fn assert_send(_: impl Send) {}
| ----------- ---- required by this bound in `assert_send`
...
LL | assert_send(non_sync_with_method_call());
| ^^^^^^^^^^^ `*mut (dyn std::ops::Fn() + 'static)` cannot be shared between threads safely
| ^^^^^^^^^^^ future returned by `non_sync_with_method_call` is not `Send`
|
= help: within `std::fmt::ArgumentV1<'_>`, the trait `std::marker::Sync` is not implemented for `*mut (dyn std::ops::Fn() + 'static)`
= note: required because it appears within the type `std::marker::PhantomData<*mut (dyn std::ops::Fn() + 'static)>`
= note: required because it appears within the type `core::fmt::Void`
= note: required because it appears within the type `&core::fmt::Void`
= note: required because it appears within the type `std::fmt::ArgumentV1<'_>`
= note: required because of the requirements on the impl of `std::marker::Send` for `std::slice::Iter<'_, std::fmt::ArgumentV1<'_>>`
= note: required because it appears within the type `std::fmt::Formatter<'_>`
= note: required because of the requirements on the impl of `std::marker::Send` for `&mut std::fmt::Formatter<'_>`
= note: required because it appears within the type `for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, bool, impl std::future::Future, impl std::future::Future, ()}`
= note: required because it appears within the type `[static generator@$DIR/async-fn-nonsend.rs:39:38: 45:2 for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, bool, impl std::future::Future, impl std::future::Future, ()}]`
= note: required because it appears within the type `std::future::GenFuture<[static generator@$DIR/async-fn-nonsend.rs:39:38: 45:2 for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, bool, impl std::future::Future, impl std::future::Future, ()}]>`
= note: required because it appears within the type `impl std::future::Future`
= note: required because it appears within the type `impl std::future::Future`
note: future is not `Send` as this value is used across an await
--> $DIR/async-fn-nonsend.rs:43:9
|
LL | let f: &mut std::fmt::Formatter = panic!();
| - has type `&mut std::fmt::Formatter<'_>`
LL | if non_sync().fmt(f).unwrap() == () {
LL | fut().await;
| ^^^^^^^^^^^ await occurs here, with `f` maybe used later
LL | }
LL | }
| - `f` is later dropped here

error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0277`.
23 changes: 23 additions & 0 deletions src/test/ui/async-await/issue-64130-1-sync.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#![feature(optin_builtin_traits)]
// edition:2018

// This tests the the specialized async-await-specific error when futures don't implement an
// auto trait (which is specifically Sync) due to some type that was captured.

struct Foo;

impl !Sync for Foo {}

fn is_sync<T: Sync>(t: T) { }

async fn bar() {
let x = Foo;
baz().await;
}

async fn baz() { }

fn main() {
is_sync(bar());
//~^ ERROR future cannot be shared between threads safely
}
22 changes: 22 additions & 0 deletions src/test/ui/async-await/issue-64130-1-sync.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
error: future cannot be shared between threads safely
--> $DIR/issue-64130-1-sync.rs:21:5
|
LL | fn is_sync<T: Sync>(t: T) { }
| ------- ---- required by this bound in `is_sync`
...
LL | is_sync(bar());
| ^^^^^^^ future returned by `bar` is not `Sync`
|
= help: within `impl std::future::Future`, the trait `std::marker::Sync` is not implemented for `Foo`
note: future is not `Sync` as this value is used across an await
--> $DIR/issue-64130-1-sync.rs:15:5
|
LL | let x = Foo;
| - has type `Foo`
LL | baz().await;
| ^^^^^^^^^^^ await occurs here, with `x` maybe used later
LL | }
| - `x` is later dropped here

error: aborting due to previous error

23 changes: 23 additions & 0 deletions src/test/ui/async-await/issue-64130-2-send.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#![feature(optin_builtin_traits)]
// edition:2018

// This tests the the specialized async-await-specific error when futures don't implement an
// auto trait (which is specifically Send) due to some type that was captured.

struct Foo;

impl !Send for Foo {}

fn is_send<T: Send>(t: T) { }

async fn bar() {
let x = Foo;
baz().await;
}

async fn baz() { }

fn main() {
is_send(bar());
//~^ ERROR future cannot be sent between threads safely
}
22 changes: 22 additions & 0 deletions src/test/ui/async-await/issue-64130-2-send.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
error: future cannot be sent between threads safely
--> $DIR/issue-64130-2-send.rs:21:5
|
LL | fn is_send<T: Send>(t: T) { }
| ------- ---- required by this bound in `is_send`
...
LL | is_send(bar());
| ^^^^^^^ future returned by `bar` is not `Send`
|
= help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `Foo`
note: future is not `Send` as this value is used across an await
--> $DIR/issue-64130-2-send.rs:15:5
|
LL | let x = Foo;
| - has type `Foo`
LL | baz().await;
| ^^^^^^^^^^^ await occurs here, with `x` maybe used later
LL | }
| - `x` is later dropped here

error: aborting due to previous error

25 changes: 25 additions & 0 deletions src/test/ui/async-await/issue-64130-3-other.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#![feature(optin_builtin_traits)]
// edition:2018

// This tests the the unspecialized async-await-specific error when futures don't implement an
// auto trait (which is not Send or Sync) due to some type that was captured.

auto trait Qux { }

struct Foo;

impl !Qux for Foo {}

fn is_qux<T: Qux>(t: T) { }

async fn bar() {
let x = Foo;
baz().await;
}

async fn baz() { }

fn main() {
is_qux(bar());
//~^ ERROR the trait bound `Foo: Qux` is not satisfied in `impl std::future::Future`
}
24 changes: 24 additions & 0 deletions src/test/ui/async-await/issue-64130-3-other.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
error[E0277]: the trait bound `Foo: Qux` is not satisfied in `impl std::future::Future`
--> $DIR/issue-64130-3-other.rs:23:5
|
LL | fn is_qux<T: Qux>(t: T) { }
| ------ --- required by this bound in `is_qux`
...
LL | is_qux(bar());
| ^^^^^^ within `impl std::future::Future`, the trait `Qux` is not implemented for `Foo`
|
= help: the following implementations were found:
<Foo as Qux>
note: future does not implement `Qux` as this value is used across an await
--> $DIR/issue-64130-3-other.rs:17:5
|
LL | let x = Foo;
| - has type `Foo`
LL | baz().await;
| ^^^^^^^^^^^ await occurs here, with `x` maybe used later
LL | }
| - `x` is later dropped here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.
28 changes: 28 additions & 0 deletions src/test/ui/async-await/issue-64130-4-async-move.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// edition:2018
use std::any::Any;
use std::future::Future;

struct Client(Box<dyn Any + Send>);

impl Client {
fn status(&self) -> u16 {
200
}
}

async fn get() { }

pub fn foo() -> impl Future + Send {
//~^ ERROR future cannot be sent between threads safely
let client = Client(Box::new(true));
async move {
match client.status() {
200 => {
let _x = get().await;
},
_ => (),
}
}
}

fn main() {}
Loading