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

Wrong Send constraint when using a trait with lifetime + associated type in async #60658

Open
Tracked by #110338
carllerche opened this issue May 9, 2019 · 13 comments
Open
Tracked by #110338
Labels
A-async-await Area: Async & Await A-diagnostics Area: Messages for errors, warnings, and lints AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. C-enhancement Category: An issue proposing an enhancement or a PR with one. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@carllerche
Copy link
Member

carllerche commented May 9, 2019

Update: See #60658 (comment) for the latest reproducer

Minimal:

#![feature(async_await, await_macro)]

use std::future::Future;

pub trait Foo<'a> {
    type Future: Future<Output = ()>;
    
    fn foo() -> Self::Future;
}

struct MyType<T>;

impl<'a, T> Foo<'a> for MyType<T>
where
    T: Foo<'a>,
    T::Future: Send,
{
    type Future = Box<Future<Output = ()> + Send>;
    
    fn foo() -> Self::Future {
        Box::new(async move {
            await!(T::foo())
        })
    }
}

fn main() {
}

Out:

   Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
  --> src/main.rs:21:9
   |
21 | /         Box::new(async move {
22 | |             await!(T::foo())
23 | |         })
   | |__________^ one type is more general than the other
   |
   = note: expected type `std::marker::Send`
              found type `std::marker::Send`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
error: Could not compile `playground`.

To learn more, run the command again with --verbose.

Compiler version: rustc 1.36.0-nightly (2019-05-07)

@Centril Centril added the A-async-await Area: Async & Await label May 9, 2019
@Nemo157
Copy link
Member

Nemo157 commented May 16, 2019

Expanding the constraint to

for<'b> <T as Foo<'b>>::Future: Send

gets past this error. I'm not sure why it needs this more general constraint though.

reproduction directly with generators

@jonas-schievink jonas-schievink added A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels May 17, 2019
@nikomatsakis
Copy link
Contributor

I suspect one can make a failure of this with impl trait alone as well, though I've not tried. I'm not sure what exactly the problem is.

@nikomatsakis nikomatsakis added the AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. label Jun 4, 2019
@nikomatsakis
Copy link
Contributor

Deferring -- not blocking stabilization. Would be good to investigate though.

@cramertj
Copy link
Member

@dtolnay
Copy link
Member

dtolnay commented Sep 18, 2019

I hit this in dtolnay/async-trait#34 and minimized to a quite similar looking minimal repro but with the associated type in argument position rather than return position.

I believe this code should compile as written, but right now I am totally stuck on what missing bound is being expected. Is there anything similar to @Nemo157's hrtb that would unblock this one?

use std::future::Future;

pub trait Trait<'a> {
    type Assoc: Send + 'static;

    fn f(x: Self::Assoc) -> Box<dyn Future<Output = ()> + Send>
    where
        'a: 'static,
        Self: Sized + 'static,
    {
        Box::new(f::<Self>(x))
    }
}

async fn f<T: Trait<'static>>(_x: T::Assoc) {
    async {}.await
}
error[E0308]: mismatched types
  --> src/main.rs:11:9
   |
11 |         Box::new(f::<Self>(x))
   |         ^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected type `std::marker::Send`
              found type `std::marker::Send`

@dtolnay dtolnay changed the title async / await + Box<Future> + Send does not compile Wrong Send constraint when using a trait with lifetime + associated type in async Sep 18, 2019
@jebrosen
Copy link
Contributor

I keep running into this both with and without async-trait while trying to implement Future-returning traits in Rocket, which makes heavy use of associated types. In particular I'm currently trying to do this: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=034908e77e7fca31dfbc73fb57c2f3e5 (compare to the version with Option that does not have an associated type).

If I or someone else were to try and investigate the cause, is there any good starting point?

@chaaz
Copy link

chaaz commented Nov 19, 2019

