-
Notifications
You must be signed in to change notification settings - Fork 1.6k
[ty] Avoid unnecessary argument type expansion #19999
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
Conversation
Diagnostic diff on typing conformance testsNo changes detected when running ty on typing conformance tests ✅ |
|
reveal_type( | ||
# error: [no-matching-overload] | ||
# revealed: Unknown | ||
f( | ||
C(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Considering the same example, but pass in A()
as the first argument and use a=None
instead of a=1
for the parameter above, this would still continue with the expansion. The reason being that A()
matches parameter of at least one overload which means we cannot skip argument type expansion.
I'm trying to think through what kind of heuristic to have that would avoid the expansion in this case as well. This would still lead to no-matching-overload because even though Unknown
is assignable to int
, the spec says that all argument lists resulting from an expansion should evaluate successfully and there's no such expansion as expanding Unknown | None
leads to argument lists where at least one would have None
which isn't assignable to int
.
Regardless, I think it'd be best to set a higher limit on the expansion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think there will be cases where we can't avoid actually trying all the expansions and see if any of them work -- best we can do there is optimize the expansions as best we can (e.g. iterator instead of eagerly materialized?) and then set a reasonable limit.
EDIT: Nevermind, it is because ty doesn't support
I wasn't expecting any mypy-primer diff but it seems that we have new diagnostics :) I think this might be due to the fact that ty doesn't support Screen.Recording.2025-08-20.at.15.46.35.mov |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, thank you!
reveal_type( | ||
# error: [no-matching-overload] | ||
# revealed: Unknown | ||
f( | ||
C(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think there will be cases where we can't avoid actually trying all the expansions and see if any of them work -- best we can do there is optimize the expansions as best we can (e.g. iterator instead of eagerly materialized?) and then set a reasonable limit.
// for evaluating the expanded argument lists. | ||
snapshotter.restore(self, pre_evaluation_snapshot); | ||
|
||
// At this point, there's at least one argument that can be expanded. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to first explicitly check if there are any expandable arguments (using the new method you added), then do this check, then actually expand the arguments? This way even if the heuristic applies, we've already generated the expansion, just haven't used the expansions yet.
But maybe generating the expansions is very cheap relative to actually trying the call with new argument types, so this doesn't matter?
And I guess we'd slow down the fast path slightly if we first check all arguments for expandability, then separately expand them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I don't think this would really matter in practice mainly because there are only a few differences between expand_type
and is_type_expandable
which are (1) collecting the enum members from the metadata (both method generates the metadata) (2) performing multi-cartesian product of tuple elements and (3) allocation. I guess it wouldn't hurt to avoid allocating when it's easy to do so. Let me try it, I think I'll make this change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah right, so the main reason I did it at this location is so that the bindings state is the one before the type checking step, so that we only skip the overloads that have been filtered by the arity check.
So, we'd either have to pay the cost of either
- Allocation for the first expansion, taking the bindings snapshot. We'd skip the snapshot if there are no expansion in this case.
- Always take the binding snapshot, check whether expansion needs to happen using the logic in this PR, skip allocating for the first expansion if there's no need
I think going with the latter sounds reasonable i.e., instead of always allocating the first expansion, we'd always allocate the bindings snapshot.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, but then there's complication on restoring the snapshots at the correct places. I think I'll leave this as is for now.
## Summary Part of: astral-sh/ty#868 This PR adds a heuristic to avoid argument type expansion if it's going to eventually lead to no matching overload. This is done by checking whether the non-expandable argument types are assignable to the corresponding annotated parameter type. If one of them is not assignable to all of the remaining overloads, then argument type expansion isn't going to help. ## Test Plan Add mdtest that would otherwise take a long time because of the number of arguments that it would need to expand (30).
Summary
Part of: astral-sh/ty#868
This PR adds a heuristic to avoid argument type expansion if it's going to eventually lead to no matching overload.
This is done by checking whether the non-expandable argument types are assignable to the corresponding annotated parameter type. If one of them is not assignable to all of the remaining overloads, then argument type expansion isn't going to help.
Test Plan
Add mdtest that would otherwise take a long time because of the number of arguments that it would need to expand (30).