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

Explanation for why future is not Send is wrong #68112

Open
tmandry opened this issue Jan 11, 2020 · 4 comments
Open

Explanation for why future is not Send is wrong #68112

tmandry opened this issue Jan 11, 2020 · 4 comments

Comments

@tmandry
Copy link
Contributor

@tmandry tmandry commented Jan 11, 2020

The following code (playground):

fn main() {
    let send_fut = async {
        let non_send_fut = make_non_send_future();
        let _ = non_send_fut.await;
        ready(0).await;
    };
    require_send(send_fut);
}

Gives an error message stating that non_send_fut can live until the end of the scope, and that's why send_fut is not Send:

error: future cannot be sent between threads safely
  --> src/main.rs:18:5
   |
6  | fn require_send(_: impl Send) {}
   |    ------------         ---- required by this bound in `require_send`
...
18 |     require_send(send_fut);
   |     ^^^^^^^^^^^^ future returned by `main` is not `Send`
   |
   = help: the trait `std::marker::Sync` is not implemented for `std::cell::RefCell<i32>`
note: future is not `Send` as this value is used across an await
  --> src/main.rs:16:9
   |
14 |         let non_send_fut = make_non_send_future();
   |             ------------ has type `impl core::future::future::Future`
15 |         let _ = non_send_fut.await;
16 |         ready(0).await;
   |         ^^^^^^^^^^^^^^ await occurs here, with `non_send_fut` maybe used later
17 |     };
   |     - `non_send_fut` is later dropped here

but it doesn't; it's consumed by value when it gets awaited. The problem is we're awaiting a non-Send future inside of send_fut, which has to be Send.

Also, the text

future returned by `main` is not `Send`

is wrong; main doesn't return anything.

Thanks to @kellerb for the reproducer and @JakeEhrlich for originally reporting this.

@tmandry

This comment has been minimized.

Copy link
Contributor Author

@tmandry tmandry commented Jan 11, 2020

The problem is we're awaiting a non-Send future inside of send_fut, which has to be Send.

Some version of this is roughly the error message I'd want.

@tmandry

This comment has been minimized.

Copy link
Contributor Author

@tmandry tmandry commented Jan 14, 2020

@rustbot modify labels to +AsyncAwait-OnDeck +AsyncAwait-Triaged

@nikomatsakis

This comment has been minimized.

Copy link
Contributor

@nikomatsakis nikomatsakis commented Jan 14, 2020

A few thoughts:

  • I suspect that the code might be incorrectly identifying the await that should be highlighted

If you remove the second await, you get this error (playground):

error: future cannot be sent between threads safely
  --> src/main.rs:17:5
   |
6  | fn require_send(_: impl Send) {}
   |    ------------         ---- required by this bound in `require_send`
...
17 |     require_send(send_fut);
   |     ^^^^^^^^^^^^ future returned by `main` is not `Send`
   |
   = help: the trait `std::marker::Sync` is not implemented for `std::cell::RefCell<i32>`
note: future is not `Send` as this value is used across an await
  --> src/main.rs:15:17
   |
14 |         let non_send_fut = make_non_send_future();
   |             ------------ has type `impl core::future::future::Future`
15 |         let _ = non_send_fut.await;
   |                 ^^^^^^^^^^^^^^^^^^ await occurs here, with `non_send_fut` maybe used later
16 |         //ready(0).await;
17 |     };
   |     - `non_send_fut` is later dropped here

Granted, also wrong, though it might be "correct" in the sense that the compiler thinks that non_send_fut may be used later (because our analysis is not precise).

@csmoe

This comment has been minimized.

Copy link
Member

@csmoe csmoe commented Jan 15, 2020

yes, live-across-yield analysis inside generator should be more precise, a test case from src/test/ui:

#![feature(optin_builtin_traits)]
// edition:2018
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());
}

this snippet will compile if the non-Send Foo's scope is shorten within a block

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
6 participants
You can’t perform that action at this time.