Skip to content

Commit

Permalink
Auto merge of #65345 - davidtwco:issue-64130-async-send-sync-error-im…
Browse files Browse the repository at this point in the history
…provements, r=nikomatsakis

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

Part of #64130. Fixes #65667.

This PR improves the errors introduced in #64895 so that they have specialized messages for `Send` and `Sync`.

r? @nikomatsakis
  • Loading branch information
bors committed Dec 11, 2019
2 parents 90b957a + 5cd9f22 commit 27d6f55
Show file tree
Hide file tree
Showing 19 changed files with 502 additions and 147 deletions.
2 changes: 2 additions & 0 deletions src/libcore/marker.rs
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
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
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
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
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
@@ -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

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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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() {}

0 comments on commit 27d6f55

Please sign in to comment.