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
Labels
A-async-await Area: Async & Await AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@Nemo157
Copy link
Member

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

(updated playground (with 2021 ed)) 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 commented Mar 21, 2019

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

@rustbot rustbot added A-async-await Area: Async & Await T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Mar 21, 2019
@Nemo157
Copy link
Member Author

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 nikomatsakis added the AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. label Apr 16, 2019
@nikomatsakis
Copy link
Contributor

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

@Centril Centril added F-async_closure `#![feature(async_closure)]` requires-nightly This issue requires a nightly compiler in some way. labels Jul 28, 2019
@PlasmaPower
Copy link
Contributor

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 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 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 Category: This is a bug. label Jun 11, 2020
@tmandry tmandry removed F-async_closure `#![feature(async_closure)]` requires-nightly This issue requires a nightly compiler in some way. labels Apr 25, 2021
@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
Member

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
Member

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

This might be related to #84937

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 AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

9 participants