1.90 regression: Cycle detected when type checking
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.1k
- PR merge metrics
- PR metrics pending
Description
I tried this code:
use socketioxide::{SocketIo, handler::ConnectHandler};
fn main() {}
async fn fails() {
let (_svc, io) = SocketIo::new_svc();
#[derive(Debug)]
struct MyError;
impl std::fmt::Display for MyError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "MyError")
}
}
let handler = || async move || Err(MyError);
io.ns(
"/",
{ async || {} }
.with(handler())
.with(handler())
.with(handler()),
);
}
See the following repo for the minimal reproducible example:
https://github.com/Totodore/socketioxide-rust-compiler-1.90-error
I expected to see this happen: Finished dev profile [unoptimized + debuginfo] target(s) in 6.03s
Instead, compilation failed, see the backtrace section below for the complete error.
The compiler error only happens when switching to v1.90. It comes from this CI and I can also reproduce it on my PC:
https://github.com/Totodore/socketioxide/actions/runs/17881639090/job/50850045405#step:5:267
Meta
- The bug also happens in rust nightly 1.92
- Here is a minimal example, the issue seems to comes from the complex traits bounds used in socketioxide, therefore I did not succeed to replicate it without the socketioxide lib.
rustc --version --verbose:
rustc 1.90.0 (1159e78c4 2025-09-14)
binary: rustc
commit-hash: 1159e78c4747b02ef996e55082b704c09b970588
commit-date: 2025-09-14
host: x86_64-unknown-linux-gnu
release: 1.90.0
LLVM version: 20.1.8
Backtrace
error[E0391]: cycle detected when type-checking `fails`
--> src/main.rs:5:1
|
5 | async fn fails() {
| ^^^^^^^^^^^^^^^^
|
= note: ...which requires computing whether `<<socketioxide::handler::connect::ConnectHandler::with::{opaque#0} as socketioxide::handler::connect::ConnectHandler<socketioxide::adapter::LocalAdapter, (socketioxide::handler::connect::private::Async,)>>::with::{anon_assoc#0}<{async closure@src/main.rs:17:22: 17:35}, (socketioxide::handler::connect::private::Async,)> as socketioxide::handler::connect::ConnectHandler<socketioxide::adapter::LocalAdapter, (socketioxide::handler::connect::private::Async,)>>::with::{anon_assoc#0}<{async closure@src/main.rs:17:22: 17:35}, (socketioxide::handler::connect::private::Async,)>` is `Copy`...
= note: ...which requires evaluating trait selection obligation `<<socketioxide::handler::connect::ConnectHandler::with::{opaque#0} as socketioxide::handler::connect::ConnectHandler<socketioxide::adapter::LocalAdapter, (socketioxide::handler::connect::private::Async,)>>::with::{anon_assoc#0}<{async closure@src/main.rs:17:22: 17:35}, (socketioxide::handler::connect::private::Async,)> as socketioxide::handler::connect::ConnectHandler<socketioxide::adapter::LocalAdapter, (socketioxide::handler::connect::private::Async,)>>::with::{anon_assoc#0}<{async closure@src/main.rs:17:22: 17:35}, (socketioxide::handler::connect::private::Async,)>: core::marker::Copy`...
note: ...which requires looking up the hidden types stored across await points in a coroutine...
--> src/main.rs:17:36
|
17 | let handler = || async move || Err(MyError);
| ^^^^^^^^^^^^
note: ...which requires coroutine witness types for `fails::{closure#0}::{closure#0}::{closure#0}::{closure#0}`...
--> src/main.rs:17:36
|
17 | let handler = || async move || Err(MyError);
| ^^^^^^^^^^^^
note: ...which requires promoting constants in MIR for `fails::{closure#0}::{closure#0}::{closure#0}::{closure#0}`...
--> src/main.rs:17:36
|
17 | let handler = || async move || Err(MyError);
| ^^^^^^^^^^^^
note: ...which requires checking if `fails::{closure#0}::{closure#0}::{closure#0}::{closure#0}` contains FFI-unwind calls...
--> src/main.rs:17:36
|
17 | let handler = || async move || Err(MyError);
| ^^^^^^^^^^^^
note: ...which requires building MIR for `fails::{closure#0}::{closure#0}::{closure#0}::{closure#0}`...
--> src/main.rs:17:36
|
17 | let handler = || async move || Err(MyError);
| ^^^^^^^^^^^^
note: ...which requires match-checking `fails::{closure#0}::{closure#0}::{closure#0}::{closure#0}`...
--> src/main.rs:17:36
|
17 | let handler = || async move || Err(MyError);
| ^^^^^^^^^^^^
note: ...which requires type-checking `fails::{closure#0}::{closure#0}::{closure#0}::{closure#0}`...
--> src/main.rs:17:36
|
17 | let handler = || async move || Err(MyError);
| ^^^^^^^^^^^^
= note: ...which again requires type-checking `fails`, completing the cycle
note: cycle used when match-checking `fails`
--> src/main.rs:5:1
|
5 | async fn fails() {
| ^^^^^^^^^^^^^^^^
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
For more information about this error, try `rustc --explain E0391`.
error: could not compile `socketioxide-compile-test-error` (bin "socketioxide-compile-test-error") due to 1 previous error
Side note
It seems that it is related to the Display bound of MyError. Also it is really weird because this bugs happens after three .with() chaining.
Bisection results
searched nightlies: from nightly-2025-08-01 to nightly-2025-09-20
regressed nightly: nightly-2025-08-03
searched commit range: https://github.com/rust-lang/rust/compare/4b55fe199cfe9c710555a5af7f2a49491ad38254...a65b04d7c9a1eaf02b62667da1c203c8a07976d9
regressed commit: https://github.com/rust-lang/rust/commit/63f6845e570305a92eaf855897768617366164d6
bisected with cargo-bisect-rustc v0.6.10
Host triple: x86_64-unknown-linux-gnu
Reproduce with:
cargo bisect-rustc
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the minimal reproducer's src/main.rs and use cargo bisect-rustc to confirm the regression around commit 63f6845e570305a92eaf855897768617366164d6. Investigate the compiler changes in that commit related to the reported type-checking cycle, then verify that the reproducer builds successfully without E0391 on the fixed compiler.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100