rust-lang / rust-lang/rust

Exponential compile time increase when nesting async closures in 1.86

Open
#140,004 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

C-bug I-compiletime S-needs-repro T-compiler
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

In a library that is chaining together a series of operations, I have it structured roughly like this:

    pub struct PropChain<F> {
        inertia: Inertia,
        props: F,
    }

pub fn prop<F, P, S>(
        self,
        name: &'static str,
        prop: impl Into<PropBuilder<F, S>>, // <-- notice how this holds `F`
    ) -> PropChain<impl Future<Output = Response>>
    where
        Fut: Future<Output = Response>,
        F: AsyncFnOnce() -> P, // <-- which is an async closure
        P: PropControlFlow,
        S: prop_builder::State,
    {
        let prop = prop.into().build();
        let eval = prop
            .eval(name)
            .req_headers(RequestHeaders::from(&self.inertia.header_map))
            .maybe_component(self.inertia.extension.component())
            .method(&self.inertia.method)
            .build();

        let extension = self.inertia.extension_ptr();
        let props = async move {
            let extension = extension.deref();
            if let ControlFlow::Break(err) = eval.apply(name, extension).await { // <-- which is called here, in the request to apply
                return err.into_response();
            }

            self.props.await
        };

        PropChain {
            inertia: self.inertia,
            props, // <-- which is then returning the "wrapped" future
        }
    }

In this code, it's used by calling .prop over and over again passing an async closure to create a stack of futures that will run all at once the response is to be evaluated. I noticed that for each .prop call added, the compile time increases exponentially until it reaches hours.

Additionally, I can confirm that this is happening during the MIR phase of compilation. Cargo check and rust analyzer (as well as a custom rustc driver being used for type generation) all fly through compiling this code, it's only when cargo build is executed that this issue occurs. I found no significant difference when attempting this during release or debug builds.

I have confirmed that this happens in 1.85 (when async closures were stabilized), 1.86, and nightly.

Meta

rustc --version --verbose:

rustc 1.87.0-nightly (617aad8c2 2025-02-24)
binary: rustc
commit-hash: 617aad8c2e8783f6df8e5d1f8bb1e4bcdc70aa7b
commit-date: 2025-02-24
host: aarch64-apple-darwin
release: 1.87.0-nightly
LLVM version: 20.1.0

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the nested async-closure example in the issue and reproduce it with cargo build, focusing on the MIR phase; cargo check and rust-analyzer do not show the problem. Trace how repeated prop calls wrap futures and compare compile time as nesting increases. Done means the exponential compile-time growth is resolved or substantially reduced.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.