Skip to content

async fn doesn't capture lifetimes of type parameters if other lifetimes are present #55324

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

Closed
cramertj opened this issue Oct 24, 2018 · 1 comment · Fixed by #59286
Closed
Assignees
Labels
A-async-await Area: Async & Await A-lifetimes Area: Lifetimes / regions AsyncAwait-Polish Async-await issues that are part of the "polish" area

Comments

@cramertj
Copy link
Member

This doesn't compile:

async fn foo<F: Future<Output = i32>>(x: &i32, future: F) -> i32 {
    let y = await!(future);
    *x + y
}
error[E0311]: the parameter type `F` may not live long enough
 --> src/lib.rs:5:62
  |
5 | async fn foo<F: Future<Output = i32>>(x: &i32, future: F) -> i32 {
  |                                                              ^^^
  |
  = help: consider adding an explicit lifetime bound for `F`
note: the parameter type `F` must be valid for the anonymous lifetime #1 defined on the function body at 5:1...
 --> src/lib.rs:5:1
  |
5 | / async fn foo<F: Future<Output = i32>>(x: &i32, future: F) -> i32 {
6 | |     let y = await!(future);
7 | |     *x + y
8 | | }
  | |_^
note: ...so that the type `impl std::future::Future` will meet its required lifetime bounds
 --> src/lib.rs:5:62
  |
5 | async fn foo<F: Future<Output = i32>>(x: &i32, future: F) -> i32 {
  |                                                              ^^^

But it does compile if you replace the &i32 with an i32. This is because async fn foo<T>() -> R desugars to -> impl Future<Output = R>, but async fn foo<T>(x: &i32) -> R desugars to -> impl Future<Output = R> + '_. Since T doesn't outlive the elided lifetime, it fails to compile. We need instead to capture the minimum lifetime of T and '_. Note that this is similar to the issue where async fn cannot have multiple different named lifetimes because there's not a way to express the desugared "minimum of all lifetimes" in the -> impl Trait return type.

cc @withoutboats who originally reported this on discord.

@estebank estebank added A-lifetimes Area: Lifetimes / regions A-async-await Area: Async & Await labels Oct 24, 2018
@cramertj cramertj changed the title async fn doesn't capture lifetimes of type parameters other lifetimes are present async fn doesn't capture lifetimes of type parameters if other lifetimes are present Feb 26, 2019
@nikomatsakis
Copy link
Contributor

Tagging as blocking -- this issue ought to be resolved before async-await can be stabilized, because it is generally surprising and kind of a bug. However, we expect it to get fixed by fixing #56238 (see that issue for notes as to why).

@nikomatsakis nikomatsakis added the AsyncAwait-Polish Async-await issues that are part of the "polish" area label Mar 5, 2019
bors added a commit that referenced this issue Apr 2, 2019
Refactor async fn return type lowering

async fn now lowers directly to an existential type declaration
rather than reusing the `impl Trait` return type lowering.

As part of this, it lowers all argument-position elided lifetimes
using the in-band-lifetimes machinery, creating fresh parameter
names for each of them, using each lifetime parameter as a generic
argument to the generated existential type.

This doesn't currently successfully allow multiple
argument-position elided lifetimes since `existential type`
doesn't yet support multiple lifetimes where neither outlive
the other:
```rust
existential type Foo<'a, 'b>:; // error: ambiguous lifetime bound in `impl Trait`
fn foo<'a, 'b>(_: &'a u8, _: &'b u8) -> Foo<'a, 'b> { () }
```

This requires a separate fix.

Fix #59001
Fix #58885
Fix #55324
Fix #54974
Progress on #56238

r? @nikomatsakis
Centril added a commit to Centril/rust that referenced this issue Apr 2, 2019
Refactor async fn return type lowering

async fn now lowers directly to an existential type declaration
rather than reusing the `impl Trait` return type lowering.

As part of this, it lowers all argument-position elided lifetimes
using the in-band-lifetimes machinery, creating fresh parameter
names for each of them, using each lifetime parameter as a generic
argument to the generated existential type.

This doesn't currently successfully allow multiple
argument-position elided lifetimes since `existential type`
doesn't yet support multiple lifetimes where neither outlive
the other:
```rust
existential type Foo<'a, 'b>:; // error: ambiguous lifetime bound in `impl Trait`
fn foo<'a, 'b>(_: &'a u8, _: &'b u8) -> Foo<'a, 'b> { () }
```

This requires a separate fix.

Fix rust-lang#59001
Fix rust-lang#58885
Fix rust-lang#55324
Fix rust-lang#54974
Progress on rust-lang#56238

r? @nikomatsakis
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-lifetimes Area: Lifetimes / regions AsyncAwait-Polish Async-await issues that are part of the "polish" area
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants