Skip to content

async fn lifetime eliding satisfies HRTB where explicit does not #114947

Open
@Wolvereness

Description

In this code, two generators are defined. Their definitions should be equivalent. The async-fn variant where lifetimes are elided works fine, but the variant where the lifetimes are defined explicitly with an impl-trait return fails to compile against the HRTB.

Playground with more definitions to actually build.

trait AsyncFnOnce<Arg> {}
impl<F, A, B, R: Future> AsyncFnOnce<(A, B, )> for F
    where
        F: FnOnce(A, B) -> R,
{}

pub unsafe trait RemitWithLifetime<T, X> {}
unsafe impl<T, X, F> RemitWithLifetime<T, (X, )> for F
    where
        F: for<'a> AsyncFnOnce<(X, Remit<'a, T>, )>,
{}

impl<T, P> Generator<T, P> {
    pub fn parameterized<'s, G, X>(
        self: Pin<&'s mut Self>,
        _gen: G,
        _parameter: X,
    ) -> GeneratorIterator<'s, T, P>
        where
            // insures fn is not implemented only for 'static
            G: RemitWithLifetime<T, (X,)>,
            // insures P is properly defined, even if it actually has a lifetime
            G: FnOnce(X, Remit<'static, T>) -> P,
    {
        unimplemented!();
    }
}

use std::pin::pin;
fn only_build() {
    let data = String::from("hi");

    /// Implicit elision works
    async fn gen_implicit(data: &str, remit: Remit<'_, usize>) {
        remit.value(data.len()).await;
        remit.value(data.len()).await;
    }
    for item in pin!(Generator::new()).parameterized(gen_implicit, &data) {
        dbg!(item);
    }
    
    /// Does not work, as explicit lifetime definitions fail HRTB.
    fn gen_explicit<'a: 'c, 'b: 'c, 'c>(data: &'a str, remit: Remit<'b, usize>) -> impl std::future::Future<Output=()> + 'c {
        async move {
            remit.value(data.len()).await;
            remit.value(data.len()).await;
        }
    }
    for item in pin!(Generator::new()).parameterized(gen_explicit, &data) {
        dbg!(item);
    }
}

Both gen_implicit and gen_explicit should have been able to be used successfully.

Instead, there is a compiler error:

error: implementation of `FnOnce` is not general enough
  --> src/lib.rs:49:17
   |
49 |     for item in pin!(Generator::new()).parameterized(gen_explicit, &data) {
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
   |
   = note: `fn(&str, Remit<'2, usize>) -> impl Future<Output = ()> + '_ {gen_explicit::<'_, '2, '_>}` must implement `FnOnce<(&str, Remit<'1, usize>)>`, for any lifetime `'1`...
   = note: ...but it actually implements `FnOnce<(&str, Remit<'2, usize>)>`, for some specific lifetime `'2`

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    A-async-awaitArea: Async & AwaitA-higher-rankedArea: Higher-ranked things (e.g., lifetimes, types, trait bounds aka HRTBs)A-lifetimesArea: Lifetimes / regionsAsyncAwait-TriagedAsync-await issues that have been triaged during a working group meeting.C-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions