Skip to content

Nested async closures result in exponential compilation time increase #83031

Open
@blazzy

Description

@blazzy

I've noticed exponential increases in compilation times as I nest async closures within each other.

I tried this code:

macro_rules! compose_middleware_inner {
  ( $route:ident, $first:ident, $second:ident, $($tail:ident), +) => {
    $first(|| async {
        compose_middleware_inner!($route, $second, $($tail),+)
    }).await
  };
  ( $route: ident, $first:ident, $second:ident ) => {
    $first(|| async move { $second($route).await }).await
  };
}

macro_rules! compose_middleware {
    ( $name:ident, $($tail:ident), +) => {
        pub async fn $name<N, Fut>(route: N)
        where
            N: FnOnce() -> Fut,
            Fut: std::future::Future<Output = ()>,
        {
            compose_middleware_inner!(route, $($tail),+)
        }
    }
}

async fn log<N, Fut>(next: N)
where
    N: FnOnce() -> Fut,
    Fut: std::future::Future<Output = ()>,
{
    println!("log start");
    next().await;
    println!("log end");
}

compose_middleware!(
    my_middleware, log, log, log, log, log, log, log, log, log, log, log, log, log
);

That compose_middleware! macro invocation generates a function that looks something like this:

pub async fn my_middleware<N, Fut>(route: N)
where
    N: FnOnce() -> Fut,
    Fut: Future<Output = ()>,
{
    log(|| async { log(|| async { log(|| async move { log(route).await }).await }).await }).await
}

I expected to see this happen: I expected this code to build within seconds

Instead, this happened: At around 3 levels it takes less than a second to build. At around 9 levels it takes around a minute to build. At around 14 levels it takes around 10 minutes to build.

Meta

I've experienced this issue on nightly, stable, and beta. And I've sampled random nightly versions going back as far as 1.39 and still witnessed the problem. There was a good bit of variability in build times, but it generally seemed to increase exponentially.

I threw this little repo up to test the issue: https://github.com/blazzy/slow-rust-async/blob/master/src/lib.rs

I thought it might be related to this issue #72408 with nested closures. Or this issue #75992 with levels of async, but those look to be resolved.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-async-awaitArea: Async & AwaitA-closuresArea: Closures (`|…| { … }`)AsyncAwait-TriagedAsync-await issues that have been triaged during a working group meeting.C-bugCategory: This is a bug.I-compiletimeIssue: Problems and improvements with respect to compile times.P-mediumMedium priorityT-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