Skip to content

Regression: cycle detected when coherence checking all impls of trait core::convert::From #156296

Closed as not planned

Description

@nazar-pc

I tried this code:

#![feature(const_convert, const_trait_impl, generic_const_exprs)]
#![expect(incomplete_features, reason = "generic_const_exprs")]

pub mod b {
    pub struct WrapperB([u8; WrapperB::SIZE]);

    impl From<WrapperB> for [u8; WrapperB::SIZE] {
        fn from(value: WrapperB) -> Self {
            <[u8; WrapperB::SIZE]>::from(value.0)
        }
    }

    impl WrapperB {
        pub const SIZE: usize = 32;
    }
}
pub mod a {
    pub struct WrapperA([u8; WrapperA::SIZE]);

    impl From<[u8; WrapperA::SIZE]> for WrapperA {
        fn from(value: [u8; WrapperA::SIZE]) -> Self {
            WrapperA(value)
        }
    }

    impl WrapperA {
        pub const SIZE: usize = usize::from(1u8);
    }
}

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=bdf3caab9574ab358c34f36083e8d4f9

I expected to see this happen: Compiles without errors

Instead, this happened:

Details
error[E0391]: cycle detected when coherence checking all impls of trait `core::convert::From`
   --> /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/convert/mod.rs:588:1
    |
