Skip to content
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

add morespecific rule for Type{Union{}} #49349

Merged
merged 4 commits into from
Apr 20, 2023
Merged

Conversation

vtjnash
Copy link
Member

@vtjnash vtjnash commented Apr 13, 2023

Make Type{Union{}} in method definitions always the most specific type
(normally these would end up being ambiguous). This ensures we do not
invalidate them, nor need to consider ambiguities that might arise from
intersections with them.

Based on the new morespecific rule for Union{} and method definitions of
the specific form f(..., Type{Union{}}, Vararg). If a method
definition exists with that specific form, the intersection visitor will
ignore all intersections that have that as their only result, saving
significant effort when working with lookups involving Type{<:T}
(which usually ended up mostly ambiguous anyways).

Fixes: #33780

This pattern turns out to have still to been making package loading
slow. We could keep adding methods following the ambiguity pattern
#46000 for the couple specific
functions that need it (constructor, eltype, IteratorEltype,
IteratorSize, and maybe a couple others) so the internals can detect
those and optimize functions that have that method pair. But it seems
somewhat odd, convoluted, and non-obvious behavior there. Instead, this
breaks all ambiguities in which Union{} is present explicitly in favor
of the method with Union{}. This means that when computing method
matches, as soon as we see one method definition with Union{}, we can
record that the method is the only possible match for that slot.

This, in essence, permits creating a rule for dispatch that a TypeVar
lower bound must be strictly a supertype of Union{}, but this creates it
at the function level, instead of expecting the user to add it to every
TypeVar they use to define methods.

This also lets us improve the error message for these cases (generally
they should error to avoid polluting the inference result), since we can
be assured this method will be called, and not result in an ambiguous
MethodError instead!

Reverts the functional change of #46000

The list of methods to add here was mainly autogenerated by Test.detect_ambiguities.

On loading OmniPackage, I see this give a few seconds savings. Roughly 26s -> 23s (>10% faster)

@vtjnash vtjnash added the compiler:latency Compiler latency label Apr 13, 2023
@vtjnash vtjnash requested a review from JeffBezanson April 13, 2023 19:40
@vtjnash vtjnash added the don't squash Don't squash merge label Apr 13, 2023
@KristofferC KristofferC added the needs pkgeval Tests for all registered packages should be run with this change label Apr 14, 2023
vtjnash added 4 commits April 18, 2023 15:51
Removes some overly strict `@test_throws MethodError` for calls with
`Union{}` or `Any`, in preparation for making those errors more precise.
Make Type{Union{}} in method definitions always the most specific type
(normally these would end up being ambiguous). This ensures we do not
invalidate them, nor need to consider ambiguities that might arise from
intersections with them.
Based on the new morespecific rule for Union{} and method definitions of
the specific form `f(..., Type{Union{}}, Vararg)`. If a method
definition exists with that specific form, the intersection visitor will
ignore all intersections that have that as their only result, saving
significant effort when working with lookups involving `Type{<:T}`
(which usually ended up mostly ambiguous anyways).

Fixes: #33780

This pattern turns out to have still to been making package loading
slow. We could keep adding methods following the ambiguity pattern
#46000 for the couple specific
functions that need it (constructor, eltype, IteratorEltype,
IteratorSize, and maybe a couple others) so the internals can detect
those and optimize functions that have that method pair. But it seems
somewhat odd, convoluted, and non-obvious behavior there. Instead, this
breaks all ambiguities in which Union{} is present explicitly in favor
of the method with Union{}. This means that when computing method
matches, as soon as we see one method definition with Union{}, we can
record that the method is the only possible match for that slot.

This, in essence, permits creating a rule for dispatch that a TypeVar
lower bound must be strictly a supertype of Union{}, but this creates it
at the function level, instead of expecting the user to add it to every
TypeVar they use to define methods.

This also lets us improve the error message for these cases (generally
they should error to avoid polluting the inference result), since we can
be assured this method will be called, and not result in an ambiguous
MethodError instead!

Reverts the functional change of #46000
Since T cannot be Union{} here, the prior optimization would not get
detected post facto, but a priori this cannot be inhabited for
T=Union{}, so we can exclude it immediately. This does not happen during
inference, but shows up during edge validation somewhat often.
@vtjnash vtjnash force-pushed the jn/morespecific-bottom branch from d1a2c9e to 285c770 Compare April 18, 2023 19:52
vtjnash added a commit that referenced this pull request Apr 19, 2023
Even if we have a new method that is more specific than the method it is
replacing, there still might exist an existing method that is more
specific than both which already covers their intersection.

An example of this pattern is adding
    Base.IteratorSize(::Type{<:NewType})
causing invalidations on
    Base.IteratorSize(::Type)
for specializations such as
    Base.IteratorSize(::Type{<:AbstractString})
even though the intersection of these is fully covered already by
    Base.IteratorSize(::Type{Union{}})
so our new method would never be selected there.

This won't detect ambiguities that already cover this intersection, but
that is why we are looking to move away from that pattern towards
explicit methods for detection in closer to O(n) instead of O(n^2): #49349.

Similarly, for this method, we were unnecessarily dropping it from the
MethodTable cache. This is not a significant latency problem (the cache
is cheap to rebuild), but it is also easy to avoid in the first place.

Refs #49350
@vtjnash
Copy link
Member Author

vtjnash commented Apr 19, 2023

@nanosoldier runtests()

@nanosoldier
Copy link
Collaborator

Your package evaluation job has completed - possible new issues were detected.
A full report can be found here.

@vtjnash vtjnash removed the needs pkgeval Tests for all registered packages should be run with this change label Apr 20, 2023
@vtjnash vtjnash merged commit d8fb5e7 into master Apr 20, 2023
@vtjnash vtjnash deleted the jn/morespecific-bottom branch April 20, 2023 15:32
vtjnash added a commit that referenced this pull request Apr 20, 2023
Even if we have a new method that is more specific than the method it is
replacing, there still might exist an existing method that is more
specific than both which already covers their intersection.

An example of this pattern is adding
    Base.IteratorSize(::Type{<:NewType})
causing invalidations on
    Base.IteratorSize(::Type)
for specializations such as
    Base.IteratorSize(::Type{<:AbstractString})
even though the intersection of these is fully covered already by
    Base.IteratorSize(::Type{Union{}})
so our new method would never be selected there.

This won't detect ambiguities that already cover this intersection, but
that is why we are looking to move away from that pattern towards
explicit methods for detection in closer to O(n) instead of O(n^2): #49349.

Similarly, for this method, we were unnecessarily dropping it from the
MethodTable cache. This is not a significant latency problem (the cache
is cheap to rebuild), but it is also easy to avoid in the first place.

Refs #49350
@Keno
Copy link
Member

Keno commented Apr 21, 2023

This PR appears to fail analyzegc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler:latency Compiler latency don't squash Don't squash merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Dispatch pitfall due to Type{Union{}} <: Type{<:MyType}.
4 participants