You can get to this error via the use of try_select, try_join, pin_mut and boxed: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0233531e1b7801aca03b4564742d5218. The error given makes it really difficult to figure out what the underlying problem is, and/or what workaround can be done to avoid it. :(

edit: an even more straightforward example: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=3e0b26047b48a0c62db94939556d4538

@jebrosen
Copy link
Contributor

@stephaneyfx pointed out a workaround (using FutureExt::map) that appears to work in my case:

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=51593d4cd22b7dc0a21d1d7c444d9210

I don't think this helps async_trait, but having this to compare to might help figure out the underlying cause.

@estebank
Copy link
Contributor

Triage, no change in the output.

@Ploppz
Copy link

Ploppz commented Feb 28, 2020

I don't know if it's helpful or to what degree it's related, but I get "one type is more general than the other" error that I fail to understand here: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=14c3f1f5a60a8b36a52ab55776ba241e
It can be fixed if I use Box::pin instead of .boxed(), which makes me think it's a bug.

@benschulz
Copy link
Contributor

I fail to do the mental gymnastics required to be sure, but this might be a dupe of #64552. That the HRTB works lines up well with Aaron1011's analysis over there.

@estebank
Copy link
Contributor

Current output:

error[E0277]: `(dyn Future<Output = ()> + Send + 'static)` cannot be unpinned
  --> src/main.rs:16:5
   |
16 |     type Future = Box<dyn Future<Output = ()> + Send>;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Unpin` is not implemented for `(dyn Future<Output = ()> + Send + 'static)`
   |
   = note: consider using `Box::pin`
   = note: required because of the requirements on the impl of `Future` for `Box<(dyn Future<Output = ()> + Send + 'static)>`
note: required by a bound in `Foo::Future`
  --> src/main.rs:4:25
   |
4  |     type Future: Future<Output = ()>;
   |                         ^^^^^^^^^^^ required by this bound in `Foo::Future`

error: implementation of `Send` is not general enough
  --> src/main.rs:19:9
   |
19 | /         Box::new(async move {
20 | |             T::foo().await
21 | |         })
   | |__________^ implementation of `Send` is not general enough
   |
   = note: `<T as Foo<'0>>::Future` must implement `Send`, for any lifetime `'0`...
   = note: ...but `Send` is actually implemented for the type `<T as Foo<'a>>::Future`

@tmandry
Copy link
Member

tmandry commented Apr 14, 2023

Updated reproducer: (playground)

use core::pin::Pin;
use std::future::Future;

pub trait Foo<'a> {
    type Future: Future<Output = ()>;
    
    fn foo() -> Self::Future;
}

struct MyType<T>(T);

impl<'a, T> Foo<'a> for MyType<T>
where
    T: Foo<'a>,
    T::Future: Send,
{
    type Future = Pin<Box<dyn Future<Output = ()> + Send + 'a>>;
    
    fn foo() -> Self::Future {
        Box::pin(async move {
            T::foo().await
        })
    }
}

produces:

error: lifetime may not live long enough
  --> src/lib.rs:21:9
   |
12 |   impl<'a, T> Foo<'a> for MyType<T>
   |        -- lifetime `'a` defined here
...
21 | /         Box::pin(async move {
22 | |             T::foo().await
23 | |         })
   | |__________^ cast requires that `'a` must outlive `'static`

error: higher-ranked lifetime error
  --> src/lib.rs:21:9
   |
21 | /         Box::pin(async move {
22 | |             T::foo().await
23 | |         })
   | |__________^
   |
   = note: could not prove `Pin<Box<[async block@src/lib.rs:21:18: 23:10]>>: CoerceUnsized<Pin<Box<(dyn Future<Output = ()> + Send + 'b)>>>`

error: could not compile `playground` due to 2 previous errors

as noted in #60658 (comment), replacing T::Future: Send with for<'b> <T as Foo<'b>>::Future: Send makes the error go away.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-async-await Area: Async & Await A-diagnostics Area: Messages for errors, warnings, and lints AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. C-enhancement Category: An issue proposing an enhancement or a PR with one. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
Status: On deck
Development

Successfully merging a pull request may close this issue.