perf: Drop irrelevant caller bounds from query type op keys - #79
Draft
xmakro wants to merge 1 commit into
Draft
Conversation
xmakro
force-pushed
the
perf/type-op-irrelevant-param-env
branch
from
August 4, 2026 07:22
a374fe2 to
01d80d0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Query type ops canonicalize the param env into their cache key, so the same goal asked under two different sets of where-clauses gets two entries. Async trait desugaring makes that expensive: every impl's method carries
Self: 'at, which expands to a bound naming that impl's own type, so each impl gets its own key and the work behind it runs once per impl instead of once. Regions are not what matters here, a bound with no regions at all behaves the same way and a bound that is identical in every impl does not.Most of those bounds cannot take part in the answer. A caller bound is reached only by unifying it with the goal, so a bound naming a type or const parameter is out of reach for a goal that has none, and outlives bounds are never assembled as candidates. When every bound is one of those two, the empty env goes into the key instead. This happens at the single point where every query type op canonicalizes, and the response is still instantiated against the original env, so only the key changes.
Lifetime parameters deliberately do not count as putting a bound out of reach.
is_globaladmits'staticin the goal and unification relates regions freely, soFoo<'a>: Traitreally can be selected forFoo<'static>: Trait. Treating a lifetime like a type parameter here makes MIR borrow checking fail to prove goals that HIR typeck accepted:Measured locally on a stage2 build with CI LLVM and jemalloc but no PGO or BOLT, so branch-shaped effects will look different upstream: -0.16% over hyper, ripgrep, serde and syn, and -21.5% on a generated file of 80 async-trait impls. rust-timer should settle the crate numbers.