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

Async fn does not compile if lifetime does not appear in bounds (sometimes) #63033

Closed
dtolnay opened this issue Jul 27, 2019 · 46 comments · Fixed by #105300
Closed

Async fn does not compile if lifetime does not appear in bounds (sometimes) #63033

dtolnay opened this issue Jul 27, 2019 · 46 comments · Fixed by #105300
Assignees
Labels
A-async-await Area: Async & Await A-lifetimes Area: lifetime related 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

@dtolnay
Copy link
Member

dtolnay commented Jul 27, 2019

#![feature(async_await)]

use std::marker::PhantomData;

trait Trait {}

async fn f<'a>(reference: &(), marker: PhantomData<dyn Trait>) {}

This fails with:

error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds
 --> src/main.rs:7:64
  |
7 | async fn f<'a>(reference: &(), marker: PhantomData<dyn Trait>) {}
  |                                                                ^
  |
note: hidden type `impl std::future::Future` captures the scope of call-site for function at 7:64
 --> src/main.rs:7:64
  |
7 | async fn f<'a>(reference: &(), marker: PhantomData<dyn Trait>) {}
  |                                                                ^^

I believe this should compile because:

  1. The non-async function with the same signature does compile;

    fn f<'a>(reference: &(), marker: PhantomData<dyn Trait>) {}
  2. Seemingly insignificant tweaks, like adding a wrapper struct, make it compile.

    struct Wrapper(PhantomData<dyn Trait>);
    async fn f<'a>(reference: &(), marker: Wrapper) {}

Mentioning @cramertj who fixed a similar error message in #59001.
Mentioning @nikomatsakis who touched this error message in #49041 and #61775.

rustc 1.38.0-nightly (c43753f 2019-07-26)

@jonas-schievink jonas-schievink added A-async-await Area: Async & Await C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jul 27, 2019
@nikomatsakis nikomatsakis added AsyncAwait-Polish Async-await issues that are part of the "polish" area AsyncAwait-Unclear labels Aug 13, 2019
@nikomatsakis
Copy link
Contributor

nikomatsakis commented Aug 13, 2019

Check-in from WG-async-await meeting: I agree this is a bug -- it doesn't necessarily block stabilization (no fwd compat risk) but would definitely be a good one to fix. Marking as blocking + unclear for the moment and will return to it.

@nikomatsakis
Copy link
Contributor

I did a bit of investigation. The error results because we have an inference variable ('_#3r) that seems to have no constraints, but must be equal to one of the captured regions (e.g., 'a). I haven't 100% figured out where this variable comes from, I imagine it's related to 'a though? In any case, the resolver basically winds up in a situation where it could pick any of the lifetimes but has no reason to prefer one over the other, and hence it reports an error.

@nikomatsakis
Copy link
Contributor

OK, I think I see what's going on. It doesn't actually have much to do with the unused lifetime per se. What happens is this:

  • The type of the generated result is something like Foo<&'2 (), PhantomData<dyn Trait + '3>> -- i.e., it includes the types of the parameters, though with region variables in place of the known regions. Those types must be supertypes of the actual parameter types.
  • We capture the argument marker of type dyn Trait + 'static -- per the subtyping rules, we create the type dyn Trait + '3 for the type of this captured value, with the requirement that 'static: '3 -- as it happens, this is always true, so there is effectively no constraint on '3.
  • We do impose the constraint that '2 and '3 must both be equal to one of the free regions -- in this case, 'a or the anonymous lifetime of reference (which I'll call 'b).

So thus the region solvers get in a state where '3 must be equal to 'a, 'b, or 'static -- but any one of them would be a reasonable choice.

Removing the unused lifetime ('a) makes this an easy choice -- it can pick 'b, which is smaller than 'static. (Per the "least choice" rules we usually use.)

Removing the type and putting it into a struct (Wrapper) removes the lifetime variable altogether, as then we have Foo<&'2 (), Wrapper>, and there are no lifetime parameters involved.

I'm not sure what I think is the best fix. It would be nice for the solver to pick 'static, though it complicates the algorithm. You could certainly imagine a tweak that picks 'static if the value is legal and otherwise looks for a least choice. That might indeed be superior to the current approach, though I'd want to think about the full implications -- in particular, when impl Trait is used in places outside of the fn return type (e.g., in a let statement), picking 'static might lead to unnecessary borrow check errors. (The alternative would be to only look for 'static when there is no least choice; that seems safer.)

(Interestingly, at some point at least I thought that a polonius-like approach sidesteps a lot of the challenges here-- if we had confidence that we would transition that might ease some of my concerns.)

@nikomatsakis
Copy link
Contributor

nikomatsakis commented Aug 14, 2019

Now that I understand better what is happening, I don't feel this has to block stabilization -- in particular, I don't think that there is a forward compatibility risk from us improving the region inference algorithm. I don't think we would choose an algorithm that makes existing code illegal. =)

As such, I'm going to mark this as Deferred.

@nikomatsakis nikomatsakis added AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. and removed AsyncAwait-Polish Async-await issues that are part of the "polish" area AsyncAwait-Unclear labels Aug 14, 2019
@sfackler
Copy link
Member

sfackler commented Oct 10, 2019

Here's another example of the same problem that doesn't have an unused lifetime (AFAIKT):

struct Foo;

impl Foo {
    async fn wat(&self, _: &'static str, _: Bar<'_>) {}
}

struct Bar<'a>(Box<dyn std::fmt::Debug + 'a>);
error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds
 --> src/lib.rs:4:54
  |
4 |     async fn wat(&self, _: &'static str, _: Bar<'_>) {}
  |                                                      ^
  |
note: hidden type `impl std::future::Future` captures the scope of call-site for function at 4:54
 --> src/lib.rs:4:54
  |
4 |     async fn wat(&self, _: &'static str, _: Bar<'_>) {}
  |                                                      ^^

error: aborting due to previous error

Removing any of the three arguments to the function allows it to compile.

@lovasoa
Copy link
Contributor

lovasoa commented Dec 14, 2019

I am also affected by this bug, where having both a reference and a box as parameters to a function causes the cryptic "hidden type for impl Trait captures lifetime that does not appear in bounds" :

trait Trait {}
async fn f<'a>(_a: &(), _b: Box<dyn Trait>) {}

(playground)

Is there a resolution in sight ?

@sfackler
Copy link
Member

'a is unused in that function - if you remove the <'a> it compiles.

@lovasoa
Copy link
Contributor

lovasoa commented Dec 14, 2019

Ok, let me change it to something closer to what I had in my original code:

trait T {}
struct S;
impl S {
    async fn f(_a: &S, _b:&S, _c: Box<dyn T>) {}
}

(playground)

MindFlavor added a commit to MindFlavor/AzureSDKForRust that referenced this issue Dec 23, 2019
@netvl
Copy link
Contributor

netvl commented Dec 25, 2019

I think I found another example of this:

trait T {}

struct L<A = Box<dyn T>> {
    value: A,
}

async fn test1(l: L, s1: &str, s2: &str) {
}

(playground)

This example more or less represents what I met in my code (when I tried to pass a structure with a type parameter to an async function with the default value for this type parameter being a trait object), but it can be simplified by removing the default parameter assignment and passing the Box<dyn T> argument to L explicitly:

trait T {}

struct L<A> {
    value: A,
}

async fn test1(l: L<Box<dyn T>>, s1: &str, s2: &str) {
}

(playground)

Curiously, if you replace async fn ... { ... } with fn ... -> impl Future { async { ... } }, the error goes away:

use std::future::Future;

trait T {}

struct L<A> {
    value: A,
}

fn test1(l: L<Box<dyn T>>, s1: &str, s2: &str) -> impl Future<Output=()> {
    async {
    }
}

(playground)

It is important to note that the test1 function has two additional arguments. If you remove one or both of them, it starts compiling again:

trait T {}

struct L<A> {
    value: A,
}

async fn test2(l: L<Box<dyn T>>, s1: &str) {
}

async fn test3(l: L<Box<dyn T>>) {
}

(playground)

@skeet70
Copy link

skeet70 commented Jan 22, 2020

@netvl 's comment is a minimal reproduction of what I ran into. I had a function async f(a: &CacheA, b: &CacheB, c: &CacheC, d: Arc<dyn Client>). If I remove two of the cache arguments it compiles. As soon as I add one back in it gives me the title error.

@nikomatsakis
Copy link
Contributor

Hmm, it is probably worth revisiting this problem now that stabilization has happened.

facebook-github-bot pushed a commit to facebookarchive/mononoke that referenced this issue Jan 29, 2020
Summary:
walker: migrate to new futures (0.3.1).

* walk.rs is not fully migrated, due to function call to `bounded_traversal_stream`, which uses old futures.

