Skip to content

Regression in trait disambiguation with the next generation trait solver (-Znext-solver=globally) #161645

Closed

Description

@HugoPeters1024

I was playing with the next generation trait solver (which is said to stabilize soon: rust-lang/goals#113) on Bevy. Bevy has a notoriously slow to compile subcrate called bevy_pbr and was hoping to see some improvements.

The good new is that the improvement is real and substantial:

Solver bevy_pbr total Frontend Codegen
Legacy (-Znext-solver=coherence) 40.8s 33.9s 6.9s
Next (-Znext-solver=globally) 32.9s 25.5s 7.4s

The bad news is that it became less equipped to deal with ambiguity. In this case the patch is relatively simple and acceptable: bevyengine/bevy#25533. That said, stabilization of the next generation trait solver would technique constitute a regression.

If the answer is, "the next generation solver can only be this performant because it chooses to reject ambiguity" then I'm also relatively satisfied, just wanted to make sure this is a conscious decision.

Tested using rustc 1.100.0-nightly (c656540d6 2026-08-21)

Activity

  1. added
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Aug 24, 2026
  2. HugoPeters1024 commented on Aug 24, 2026

    @HugoPeters1024
    Author

    While trying to make a minimal, dependency-free reproducer, I've come to wonder if it's not actually the current coherence solver that is at fault. Because I don't see how it is able to resolve the following ambiguity without simply discarding the possibility that the opaque impl Sized return type could be u32, making both Schedule::run_if and the blanket Observer::run_if applicable.

    // `rustc -Znext-solver=coherence this_file.rs` accepts this on
    // nightly-2026-08-22. Replacing `coherence` with `globally` gives E0034.
    //
    // Crux: the recursive opaque return type might be `u32`, so the next solver
    // considers both extension traits below to be candidates for `.run_if()`.
    
    struct System<T>(T);
    
    trait Schedule: Sized {
        fn run_if(self) {}
    }
    
    impl Schedule for System<u32> {}
    
    trait Observer: Sized {
        fn run_if(self) {}
    }
    
    impl<T> Observer for System<T> {}
    
    fn system(recurse: bool) -> System<impl Sized> {
        if recurse {
            system(false).run_if();
        }
        System(42_i32)
    }
    
    fn main() {}
  3. added
    T-typesRelevant to the types team, which will review and decide on the PR/issue.
    WG-trait-system-refactorThe Rustc Trait System Refactor Initiative (-Znext-solver)
    A-impl-traitArea: `impl Trait`. Universally / existentially quantified anonymous types with static dispatch.
    A-method-lookupArea: Method lookup (typeck, post `rustc_resolve`, not to be confused with `A-resolve`)
    requires-nightlyThis issue requires a nightly compiler in some way. When possible, use a F-* label instead.
    and removed
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Aug 24, 2026
  4. lcnr commented on Aug 24, 2026

    @lcnr
    Contributor

    can you try the current main branch with https://github.com/kennytm/rustup-toolchain-install-master (or nightly tomorrow)

    I think this should be fixed by #161552 which has been merged today. I think the bevy regression was bevyengine/bevy#25511?

  5. lcnr commented on Aug 24, 2026

    @lcnr
    Contributor

    jup, bevy_pbr also compiles with after that change

    warning: overflow evaluating the requirement `WireframeWideBindGroups: Sync`
       --> src/wireframe.rs:407:10
        |
    407 | #[derive(Resource, Default)]
        |          ^^^^^^^^
        |
        = note: which requires `bevy_platform::collections::HashMap<AssetId<bevy_mesh::Mesh>, (bevy_render::render_resource::BindGroup, u32)>: Sync`
        = note: which requires `hashbrown::map::HashMap<AssetId<bevy_mesh::Mesh>, (bevy_render::render_resource::BindGroup, u32), FixedHasher>: Sync`
        = note: which requires `hashbrown::raw::RawTable<(AssetId<bevy_mesh::Mesh>, (bevy_render::render_resource::BindGroup, u32))>: Sync`
        = note: which requires `(AssetId<bevy_mesh::Mesh>, (bevy_render::render_resource::BindGroup, u32)): Sync`
        = note: which requires `(bevy_render::render_resource::BindGroup, u32): Sync`
        = note: which requires `bevy_render::render_resource::BindGroup: Sync`
        = note: which requires `WgpuWrapper<wgpu::api::bind_group::BindGroup>: Sync`
        = note: which requires `wgpu::api::bind_group::BindGroup: Sync`
        = note: which requires `wgpu::dispatch::DispatchBindGroup: Sync`
        = note: which requires `Arc<wgpu::backend::wgpu_core::CoreBindGroup>: Sync`
        = note: which requires `wgpu::backend::wgpu_core::CoreBindGroup: Send`
        = note: which requires `wgpu::backend::wgpu_core::ContextWgpuCore: Send`
        = note: which requires `Arc<wgpu_core::global::Global>: Send`
        = note: which requires `wgpu_core::global::Global: Send`
        = note: which requires `wgpu_core::hub::Hub: Send`
        = note: and so on...
        = help: consider adding a manual `impl` of auto traits like `Send` for intermediate types, if auto traits are involved
        = help: or consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`bevy_pbr`)
        = note: this lint is attached to the whole crate and can't be disabled on a per-function basis
        = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
        = note: for more information, see issue #159228 <https://github.com/rust-lang/rust/issues/159228>
        = note: `#[warn(recursion_depth_exceeding_limit)]` (part of `#[warn(future_incompatible)]`) on by default
        = note: this warning originates in the derive macro `Resource` (in Nightly builds, run with -Z macro-backtrace for more info)
    
    warning: `bevy_pbr` (lib) generated 1 warning
        Finished `dev` profile [unoptimized + debuginfo] target(s) in 1m 32s
  6. theemathas commented on Aug 24, 2026

    @theemathas
    Contributor

    @lcnr Does that PR change how method resolution works? That sounds odd to me.

  7. HugoPeters1024 commented on Aug 24, 2026

    @HugoPeters1024
    Author

    @lcnr it looks like the bevy_pbr problem itself was already discovered and root caused some extent: bevyengine/bevy#25511. It confusingly was the result of some Send/Sync shenanigans.

    However, the reproducer I posted above still fails on the latest main (04a3cad16c5) with globally solver but not coherence 🤔

  8. lcnr commented on Aug 24, 2026

    @lcnr
    Contributor

    @lcnr Does that PR change how method resolution works? That sounds odd to me.

    that PR causes method selection to discard candidates if they depend on a goal which only evaluates to NoSolution with double the recursion limit.

    However, the reproducer I posted above still fails on the latest main (04a3cad) with globally solver but not coherence 🤔

    👍 that is not required for bevy_pbr, is it? I don't have a strong opinion on whether this example should compile. We don#t know the fact that impl Sized is i32 at the point of doing method selection here.

    What is your expected behavior for

    struct System<T>(T);
    
    trait Schedule: Sized {
        fn run_if(self) {}
    }
    
    impl Schedule for System<u32> {}
    
    trait Observer: Sized {
        fn run_if(self) {}
    }
    
    impl<T> Observer for System<T> {}
    
    fn system(recurse: bool) -> System<impl Sized> {
        if recurse {
            system(false).run_if();
        }
        System(42_u32)
    }
    
    fn main() {}
  9. HugoPeters1024 commented on Aug 25, 2026

    @HugoPeters1024
    Author

    that is not required for bevy_pbr, is it?

    Having now understood the issue better, it's clear that my reproducer was not representative of the bevy_pbr issue.

    In fact, I'd say that throwing an error on that file is in fact the desired behavior! In the case of System(42_u32) there really are 2 equivalently valid choices. So in hindsight, I think globally actually improves upon coherence here 🥳

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

    A-impl-traitArea: `impl Trait`. Universally / existentially quantified anonymous types with static dispatch.A-method-lookupArea: Method lookup (typeck, post `rustc_resolve`, not to be confused with `A-resolve`)C-bugCategory: This is a bug.T-typesRelevant to the types team, which will review and decide on the PR/issue.WG-trait-system-refactorThe Rustc Trait System Refactor Initiative (-Znext-solver)requires-nightlyThis issue requires a nightly compiler in some way. When possible, use a F-* label instead.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions