Skip to content

1.90 regression: Cycle detected when type checking #146813

@Totodore

Description

@Totodore

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: 4b55fe1...a65b04d
regressed commit: 63f6845

bisected with cargo-bisect-rustc v0.6.10

Host triple: x86_64-unknown-linux-gnu
Reproduce with:

cargo bisect-rustc

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.I-cycleIssue: A query cycle occurred while none was expectedP-highHigh priorityS-has-bisectionStatus: A bisection has been found for this issueS-has-mcveStatus: A Minimal Complete and Verifiable Example has been found for this issueT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-typesRelevant to the types team, which will review and decide on the PR/issue.fixed-by-next-solverFixed by the next-generation trait solver, `-Znext-solver`.regression-from-stable-to-stablePerformance or correctness regression from one stable version to another.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions