rust-lang / rust-lang/rust

E0275 in 7 s at the default recursion limit becomes a multi-hour hang at `recursion_limit = "4096"`, which is what the E0275 help text suggests

Open
#162,356 8 comments 0 reactions 1 assignee View on GitHub

@hammadk306 is already working on this.

Since Sep 6, 2026.

A-associated-items A-monomorphization I-crash I-hang I-prioritize regression-from-stable-to-stable S-has-mcve T-compiler T-types
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

Summary

A #[derive(Serialize)] on an internally tagged enum whose newtype variants hold Self fails with E0275 in about 7 seconds at the default recursion limit. The diagnostic's help: says to raise recursion_limit. Raising it to 4096 turns the same failure into a trait-solver search that did not finish in 54 minutes locally and hit a 240-minute CI cap twice, at 100 % of one core and flat 375 MB RSS, with no output. The overflow is real either way (the impl genuinely does not exist), so the limit is not the problem; the solver keeps exploring a strictly-deeper candidate at every level until the limit is reached, and 4096 levels is effectively unbounded.

Related: #127315, #113359 (hang instead of overflow), but this one has a twist: the default limit does report E0275, and the diagnostic itself points the user at the setting that produces the hang.

Reproducer

Cargo.toml

[package]
name = "repro"
version = "0.1.0"
edition = "2021"

[dependencies]
serde = { version = "=1.0.228", features = ["derive"] }
serde_json = "=1.0.150"

src/main.rs

// Uncomment the next line to turn the 7 s E0275 into a hang.
// #![recursion_limit = "4096"]
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum Expr {
    Eq { field: String },
    And(Vec<Expr>),
    Not(Box<Expr>),
}

fn main() {
    let _ = serde_json::to_string(&Expr::Eq { field: "a".into() }).unwrap();
}
Actual behaviour

Default limit (cargo build, rustc 1.98.1, 7 s for the crate after deps):

error[E0275]: overflow evaluating the requirement `&mut Vec<u8>: std::io::Write`
  |
  = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`repro`)
  = note: required for `&mut serde_json::Serializer<&mut Vec<u8>>` to implement `_::_serde::Serializer`

With #![recursion_limit = "4096"]: rustc runs at 99.5 % of one core, RSS flat at ~375 MB, no diagnostics, until killed. On this minimal crate: still compiling when killed at a 300 s cap (rustc 1.98.1), against 7 s to E0275 without the attribute. On the originating 16-variant enum (same three self-referential variants plus 13 struct variants holding serde_json::Value): > 54 min locally on rustc 1.94.1 and 1.98.1 (CARGO_INCREMENTAL=0 and --release make no difference), and 240 min 16 s twice on GitHub's ubuntu-latest before the job cap killed it. cargo check passes (the overflow only surfaces when to_string is monomorphised).

Expected behaviour

Either the same E0275 after a bounded amount of work, or the depth counter to notice that each level nests a strictly larger serializer type (TaggedSerializer<TaggedSerializer<…>>) and cut off. At minimum, the help: text should not recommend the setting that turns a 7-second error into a hang when the obligation is provably unsatisfiable.

Why the recursion happens (for context)

Under #[serde(tag = "type")] a newtype variant serialises through serde::__private::ser::TaggedSerializer<S>, so proving Expr: Serialize for serializer S requires proving it for TaggedSerializer<S>, which requires it for TaggedSerializer<TaggedSerializer<S>>, and so on. Changing the three variants to struct variants (And { filters: Vec<Self> }) compiles in 5 s under the default limit and round-trips, which is how the originating project fixed it (ruvnet/ruvector, crates/ruvector-filter/src/expression.rs).

Meta
rustc 1.98.1 (48a229cea 2026-09-01)
host: x86_64-unknown-linux-gnu

Also reproduced on rustc 1.94.1 (e408947bf 2026-03-25). serde 1.0.228 and 1.0.229, serde_json 1.0.150 and 1.0.151.

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.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.