Bevy traits slow with -Znext-solver=globally
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.1k
- PR merge metrics
- PR metrics pending
Description
Edit: @eugineerd below has a simple macro-less reproduction which is just the same line repeated 80 times
rust-analyzer version: rust-analyzer version: 0.4.2940-standalone
rustc version: rustc 1.98.0-nightly (57d06900f 2026-05-27)
editor or extension: vscode, extension: 0.4.2940
relevant settings: "rust-analyzer.server.extraEnv": {"RA_PROFILE": "*>500",},
repository link (if public, optional): https://github.com/bevyengine/bevy/tree/c6f634ca9f406d68ba5109d921247b654cb42c10
code snippet to reproduce: https://github.com/bevyengine/bevy/blob/c6f634ca9f406d68ba5109d921247b654cb42c10/examples/ui/widgets/feathers_gallery.rs
In the file above, specifically in the normal rust expressions inside bsn! macro invocation in the function "demo_column_1", a simple autocomplete takes more than 18s.
The macro might seem like a smoking gun here, but it struggles just the same after expanding it.
At the same time, the deeply nested nature of the expanded code seemed like an issue, but i tested changing the macro to output a lot of let bindings which are later used and combined into the final value, but this seemed to have made inference slower, even if each let binding is of type Box<dyn Trait> (Trait being the same for all). So it probably isn't the macro, but something else.
Of course this is a worst-case, an enormous 400 line macro invocation which expands to more than 1k lines of code, but we hope smaller versions of this can become very common for users of bevy.
This is all on a Ryzen 9 9900X with 32gb of RAM, not the weakest machine by far.
I've recorded a profile of one way this issue becomes obvious (autocomplete inside a closure in the function body) with samply. Sadly, thats not even necessary for this to be an issue: simply having the file open causes operations in all other files to take 18s or more.
samply command:
sudo sysctl kernel.perf_event_mlock_kb=2048
sudo sysctl kernel.perf_event_paranoid=-1
SAMPLY_USE_DEBUGINFOD=1 samply record --profile-name rust-analyzer --output /tmp/rust-analyzer.json.gz --reuse-threads -r 20000 -p $(pgrep -o rust-analyzer)
download link for the profile file: https://share.yadamiel.com/rust-analyzer.json.gz
You can view it using samply load rust-analyzer.json.gz which will start a local webserver to serve the profile to profiler.firefox.com. Note that it by default only shows one thread, to see the full profile, select all of them.
Heres the RA_PROFILE logs of the same timespan as the samply profile
2026-06-18T20:40:16.634042933+02:00 WARN overly long loop turn took 8.267930083s:
(event handling took 94.94µs): Notification { method: "textDocument/didChange" }
(garbage collection took Some(8.26383417s))
8267ms GlobalState::handle_event @ event = Event::Lsp
17742ms semantic_diagnostics
17742ms diagnostics @ name = None
17742ms semantic_diagnostics
17742ms diagnostics @ name = None
16641ms semantic_diagnostics
16641ms diagnostics @ name = None
17769ms handle_code_action
17742ms semantic_diagnostics
17742ms diagnostics @ name = None
17769ms handle_code_action
17742ms semantic_diagnostics
17742ms diagnostics @ name = None
17844ms handle_completion
17744ms CompletionContext::new
17579ms CompletionContext::analyze
17579ms SemanticsImpl::analyze_impl (2 calls)
17841ms handle_semantic_tokens_full_delta
17841ms highlight
17599ms normalize_opaque_type @ goal = Goal { param_env: ParamEnv { clauses: [] }, predicate: NormalizesTo(AliasTerm { args: [], kind: OpaqueTy { def_id: InternedOpaqueTyId(InternedOpaqueTyId(Id(14f401))) }, .. }, ?0t) } def_id = InternedOpaqueTyId(InternedOpaqueTyId(Id(14f401))) (2 calls)
17598ms normalize_opaque_type @ goal = Goal { param_env: ParamEnv { clauses: [] }, predicate: NormalizesTo(AliasTerm { args: [], kind: OpaqueTy { def_id: InternedOpaqueTyId(InternedOpaqueTyId(Id(14f403))) }, .. }, ?0t) } def_id = InternedOpaqueTyId(InternedOpaqueTyId(Id(14f403))) (2 calls)
17596ms normalize_opaque_type @ goal = Goal { param_env: ParamEnv { clauses: [] }, predicate: NormalizesTo(AliasTerm { args: [], kind: OpaqueTy { def_id: InternedOpaqueTyId(InternedOpaqueTyId(Id(14f404))) }, .. }, ?0t) } def_id = InternedOpaqueTyId(InternedOpaqueTyId(Id(14f404))) (4 calls)
17591ms infer_query
888ms impl_trait_ref @ impl_id = ImplId(ImplId(1248f2)) (1743753 calls)
2026-06-18T20:40:49.750369849+02:00 WARN overly long loop turn took 7.168189722s:
(event handling took 187.615µs): Notification { method: "textDocument/didChange" }
(garbage collection took Some(7.162855639s))
7176ms GlobalState::handle_event @ event = Event::Lsp
17155ms semantic_diagnostics
17154ms diagnostics @ name = None
18677ms semantic_diagnostics
18677ms diagnostics @ name = None
18677ms semantic_diagnostics
18677ms diagnostics @ name = None
18458ms handle_code_action
18453ms semantic_diagnostics
18453ms diagnostics @ name = None
18711ms handle_code_action
18681ms semantic_diagnostics
18680ms diagnostics @ name = None
18799ms handle_semantic_tokens_full_delta
18799ms highlight
18505ms normalize_opaque_type @ goal = Goal { param_env: ParamEnv { clauses: [] }, predicate: NormalizesTo(AliasTerm { args: [], kind: OpaqueTy { def_id: InternedOpaqueTyId(InternedOpaqueTyId(Id(14f401))) }, .. }, ?0t) } def_id = InternedOpaqueTyId(InternedOpaqueTyId(Id(14f401))) (2 calls)
18503ms normalize_opaque_type @ goal = Goal { param_env: ParamEnv { clauses: [] }, predicate: NormalizesTo(AliasTerm { args: [], kind: OpaqueTy { def_id: InternedOpaqueTyId(InternedOpaqueTyId(Id(14f403))) }, .. }, ?0t) } def_id = InternedOpaqueTyId(InternedOpaqueTyId(Id(14f403))) (2 calls)
18500ms normalize_opaque_type @ goal = Goal { param_env: ParamEnv { clauses: [] }, predicate: NormalizesTo(AliasTerm { args: [], kind: OpaqueTy { def_id: InternedOpaqueTyId(InternedOpaqueTyId(Id(14f404))) }, .. }, ?0t) } def_id = InternedOpaqueTyId(InternedOpaqueTyId(Id(14f404))) (4 calls)
18771ms handle_completion_resolve
18668ms CompletionContext::new
18570ms CompletionContext::analyze
18570ms SemanticsImpl::analyze_impl (2 calls)
18570ms infer_query
903ms impl_trait_ref @ impl_id = ImplId(ImplId(1248f2)) (1743753 calls)
According to the samply profile, at least 73% of samples happened due to hir_ty::infer:
The flamegraph is also fun to view. so much hir_ty::infer:
I'm very open to suggestions what to try, guidance how to better profile/narrow down the issue or just ideas how to make the macro more rust-analyzer friendly.
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 examples/ui/widgets/feathers_gallery.rs, especially demo_column_1 and the repeated expressions inside the bsn! invocation, then reproduce the delay with -Znext-solver=globally. Use the supplied samply profile and RA_PROFILE logs to trace hir_ty::infer, normalize_opaque_type, and impl_trait_ref; done means identifying and fixing the cause so completion and related operations no longer take roughly 18 seconds on the reproduction.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers, performance
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 30/100