-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
[mypyc] Fix order of dispatch type checking in singledispatch functions #10844
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
[mypyc] Fix order of dispatch type checking in singledispatch functions #10844
Conversation
When creating a dispatch function for a singledispatch function, avoid checking any classes before we've checked all of their subclasses. That makes sure we use the implementation with the dispatch type that appears first in the actual argument's type's MRO, which is what the standard library implementation of singledispatch does (it goes up the argument's MRO, looking for classes that have registered implementations associated with them).
The CI failures looks a bit weird, so I restarted the builds just in case. |
mypyc/irbuild/function.py
Outdated
# gets checked first | ||
if any(is_subclass(typ, prev_type) for prev_type in dispatch_types): | ||
dispatch_types.insert(0, typ) | ||
funcs.insert(0, impl) |
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.
I think this logic is flawed. Imagine a situation like this:
class A: ...
class B(A): ...
class C(B): ...
Then if these classes appear in impls
in the order A, C, B
, then after this "sorting", they will be in the order B, C, A
, which is wrong.
Also when fixing this I think it could make sense to make it stable (i.e. order of unrelated items should be preserved).
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.
I think this should be fixed now. Could you take a look and let me know what you think?
Add a test to reproduce the bug in the class sorting implementation mentioned in python#10844 (comment).
Improve the sorting of classes to: - always put subclasses before any of their superclasses, even when there are multiple related classes (see testMultipleRelatedClassesBeingRegistered for an example of this) - preserve the order of implementations with unrelated dispatch types
The one use of Iterable was removed in the previous commit, so this is unnecessary.
@@ -857,3 +858,44 @@ def gen_dispatch_func_ir( | |||
func_decl = FuncDecl(dispatch_name, None, builder.module_name, sig) | |||
dispatch_func_ir = FuncIR(func_decl, args, blocks) | |||
return dispatch_func_ir | |||
|
|||
|
|||
def sort_with_subclasses_first( |
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.
This seems plausible, but I think that it might be more straightforward to implement this as a topological sort, instead. I'll follow up via PM.
Change the sorting of classes that we use when generating dispatch functions to use topological sort on a graph made up of the classes with edges pointing from subclasses to the classes on their MRO. This also modifies the signature of topsort to take any type for the vertices of the graph.
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.
Looks good, one comment
Use a dictionary comprehension to make the graph creation nicer.
This PR changes the order that the dispatch function checks dispatch types of registered implementations to always check any of the class's subclasses (if any have registered implementations) before checking the class itself.
That matches the semantics of the standard library implementation of singledispatch more closely, which goes up the MRO of the argument's type and uses the registered implementation that corresponds with the first type that it finds that is also a dispatch type.
Test Plan
I added a test to run-singledispatch.test that reproduces this bug.