* `scrub::scrub_objects`, `sizing::compression_benefit` and `validate::validate` returns `BoxFuture` instead of being a regular `async` function, due to limitation of rust issue [#63303](rust-lang/rust#63033).

Reviewed By: farnz

Differential Revision: D19536696

fbshipit-source-id: a0df337b86d7b067a44bf3b18834193d3f63f5dc
@swilcox3
Copy link

Any word on this? I'm getting this error as well.

pub async fn publish(
    conn: &mut StateConnection,
    file: &FileID,
    user: &UserID,
    latest: ChangeID,
    mut subscribers: Vec<Box<dyn ChangeSubscriber>>,
) -> Result<(), ObjError> {
    let to_publish = gather_changes(conn, file, latest).await?;
    for sub in subscribers {
        sub.get_changes(file, user, latest, &to_publish).await?;
    }
    Ok(())
}

Changing mut subscribers: Vec<Box<dyn ChangeSubscriber>> to subscribers: &mut Vec<Box<dyn ChangeSubscriber>> fixes the issue. Only problem is that I actually want to move that vector in because I want to tokio::spawn each call to get_changes.

@nikomatsakis
Copy link
Contributor

No progress or major updates. The description here is still accurate as to the root cause, but I think we don't yet know the best fix.

@danieleades
Copy link
Contributor

this is going to become a problem now that #72459 is merged.

It makes it impossible to implement IntoFuture for types with lifetimes, when the future that's returned is unnameable.

consider

pub struct Request<'a> {
    ...
}

impl<'a> Request<'a> {
    async fn send(self) -> Result<(), Error> {
        ...
    }
}

// does not compile
// "cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements"
impl<'a> IntoFuture for Request<'a> {
    type Output = Result<(), Error>;
    type Future = impl Future<Output = Self::Output>;

    fn into_future(self) -> Self::Future {
        self.send()
    }
}

// does not compile
// "hidden type for `impl Trait` captures lifetime that does not appear in bounds"
impl<'a> IntoFuture for Request<'a> {
    type Output = Result<(), Error>;
    type Future = impl Future<Output = Self::Output> + 'a;

    fn into_future(self) -> Self::Future {
        self.send()
    }
}

@AndiDog
Copy link
Contributor

AndiDog commented Aug 17, 2020

Here's another very simple example:

#[allow(dead_code)]
struct SomeStruct<'a> {
    s: &'a str,
}

async fn something_asynchronous(_a: &'static str, _b: &str, _c: &SomeStruct<'_>) {}
error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds
 --> src/main.rs:6:82
  |
6 | async fn something_asynchronous(_a: &'static str, _b: &str, _c: &SomeStruct<'_>) {}
  |                                                                                  ^
  |
note: hidden type `impl std::future::Future` captures lifetime smaller than the function body
 --> src/main.rs:6:82
  |
6 | async fn something_asynchronous(_a: &'static str, _b: &str, _c: &SomeStruct<'_>) {}
  |                                                                                  ^

Removing any of the 3 parameters makes the code compile. Alternatively, removing the static lifetime of _a (→ _a: &str) makes it compile as well.

@mingmwang

This comment has been minimized.

zhuhaow added a commit to zhuhaow/dandelion that referenced this issue Dec 22, 2021
claymcleod added a commit to stjude-rust-labs/ngs that referenced this issue Sep 27, 2022
Previously, the tool simply had a hardcoded set of PRIMARY_CHROMOSOMES that
were hardcoded to the hg38 primary chromosomes. Now, the tool has a supported
set of reference genomes, namely (to start):

* GRCh38NoAlt (from the NCBI)
* hs37d5      (from the 1000 Genomes Project)

These two genomes were selected simply because (a) GRCh38NoAlt is probably the
most popular GRCh38 genome and (b) hs37d5 is the genome used for phase 2 and
phase 3 of the 1000 Genomes project: a fairly popular publicly available
resource and the subject of many QC papers.

Introducing a reference genome into the code required multiple QC facets to be
updated to use this functionality. For each of these, I chose to simply pass
the reference genome to the initialization function for the facet: it's up to
the facet to take what it needs from the reference genome and store it for
later use (as opposed to adding a lifecycle hook injecting it).

Other notable, related changes:

* I include now a check at the beginning of the `qc` command to ensure that the
  sequences in the header of the file match the reference genome the user
  specified on the commmand line. In the future, I also plan to add checks that
  the actual FASTA file matches the specified reference genome (if provided)
  _and_ that the GFF file matches the specified reference genome (if provided).

There were some other changes that are introduced in this changeset that, at
first, don't appear directly related:

* We've now moved away from using `async`/`await` for the `qc` subcommand, as
  there is an obscure bug that doesn't allow two generic lifetimes and one
  static lifetime with an `async` function. Thus, I decided to just move away
  from using `async`/`await` altogether, as I had been considering that
  regardless (we already moved away from using the lazy evaluation facilities
  in noodles). See issues rust-lang/rust#63033 and
  rust-lang/rust#99190 for more details.
* In testing this code, I was running into an error where a record fell outside
  of the valid range of a sequence. This was annoying, so I just decided to fix
  it as part of this changeset. There is no other deep reason why those changes
  are included here.
@JakkuSakura
Copy link

JakkuSakura commented Oct 8, 2022

Encountered this today.
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=7171c029eb79ccf2068ee469dbce73a1

Though the original error was this, which I couldn't reproduce it in playground.

async fn handle_tx(
    signer_info: &CliSignerInfo,
    config: &Config,
    fee_payer: Arc<dyn Signer>,
    no_wait: bool,
    minimum_balance_for_rent_exemption: u64,
    instructions: Vec<Instruction>,
) -> Result<TransactionReturnData> {
    todo!()
}
error[E0700]: hidden type for impl std::future::Future<Output = std::result::Result<TransactionReturnData, ErrReport>> captures lifetime that does not appear in bounds
std::result::Result<TransactionReturnData, ErrReport>> captures lifetime '_#22r
use std::sync::Arc;
trait Signer {}
async fn handle_tx(
    signer_info: &u8,
    config: &u8,
    fee_payer: Arc<dyn Signer>,
) -> Result<(), ()> {
    todo!()
}

Removing any of the parameters the error is gone

rustc 1.66.0-nightly (57f097e 2022-10-01)

Dylan-DPC added a commit to Dylan-DPC/rust that referenced this issue Feb 14, 2023
rework min_choice algorithm of member constraints

See [this comment](rust-lang#105300 (comment)) for the description of the new algorithm.

Fixes rust-lang#63033
Fixes rust-lang#104639

This uses a more general algorithm than rust-lang#89056 that doesn't treat `'static` as a special case. It thus accepts more code. For example:
```rust
async fn test2<'s>(_: &'s u8, _: &'_ &'s u8, _: &'_ &'s u8) {}
```
I claim it's more correct as well because it fixes rust-lang#104639.

cc `@nikomatsakis` `@lqd` `@tmandry` `@eholk` `@chenyukang` `@oli-obk`

r? types
@bors bors closed this as completed in 83f10ea Feb 15, 2023
ananos added a commit to nubificus/kata-containers that referenced this issue May 31, 2023
We are probably hitting this:
rust-lang/rust#63033

Seems like it is worth a try to upgrade to 1.69.0

Signed-off-by: Anastassios Nanos <ananos@nubificus.co.uk>
ananos added a commit to nubificus/kata-containers that referenced this issue Jun 1, 2023
We are probably hitting this:
rust-lang/rust#63033

Seems like it is worth a try to upgrade to 1.69.0

Signed-off-by: Anastassios Nanos <ananos@nubificus.co.uk>
ananos added a commit to nubificus/kata-containers that referenced this issue Jun 1, 2023
We are probably hitting this:
rust-lang/rust#63033

Seems like it is worth a try to upgrade to 1.69.0

Signed-off-by: Anastassios Nanos <ananos@nubificus.co.uk>
ananos added a commit to nubificus/kata-containers that referenced this issue Jun 2, 2023
We are probably hitting this:
rust-lang/rust#63033

Seems like it is worth a try to upgrade to 1.69.0

Signed-off-by: Anastassios Nanos <ananos@nubificus.co.uk>
ananos added a commit to nubificus/kata-containers that referenced this issue Jun 3, 2023
We are probably hitting this:
rust-lang/rust#63033

Seems like it is worth a try to upgrade to 1.69.0

Signed-off-by: Anastassios Nanos <ananos@nubificus.co.uk>
ananos added a commit to nubificus/kata-containers that referenced this issue Jun 6, 2023
We are probably hitting this:
rust-lang/rust#63033

Seems like it is worth a try to upgrade to 1.69.0

Signed-off-by: Anastassios Nanos <ananos@nubificus.co.uk>
ananos added a commit to kata-containers/kata-containers that referenced this issue Jun 6, 2023
We are probably hitting this:
rust-lang/rust#63033

Seems like it is worth a try to upgrade to 1.69.0

Signed-off-by: Anastassios Nanos <ananos@nubificus.co.uk>
demoray pushed a commit to demoray/azure-sdk-for-rust that referenced this issue Sep 21, 2023
The underlying issue was fixed in rust 1.69.0.  Our minimum supported rust is now 1.70.0 (set in azure_core), which means this workaround is no longer needed.

Ref: rust-lang/rust#63033
demoray added a commit to Azure/azure-sdk-for-rust that referenced this issue Sep 22, 2023
The underlying issue was fixed in rust 1.69.0.  Our minimum supported rust is now 1.70.0 (set in azure_core), which means this workaround is no longer needed.

Ref: rust-lang/rust#63033
mayamrinal pushed a commit to mayamrinal/kata-containers that referenced this issue Dec 13, 2023
We are probably hitting this:
rust-lang/rust#63033

Seems like it is worth a try to upgrade to 1.69.0

Signed-off-by: Anastassios Nanos <ananos@nubificus.co.uk>
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: lifetime related 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
Status: Done
wg-async work
In progress (current sprint)