Skip to content

GAT Self: 'a bounds break some async blocks with impl Trait #92096

Open

Description

@cairijun

As required by #87479, Self: 'a must be explicitly added for GATs with self lifetimes. But it seems such a bound breaks async blocks with impl Trait sometimes.

Given a trait with a GAT:

trait Client {
    type Connecting<'a>: Future + Send
    where
        Self: 'a;

    fn connect(&'_ self) -> Self::Connecting<'_>;
}

Awaiting on Client::connect() in an async block results in a confusing lifeime error on impl ...:

fn call_connect<C>(c: &'_ C) -> impl '_ + Future + Send  // removing +Send will compile
where
    C: Client + Send + Sync,
{
    // c.connect().inspect(|_| println!("this compiles"))
    async move {
        println!("this does not compile");
        c.connect().await
    }
}
error[E0311]: the parameter type `C` may not live long enough
  --> src/lib.rs:19:33
   |
19 | fn call_connect<C>(c: &'_ C) -> impl '_ + Future + Send  // removing +Send will compile
   |                 -               ^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `C` will meet its required lifetime bounds
   |                 |
   |                 help: consider adding an explicit lifetime bound...: `C: 'a`

It also breaks a generic impl on the trait with type_alias_impl_trait:

struct ClientWrapper<T>(T);

impl<T> Client for ClientWrapper<T>
where
    T: Client + Send + Sync,
{
    type Connecting<'a>
    where
        T: 'a,
    = impl Future + Send; // removing +Send here & in the trait will compile

    fn connect(&'_ self) -> Self::Connecting<'_> {
        // self.0.connect()  // <- this compiles
        // self.0.connect().inspect(|_| println!("this compiles"))
        async move {
            println!("this does not compile");
            self.0.connect().await
        }
    }
}
error[E0311]: the parameter type `T` may not live long enough
  --> src/lib.rs:42:29
   |
33 | impl<T> Client for ClientWrapper<T>
   |      - help: consider adding an explicit lifetime bound...: `T: 'a`
...
42 |     fn connect(&'_ self) -> Self::Connecting<'_> {
   |                             ^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds

Full code on the playground: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=15714304b9b5efab401efad8c2e84887

Meta

rustc --version --verbose:

rustc 1.59.0-nightly (7abab1efb 2021-12-17)
binary: rustc
commit-hash: 7abab1efb21617ba6845fa86328dffa16cfcf1dc
commit-date: 2021-12-17
host: x86_64-unknown-linux-gnu
release: 1.59.0-nightly
LLVM version: 13.0.0

Activity

  1. jackh726 commented on Dec 19, 2021

    @jackh726
    Member

    It's quite concerning to me that just adding the extra Send bound causes a compilation error (particularly because Send doesn't have lifetimes on it). Will have to look into it.

  2. jackh726 commented on Jan 10, 2022

    @jackh726
    Member

    So, I think this is ending up into the same problem that causes us trouble with iterator adapters with GATs: we have late bound vars, which get turned into placeholders, and we end up required that for<'r> C: 'r. I don't think we can solve this easily.

    For a bit more detail on this issue: Specifically here, we end up with a generator witness with a type for<'r, 's> { .., <C as Client>::Connecting<ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) })> }, that we have to prove is Send. So, we have to prove that that projection is Send. We use the type Connecting<'a>: Future + Send where Self: 'a definition, but this requires us to that for<'s> C: 's.

  3. jackh726 commented on Feb 7, 2022

    @jackh726
    Member

    GATs issue triage: not blocking. This isn't a design concern for the Self: 'a lint; instead, it's a borrow checker limitation. Fixing it won't be backwards-incompatible.

  4. nikomatsakis commented on Apr 13, 2022

    @nikomatsakis
    Contributor

    Is this a dup of #71723 ? I guess the error looks different.

  5. jackh726 commented on Apr 13, 2022

    @jackh726
    Member

    I think this is different. This is caused (once again) by normalizing under a binder, creating an obligation with a placeholder region. That other issue looks like to be caused by a simple type mismatch.

  6. added a commit that references this issue on Sep 13, 2022
  7. added a commit that references this issue on Jan 24, 2023
  8. added
    A-GATsArea: Generic associated types (GATs)
    and removed
    C-bugCategory: This is a bug.
    on Sep 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-GATsArea: Generic associated types (GATs)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions