[implied_bounds_in_impls
]: don't ICE on default generic parameter and move to nursery
#11437
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.
Fixes #11422
This fixes two ICEs (1, 2), and moves it to nursery for now, because this lint needs some improvements in its suggestion (see #11435, for one such example).
changelog: Moved [
implied_bounds_in_impls
] to nursery (Now allow-by-default)#11437
changelog: [
implied_bounds_in_impls
]: don't ICE on default generic parameter in supertrait clauser? @xFrednet (since you reviewed my PR that added this lint, I figured it might make sense to have you review this as well since you have seen this code before. If you don't want to review this, sorry! Feel free to reroll then)
As for the ICE, it's pretty complicated and very confusing imo, so I'm going to try to explain the idea here (partly for myself, too, because I've confused myself several times writing- and fixing this):
Expand
The general idea behind the lint is that, if we have this function:
We want to lint the
PartialEq
bound because it's unnecessary. That exact bound is already specified inPartialOrd<i32>
's supertrait clause:The way it does this is in two steps:
impl Trait
return type and collect each of the trait's supertrait bounds into a vec. We also store the generic arguments for later.PartialEq
has no supertraits, nothing to add.PartialOrd
is defined astrait PartialOrd: PartialEq
, so addPartialEq
to the list, as well as the generic argument(s)<i32>
Once we are done, we have these entries in the vec:
[(PartialEq, [i32])]
DefId
in the implied bounds vec.PartialEq
is in that vec. However, that is not enough, because the trait is generic. If the user wroteimpl PartialEq<String> + PartialOrd<i32>
, thenPartialOrd
clearly doesn't implyPartialEq
. Which means, we also need to check that the generic parameters match. This is why we also collected the generic arguments inPartialOrd<i32>
. This process of checking generic arguments is pretty complicated and is also where the two ICEs happened.The way it checks that the generic arguments match is by comparing the generic parameters in the super trait clause:
...this needs to match...
In the compiler, the
Rhs
generic parameter is its own type and we cannot just compare it toi32
. We need to "substitute" it.Internally,
Rhs
is represented asRhs#1
(the number next to # represents the type parameter index. They start at 0, but 0 is "reserved" for the implicitSelf
generic parameter).How do we go from
Rhs#1
toi32
? Well, we know that all the generic parameters had to be substituted in theimpl ... + PartialOrd<i32>
type. So we subtract 1 from the type parameter index, giving us 0 (Self
is not specified in that list of arguments). We use that as the index into the generic argument list<i32>
. That'si32
. Now we know that the supertrait clause looks like: PartialEq<i32>
.Then, we can compare that to what the user actually wrote on the bound that we think is being implied:
impl PartialEq<i32> + ...
.Now to the actual bug: this whole logic doesn't take into account default generic parameters. Actually,
PartialOrd
is defined like this:If we now have a function like this:
that logic breaks apart... We look at the supertrait predicate
: PartialEq<Rhs>
(Rhs
isRhs#1
), then take the first argument in the generic argument listPartialEq<..>
to resolve theRhs
, but at this point we crash because there is no generic argument.The index 0 is out of bounds. If this happens (and we even get to linting here, which could only happen if it passes typeck), it must mean that that generic parameter has a default type that is not required to be specified.
This PR changes the logic such that if we have a type parameter index that is out of bounds, it looks at the definition of the trait and check that there exists a default type that we can use instead.
So, we see
<Rhs = Self>
, and useSelf
for substitution, and end up with this predicate:: PartialEq<Self>
. No crash this time.