588 | pub const trait From<T>: Sized {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
note: ...which requires building specialization graph of trait `core::convert::From`...
   --> src/lib.rs:20:20
    |
 20 |     impl From<[u8; WrapperA::SIZE]> for WrapperA {
    |                    ^^^^^^^^^^^^^^
note: ...which requires evaluating type-level constant...
   --> src/lib.rs:20:20
    |
 20 |     impl From<[u8; WrapperA::SIZE]> for WrapperA {
    |                    ^^^^^^^^^^^^^^
note: ...which requires const-evaluating + checking `a::<impl at src/lib.rs:20:5: 20:49>::{constant#0}`...
   --> src/lib.rs:20:20
    |
 20 |     impl From<[u8; WrapperA::SIZE]> for WrapperA {
    |                    ^^^^^^^^^^^^^^
note: ...which requires checking if `a::<impl at src/lib.rs:20:5: 20:49>::{constant#0}` is a trivial const...
   --> src/lib.rs:20:20
    |
 20 |     impl From<[u8; WrapperA::SIZE]> for WrapperA {
    |                    ^^^^^^^^^^^^^^
note: ...which requires building MIR for `a::<impl at src/lib.rs:20:5: 20:49>::{constant#0}`...
   --> src/lib.rs:20:20
    |
 20 |     impl From<[u8; WrapperA::SIZE]> for WrapperA {
    |                    ^^^^^^^^^^^^^^
note: ...which requires checking if `a::<impl at src/lib.rs:26:5: 26:18>::SIZE` is a trivial const...
   --> src/lib.rs:27:9
    |
 27 |         pub const SIZE: usize = usize::from(1);
    |         ^^^^^^^^^^^^^^^^^^^^^
note: ...which requires building MIR for `a::<impl at src/lib.rs:26:5: 26:18>::SIZE`...
   --> src/lib.rs:27:9
    |
 27 |         pub const SIZE: usize = usize::from(1);
    |         ^^^^^^^^^^^^^^^^^^^^^
note: ...which requires match-checking `a::<impl at src/lib.rs:26:5: 26:18>::SIZE`...
   --> src/lib.rs:27:9
    |
 27 |         pub const SIZE: usize = usize::from(1);
    |         ^^^^^^^^^^^^^^^^^^^^^
note: ...which requires type-checking `a::<impl at src/lib.rs:26:5: 26:18>::SIZE`...
   --> src/lib.rs:27:9
    |
 27 |         pub const SIZE: usize = usize::from(1);
    |         ^^^^^^^^^^^^^^^^^^^^^
    = note: ...which again requires coherence checking all impls of trait `core::convert::From`, completing the cycle
note: cycle used when checking that `b::<impl at src/lib.rs:7:5: 7:49>` is well-formed
   --> src/lib.rs:7:5
    |
  7 |     impl From<WrapperB> for [u8; WrapperB::SIZE] {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = 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

Even more confusingly, rearranging modules a and b results in a different error, but it also mentions cycles:

Details
error[E0391]: cycle detected when checking if `a::<impl at src/lib.rs:13:5: 13:18>::SIZE` is a trivial const
   --> src/lib.rs:14:9
    |
 14 |         pub const SIZE: usize = usize::from(1u8);
    |         ^^^^^^^^^^^^^^^^^^^^^
    |
note: ...which requires building MIR for `a::<impl at src/lib.rs:13:5: 13:18>::SIZE`...
   --> src/lib.rs:14:9
    |
 14 |         pub const SIZE: usize = usize::from(1u8);
    |         ^^^^^^^^^^^^^^^^^^^^^
note: ...which requires match-checking `a::<impl at src/lib.rs:13:5: 13:18>::SIZE`...
   --> src/lib.rs:14:9
    |
 14 |         pub const SIZE: usize = usize::from(1u8);
    |         ^^^^^^^^^^^^^^^^^^^^^
note: ...which requires type-checking `a::<impl at src/lib.rs:13:5: 13:18>::SIZE`...
   --> src/lib.rs:14:9
    |
 14 |         pub const SIZE: usize = usize::from(1u8);
    |         ^^^^^^^^^^^^^^^^^^^^^
note: ...which requires coherence checking all impls of trait `core::convert::From`...
   --> /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/convert/mod.rs:588:1
    |
588 | pub const trait From<T>: Sized {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires building specialization graph of trait `core::convert::From`...
   --> src/lib.rs:7:20
    |
  7 |     impl From<[u8; WrapperA::SIZE]> for WrapperA {
    |                    ^^^^^^^^^^^^^^
note: ...which requires evaluating type-level constant...
   --> src/lib.rs:7:20
    |
  7 |     impl From<[u8; WrapperA::SIZE]> for WrapperA {
    |                    ^^^^^^^^^^^^^^
note: ...which requires const-evaluating + checking `a::<impl at src/lib.rs:7:5: 7:49>::{constant#0}`...
   --> src/lib.rs:7:20
    |
  7 |     impl From<[u8; WrapperA::SIZE]> for WrapperA {
    |                    ^^^^^^^^^^^^^^
note: ...which requires checking if `a::<impl at src/lib.rs:7:5: 7:49>::{constant#0}` is a trivial const...
   --> src/lib.rs:7:20
    |
  7 |     impl From<[u8; WrapperA::SIZE]> for WrapperA {
    |                    ^^^^^^^^^^^^^^
note: ...which requires building MIR for `a::<impl at src/lib.rs:7:5: 7:49>::{constant#0}`...
   --> src/lib.rs:7:20
    |
  7 |     impl From<[u8; WrapperA::SIZE]> for WrapperA {
    |                    ^^^^^^^^^^^^^^
    = note: ...which again requires checking if `a::<impl at src/lib.rs:13:5: 13:18>::SIZE` is a trivial const, completing the cycle
note: cycle used when building MIR for `a::WrapperA::0::{constant#0}`
   --> src/lib.rs:5:30
    |
  5 |     pub struct WrapperA([u8; WrapperA::SIZE]);
    |                              ^^^^^^^^^^^^^^
    = 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

Meta

rustc --version --verbose:

rustc 1.97.0-nightly (365c0e1d7 2026-05-06)
binary: rustc
commit-hash: 365c0e1d7a614ca94cb48431dcd2bc6d3b645db1
commit-date: 2026-05-06
host: x86_64-unknown-linux-gnu
release: 1.97.0-nightly
LLVM version: 22.1.4

The first version that fails is nightly-2026-05-04:

rustc 1.97.0-nightly (ad3a598ca 2026-05-03)
binary: rustc
commit-hash: ad3a598ca4bc7c68bcbbce3e0d3be9a7618df190
commit-date: 2026-05-03
host: x86_64-unknown-linux-gnu
release: 1.97.0-nightly
LLVM version: 22.1.4

The last version that works correctly is nightly-2026-05-03:

rustc 1.97.0-nightly (20de910db 2026-05-02)
binary: rustc
commit-hash: 20de910db49d3476ccf49ea79a4b22e2b5dface0
commit-date: 2026-05-02
host: x86_64-unknown-linux-gnu
release: 1.97.0-nightly
LLVM version: 22.1.4

Activity

  1. added
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on May 8, 2026
  2. nazar-pc commented on May 14, 2026

    @nazar-pc
    ContributorAuthor

    I'd really appreciate if this regression could be tracked down. I understand this is an unstable feature, but having something like this seems to make generic const expressions essentially unusable, while previously they just required a large number of hacks and workarounds.

  3. theemathas commented on May 14, 2026

    @theemathas
    Contributor

    @nazar-pc You can use cargo-bisect-rustc to figure out which PR caused this regression.

    Unfortunately though, generic_const_exprs is a rather broken feature, and there's a good chance that it might be removed from the compiler completely, and replaced with min_generic_const_args.

  4. nazar-pc commented on May 15, 2026

    @nazar-pc
    ContributorAuthor

    Thanks, I didn't know about cargo-bisect-rustc before.
    ad3a598 is the commit with regression, which points to #156113.

  5. theemathas commented on May 15, 2026

    @theemathas
    Contributor

    cc @zedddie and @BoxyUwU (author and reviewer of #153536, which seems to have regressed this)

  6. added
    T-typesRelevant to the types team, which will review and decide on the PR/issue.
    I-cycleIssue: A query cycle occurred while none was expected
    and removed
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on May 15, 2026
  7. BoxyUwU commented on May 15, 2026

    @BoxyUwU
    Member

    would be useful to figure out what's going on here as there might be non-GCE impact

  8. zedddie commented on May 15, 2026

    @zedddie
    Contributor

    this goes from a3e96d8, which is in #155767

  9. BoxyUwU commented on May 15, 2026

    @BoxyUwU
    Member

    ah thx zedddie, this seems not worth fixing then unfortunately. @nazar-pc you might want to try porting your code over to feature(generic_const_args)

  10. nazar-pc commented on May 15, 2026

    @nazar-pc
    ContributorAuthor

    Hm, so generic_const_exprs is now essentially perma-broken if even something as basic as pub const SIZE: usize = usize::from(1u8); is no longer supported?

  11. BoxyUwU commented on May 15, 2026

    @BoxyUwU
    Member

    hmm dont understand what's going on here and also won't really have time to look into it for a bit

  12. theemathas commented on May 16, 2026

    @theemathas
    Contributor

    Should this reissue be reopened, then? I'm not sure I understand what's going on.

  13. BoxyUwU commented on May 16, 2026

    @BoxyUwU
    Member

    I think what's going on is that coherence uses the new solver and the new solver also just doesn't support GCE and currently things are being changed about how normalization works in the new solver (which affects coherence). In theory GCE should only be more broken when it comes to const arguments in impl headers, but I do agree that it's significantly more broken now than it was before which is quite unfortunate ^^'

    I think generic_const_args is "ready" enough for people to migrate off of GCE to it at this point and generally is what people should do as GCE won't stick around for much longer, regardless of the fact that its support has been gradually degrading over the last couple years. Given this, I don't think GCE is important enough to revert or block any of the work happening around normalization in the new solver, but yeah sorry for GCE kinda falling apart.

  14. nazar-pc commented on May 16, 2026

    @nazar-pc
    ContributorAuthor

    Well, generic_const_args seems too limiting today. The first method I needed to refactor was:

    pub unsafe trait TrivialType
    where
        Self: Copy,
    {
        // ...
        fn as_bytes(&self) -> &[u8; size_of::<Self>()] {
            unsafe { ptr::from_ref(self).cast::<[u8; _]>().as_ref_unchecked() }
        }
        // ...
    }

    But it is not supported:

    error: generic parameters may not be used in const operations
       --> crates/shared/ab-io-type/src/trivial_type.rs:114:43
        |
    114 |     fn as_bytes(&self) -> &[u8; size_of::<Self>()] {
        |                                           ^^^^ cannot perform const operation using `Self`
        |
        = note: type parameters may not be used in const expressions
        = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions
    

    I completely understand the desire and benefits of completing/stabilizing something more minimal, but such things are everywhere in my project.

  15. BoxyUwU commented on May 18, 2026

    @BoxyUwU
    Member

    Would you be able to open a thread in #project-const-generics on the rust zulip titled sth like "help migrating from GCE to GCA"? I (and others) will be able to help you with migrating from GCE to GCA.

    Other than that, the diagnositc you're getting sure is unfortunate 😅 does not set you up for success (i will file a bug for this). The way GCA works is that it allows arbitrary paths to const items including generics. so you can do const FOO<T>: usize = size_of::<T>(); and then [u8; FOO<T>] but you can't do [u8; size_of::<T>()].

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.F-const_trait_impl`#![feature(const_trait_impl)]`F-generic_const_exprs`#![feature(generic_const_exprs)]`I-cycleIssue: A query cycle occurred while none was expectedT-typesRelevant to the types team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions