-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
rework winnowing to sensibly handle global where-bounds #132325
Conversation
@bors try @rust-timer queue |
This comment has been minimized.
This comment has been minimized.
rework winnowing to sensibly handle global where-bounds this is somewhat weird, but it at least allows us to mirror this behavior in the new solver: - trivial builtin-impls - non-global where-bounds, bailing with ambiguity if at least one global where-bound exists - object ☠️ + alias-bound candidates - merge candidates ignoring global where-bounds r? `@compiler-errors`
☀️ Try build successful - checks-actions |
Finished benchmarking commit (6d565b7): comparison URL. Overall result: ✅ improvements - no action neededBenchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. While you can manually mark this PR as fit for rollup, we strongly recommend not doing so since this PR may lead to changes in compiler perf. @bors rollup=never Instruction countThis is the most reliable metric that we have; it was used to determine the overall result at the top of this comment. However, even this metric can sometimes exhibit noise.
Max RSS (memory usage)Results (primary -2.2%, secondary 0.6%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesResults (primary 2.3%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Binary sizeResults (primary -0.1%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Bootstrap: 784.701s -> 783.947s (-0.10%) |
@craterbot check |
👌 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
🚧 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
🎉 Experiment
|
@bors try for later: |
rework winnowing to sensibly handle global where-bounds this is somewhat weird, but it at least allows us to mirror this behavior in the new solver: - trivial builtin-impls - non-global where-bounds, bailing with ambiguity if at least one global where-bound exists - object ☠️ + alias-bound candidates - merge candidates ignoring global where-bounds This is a different approach from rust-lang#124592 which maintains the "if there are global where-bounds, don't guide type inference using non-global where-bounds" behavior, hopefully avoiding the breakage and means we use guidance from non-global where-bounds in fewer, instead of in more cases. r? `@compiler-errors`
☀️ Try build successful - checks-actions |
👌 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
🚧 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
🎉 Experiment
|
it looks like the result are unknown yet again 🤔 what's going on there cc @rust-lang/infra |
The final comment period, with a disposition to merge, as per the review above, is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. This will be merged soon. |
@bors r=oli-obk rollup=never |
☀️ Test successful - checks-actions |
Finished benchmarking commit (604d669): comparison URL. Overall result: ✅ improvements - no action needed@rustbot label: -perf-regression Instruction countThis is the most reliable metric that we have; it was used to determine the overall result at the top of this comment. However, even this metric can sometimes exhibit noise.
Max RSS (memory usage)Results (primary 0.9%, secondary -0.3%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesResults (primary -0.3%, secondary -4.1%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Binary sizeResults (primary -0.1%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Bootstrap: 770.381s -> 768.397s (-0.26%) |
…errors -Znext-solver: modify candidate preference rules This implements the design proposed in the FCP in rust-lang#132325 and matches the old solver behavior. I hope the inline comments are all sufficiently clear, I personally think this is a fairly clear improvement over the existing approach using `fn discard_impls_shadowed_by_env`. This fixes rust-lang/trait-system-refactor-initiative#96. This also fixes rust-lang#133639 which encounters an ICE in negative coherence when evaluating the where-clause. Given the features required to trigger this ICE 🤷 r? `@compiler-errors`
…errors -Znext-solver: modify candidate preference rules This implements the design proposed in the FCP in rust-lang#132325 and matches the old solver behavior. I hope the inline comments are all sufficiently clear, I personally think this is a fairly clear improvement over the existing approach using `fn discard_impls_shadowed_by_env`. This fixes rust-lang/trait-system-refactor-initiative#96. This also fixes rust-lang#133639 which encounters an ICE in negative coherence when evaluating the where-clause. Given the features required to trigger this ICE 🤷 r? ``@compiler-errors``
Rollup merge of rust-lang#133643 - lcnr:merge-candidates, r=compiler-errors -Znext-solver: modify candidate preference rules This implements the design proposed in the FCP in rust-lang#132325 and matches the old solver behavior. I hope the inline comments are all sufficiently clear, I personally think this is a fairly clear improvement over the existing approach using `fn discard_impls_shadowed_by_env`. This fixes rust-lang/trait-system-refactor-initiative#96. This also fixes rust-lang#133639 which encounters an ICE in negative coherence when evaluating the where-clause. Given the features required to trigger this ICE 🤷 r? ``@compiler-errors``
There may be multiple ways to prove a given trait-bound. In case there are multiple such applicable candidates we need to somehow merge them or bail with ambiguity. When merging candidates we prefer some over others for multiple reasons:
The approach in this PR1
We disable normalization via impls when using non-global where-bounds or alias-bounds, even if we're unable to normalize by using the where-bound.
Why this behavior?
inference guidance via where-bounds and alias-bounds
where-bounds
While the above pattern exists in the wild, I think that most inference guidance due to where-bounds is actually unintended. I believe we may want to restrict inference guidance in the future, e.g. limit it to where-bounds whose self-type is a param.
alias-bounds
Disable normalization via impls when using where-bounds
cc rust-lang/trait-system-refactor-initiative#125
If an impl adds constraints not required by a where-bound, using the impl may cause compilation failure avoided by treating the associated type as rigid.
This is also why we can always use trivial builtin impls, even for normalization. They are guaranteed to never add any requirements.
Lower priority for global where-bounds
A where-bound is considered global if it does not refer to any generic parameters and is not higher-ranked. It may refer to
'static
.This means global where-bounds are either always fully implied by an impl or unsatisfiable. We don't really care about the inference behavior of unsatisfiable where-bounds :3
If a where-bound is fully implied then using an applicable impl for normalization cannot result in additional constraints. As this is the - afaict only - reason why we disable normalization via impls in the first place, we don't have to disable normalization via impls when encountering global where-bounds.
Consider global where-bounds at all
Given that we just use impls even if there exists a global where-bounds, you may ask why we don't just ignore these global where-bounds entirely: we use them to weaken the inference guidance from non-global where-bounds.
Without a global where-bound, we currently prefer non-global where bounds even though there would be an applicable impl as well. By adding a non-global where-bound, this unnecessary inference guidance is disabled, allowing the following to compile:
There exist multiple crates which rely on this behavior.
Design considerations
We would like to be able to normalize via impls as much as possible. Disabling normalization simply because there exists a where-bound is undesirable.
For the sake of backwards compatability I intend to mostly mirror the current inference guidance rules and then explore possible improvements once the new solver is done. I do believe that removing unnecessary inference guidance where possible is desirable however.
Whether a where-bound is global depends on whether used lifetimes are
'static
. The where-boundu32: Trait<'static>
is either entirely implied by an impl, meaning that it does not have to disable normalization via impls, whileu32: Trait<'a>
needs to disable normalization via impls as the impl may only hold for'static
. Considering all where-bounds to be non-global once they contain any region is unfortunately a breaking change.How does this differ from stable
The currently stable approach is order dependent:
This means that whether we bail with ambiguity or simply use the non-global where bound depending on the order of where-clauses and number of applicable impl candidates. See the tests added in the first commit for more details. With this PR we now always bail with ambiguity.
I've previously tried to always use the non-global candidate, causing unnecessary inference guidance and undesirable breakage. This already went through an FCP in #124592. However, I consider the new approach to be preferable as it exclusively removes incompleteness. It also doesn't cause any crater breakage.
How to support this in the new solver :o
This is separately implemented in #133643 and not part of this FCP!
To implement the global vs non-global where-bound distinction, we have to either keep
'static
in theparam_env
when canonicalizing, or eagerly distinguish global from non-global where-bounds and provide that information to the canonical query.The old solver currently keeps
'static
only theparam_env
, replacing it with an inference variable in thevalue
.rust/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
Lines 49 to 64 in a4cedec
I dislike that based on vibes and it may end up being a problem once we extend the environment inside of the solver as we must not rely on
'static
in thepredicate
as it would get erased in MIR typeck.An alternative would be to eagerly detect trivial where-bounds when constructing the
ParamEnv
. We can't entirely drop them as explained above, so we'd instead replace them with a new clause kindTraitImpliedByImpl
which gets entirely ignored except when checking whether we should eagerly guide inference via a where-bound. This approach can be extended to where-bounds which are currently not considered global to stop disabling normalization for them as well.Keeping
'static
in theparam_env
is the simpler solution here and we should be able to move to the second approach without any breakage. I therefore propose to keep'static
in the environment for now.r? @compiler-errors
Footnotes
see the source for more details ↩
they don't constrain inference and don't add any lifetime constraints ↩
a where-bound is global if it is not higher-ranked and doesn't contain any generic parameters,
'static
is ok ↩we arbitrary select a single object and alias-bound candidate in case multiple apply and they don't impact inference. This should be unnecessary in the new solver. ↩ ↩2
Necessary for
dyn Any
and https://github.com/rust-lang/rust/issues/57893 ↩global where-bounds are only used if they are unsatisfiable, i.e. no impl candidate exists ↩