rust-lang / rust-lang/rust

`rustdoc` extreme slowdown with cyclic generic structures

Open
#160,105 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-synthetic-impls C-bug I-compiletime T-rustdoc
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

Consider this MRE:

#![no_std]

extern crate alloc;

use alloc::{
    collections::{BTreeMap, BTreeSet},
    string::String,
    sync::Arc,
};

pub trait Erasable: Sync {}

impl<T> Erasable for T where T: Sync {}

pub trait SessionParameters {
    type Verifier;
}

pub struct Node<T>(pub Arc<T>);

pub struct ComputeScalar<SP: SessionParameters> {
    pub args: BTreeMap<String, ComputeScalarArg<SP>>,
    pub dependencies: Dependency<SP>,
}

pub struct Collect<SP: SessionParameters> {
    pub values: CollectArg<SP>,
    pub dependencies: Dependency<SP>,
}

pub struct ComputeMapping<SP: SessionParameters> {
    pub args: BTreeMap<String, ComputeMappingArg<SP>>,
    pub dependencies: Dependency<SP>,
}

pub struct SerializeAndSignBC<SP: SessionParameters> {
    pub data: Node<ComputeScalar<SP>>,
    pub dependencies: Dependency<SP>,
}

pub struct SerializeAndSignDM<SP: SessionParameters> {
    pub data: DirectMessageArg<SP>,
    pub dependencies: Dependency<SP>,
}

pub struct DeserializeAndCheck<SP: SessionParameters> {
    pub data: Node<Receive<SP>>,
    pub dependencies: Dependency<SP>,
}

pub struct SendDM<SP: SessionParameters> {
    pub data: Node<SerializeAndSignDM<SP>>,
    pub dependencies: Dependency<SP>,
}

pub struct SendBC<SP: SessionParameters> {
    pub data: Node<SerializeAndSignBC<SP>>,
    pub destinations: BTreeSet<SP::Verifier>,
    pub dependencies: Dependency<SP>,
}

pub struct Receive<SP: SessionParameters> {
    pub dependencies: Dependency<SP>,
}

pub struct MergeScalars<SP: SessionParameters> {
    pub left: ComputeScalarArg<SP>,
    pub right: ComputeScalarArg<SP>,
}

pub enum ComputeScalarArg<SP: SessionParameters> {
    ComputeScalar(Node<ComputeScalar<SP>>),
    MergeScalars(Node<MergeScalars<SP>>),
    Collect(Node<Collect<SP>>),
}

pub enum ComputeMappingArg<SP: SessionParameters> {
    ComputeScalar(Node<ComputeScalar<SP>>),
    MergeScalars(Node<MergeScalars<SP>>),
    Collect(Node<Collect<SP>>),
    ComputeMapping(Node<ComputeMapping<SP>>),
    SerializeAndSignBC(Node<SerializeAndSignBC<SP>>),
    SerializeAndSignDM(Node<SerializeAndSignDM<SP>>),
    DeserializeAndCheck(Node<DeserializeAndCheck<SP>>),
}

pub enum CollectArg<SP: SessionParameters> {
    ComputeMapping(Node<ComputeMapping<SP>>),
    SerializeAndSign(Node<SerializeAndSignDM<SP>>),
    DeserializeAndCheck(Node<DeserializeAndCheck<SP>>),
    Send(Node<SendDM<SP>>),
    Receive(Node<Receive<SP>>),
}

pub enum DirectMessageArg<SP: SessionParameters> {
    ComputeScalar(Node<ComputeScalar<SP>>),
    ComputeMapping(Node<ComputeMapping<SP>>),
    DeserializeAndCheck(Node<DeserializeAndCheck<SP>>),
}

pub enum Dependency<SP: SessionParameters> {
    ComputeScalar(Node<ComputeScalar<SP>>),
    Collect(Node<Collect<SP>>),
    MergeScalars(Node<MergeScalars<SP>>),
    SendBC(Node<SendBC<SP>>),
}

After creating a new library crate for it, running cargo doc --no-deps takes ~2 minutes on my machine. Removing the blanket impl impl<T> Erasable for T where T: Sync {} reduces the time to 0.5 seconds.

The problem seems to be caused by a combination of:

  • a public trait with a blanket impl
  • the trait is bound on Sync
  • closed loops of connections of structures to their fields (e.g. ComputeScalar -> Dependency -> ComputeScalar)
  • structures being generic
  • the trait that's the bound for that generic type having an associated type, and that type being used in some structs
  • some fields being BTreeMap/BTreeSet of other structs

Removing any one of these either removes the problem completely, or reduces the documentation generation time dramatically (e.g. from 2 minutes to 10 seconds, which is still disproportionately much, of course).

Meta

rustc --version --verbose:

rustc 1.97.1 (8bab26f4f 2026-07-14)
binary: rustc
commit-hash: 8bab26f4f68e0e26f0bb7960be334d5b520ea452
commit-date: 2026-07-14
host: aarch64-apple-darwin
release: 1.97.1
LLVM version: 22.1.6

Also tried on nightly, with the same results:

rustc 1.99.0-nightly (dc3f85158 2026-07-26)
binary: rustc
commit-hash: dc3f85158a955a87a6e4363af1fbe9cf2d063cce
commit-date: 2026-07-26
host: aarch64-apple-darwin
release: 1.99.0-nightly
LLVM version: 22.1.8

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

Use the supplied MRE in a new library crate and run cargo doc --no-deps to establish the slowdown. Compare the result with the blanket Erasable implementation removed, then trace the rustdoc path responsible for processing the cyclic generic structures. Done means a focused regression test reproduces the case and the excessive documentation-generation time is addressed.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.