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

closures with async blocks have concrete argument lifetimes #59337

Open
Nemo157 opened this issue Mar 21, 2019 · 9 comments
Open

closures with async blocks have concrete argument lifetimes #59337

Nemo157 opened this issue Mar 21, 2019 · 9 comments

Comments

@Nemo157
Copy link
Member

@Nemo157 Nemo157 commented Mar 21, 2019

#![feature(async_await, futures_api)]

use std::future::Future;

trait Foo<'a> {
    type Future: Future<Output = u8> + 'a;

    fn start(self, f: &'a u8) -> Self::Future;
}

impl<'a, Fn, Fut> Foo<'a> for Fn
where
    Fn: FnOnce(&'a u8) -> Fut,
    Fut: Future<Output = u8> + 'a,
{
    type Future = Fut;

    fn start(self, f: &'a u8) -> Self::Future { (self)(f) }
}

fn foo<F>(f: F) where F: for<'a> Foo<'a> {
    let bar = 5;
    f.start(&bar);
}

fn main() {
    foo(async move | f: &u8 | { *f });

    foo({ async fn baz(f: &u8) -> u8 { *f } baz });
}

(playground) currently errors with

error: implementation of `Foo` is not general enough
  --> src/main.rs:27:5
   |
27 |     foo(async move | f: &u8 | { *f });
   |     ^^^
   |
   = note: Due to a where-clause on `foo`,
   = note: `Foo<'1>` would have to be implemented for the type `[closure@src/main.rs:27:9: 27:37]`, for any lifetime `'1`
   = note: but `Foo<'_>` is actually implemented for the type `[closure@src/main.rs:27:9: 27:37]`, for some specific lifetime `'2`

You can see that the async fn correctly satisfies the HRLB required by foo, but the async closure has a concrete lifetime for the argument instead of being for any lifetime.

@Nemo157
Copy link
Member Author

@Nemo157 Nemo157 commented Mar 21, 2019

@rustbot modify labels: A-async-await and T-compiler.

@Nemo157
Copy link
Member Author

@Nemo157 Nemo157 commented Mar 21, 2019

Interestingly, using trait aliases instead of the extra trait above gives a different error message, not sure exactly what it believes is wrong here (could easily be a trait alias issue rather than an async closure issue) (playground)

#![feature(async_await, futures_api, trait_alias, unboxed_closures)]

use std::future::Future;

trait Foo<'a> = FnOnce<(&'a u8,)> where <Self as FnOnce<(&'a u8,)>>::Output: Future<Output = u8> + 'a;

fn foo<F>(f: F) where F: for<'a> Foo<'a> {
    let bar = 5;
    f(&bar);
}

fn main() {
    foo(async move | f: &u8 | { *f });

    foo({ async fn baz(f: &u8) -> u8 { *f } baz });
}
error[E0477]: the type `impl std::future::Future` does not fulfill the required lifetime
  --> src/main.rs:13:5
   |
13 |     foo(async move | f: &u8 | { *f });
   |     ^^^
   |
   = note: type must outlive any other region
@nikomatsakis
Copy link
Contributor

@nikomatsakis nikomatsakis commented Apr 16, 2019

Marking as deferred since async closures are not considered part of the "core functionality" we are trying to stabilize.

@PlasmaPower
Copy link
Contributor

@PlasmaPower PlasmaPower commented Sep 6, 2019

I think this is actually a problem with regular closures. It's just a lot more noticeable with async closures. In the working example, replace:

foo({ async fn baz(f: &u8) -> u8 { *f } baz });

with:

foo({ async fn baz(f: &u8) -> u8 { *f } |f| baz(f) });

This isn't an async closure. It should have the same function signature as baz, taking a borrowed u8 and returning a future. However, it doesn't compile:

error[E0308]: mismatched types
  --> src/main.rs:15:5
   |
15 |     foo({ async fn baz(f: &u8) -> u8 { *f } |f| baz(f) });
   |     ^^^ one type is more general than the other
   |
   = note: expected type `std::ops::FnOnce<(&'a u8,)>`
              found type `std::ops::FnOnce<(&u8,)>`
@Diggsey
Copy link
Contributor

@Diggsey Diggsey commented May 30, 2020

I just ran into this too: in relation to async/await, but in a situation that does not require a nightly compiler:

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

I think the "requires-nightly" label should be removed as this is clearly a broader problem that affects all closures.

@Diggsey
Copy link
Contributor

@Diggsey Diggsey commented Jun 2, 2020

It seems like it might be a problem with the type inference of the closures return type? It ends up inferring the return type to be impl Future instead of impl Future + 'a.

@crlf0710 crlf0710 added the C-bug label Jun 11, 2020
@tmandry tmandry changed the title async closures have concrete argument lifetimes closures returning futures have concrete argument lifetimes Apr 25, 2021
@tmandry tmandry changed the title closures returning futures have concrete argument lifetimes closures with async blocks have concrete argument lifetimes Apr 25, 2021
@tmandry
Copy link
Contributor

@tmandry tmandry commented Apr 26, 2021

Looking at debug logs, the inferred function signature appears to come from from_generator. The argument to that function, the generator, is the type which contains 'a here.

Free functions that look more like from_generator (accepting a generic type instead of a reference type) don't work:

fn test<'a, T>(_x: &'a T) -> impl Future<Output = ()> + 'a {  // works fine
    std::future::ready(())
}

fn test2<'a, T: 'a>(_x: T) -> impl Future<Output = ()> + 'a {  // error
    std::future::ready(())
}

fn test3<T>(_x: T) -> impl Future<Output = ()> {  // error
    std::future::ready(())
}
error: implementation of `Test` is not general enough
  --> src/main.rs:38:5
   |
38 |     do_test(test2);
   |     ^^^^^^^ implementation of `Test` is not general enough
   |
   = note: `fn(&mut i32) -> impl Future {test2::<'_, &mut i32>}` must implement `Test<'0>`, for any lifetime `'0`...
   = note: ...but it actually implements `Test<'1>`, for some specific lifetime `'1`

Playground

Note that this works exactly the same for async fn.

I think this is the underlying problem.

@tmandry
Copy link
Contributor

@tmandry tmandry commented Apr 26, 2021

This doesn't require impl Trait. Even the generic identity function fails.

Minimized

EDIT: Even further minimized, but with a different error message:

fn test1<T>(_: &T) { }
fn test2<T>(_: T) { }

fn main() {
    let _: for<'a> fn(&'a u32) = test1;
    let _: for<'a> fn(&'a u32) = test2; // error: one type is more general than the other
}
@Manishearth
Copy link
Member

@Manishearth Manishearth commented May 5, 2021

This might be related to #84937

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
9 participants