Skip to content

[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

Merged

Conversation

pranavrajpal
Copy link
Contributor

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.

Add a test that makes sure that our implementation goes up the MRO of
the dispatch type and uses the registered implementation that comes
first, instead of going through implementations in the reverse order.
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).
@ilevkivskyi ilevkivskyi requested a review from msullivan July 22, 2021 11:21
@ilevkivskyi
Copy link
Member

The CI failures looks a bit weird, so I restarted the builds just in case.

# 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)
Copy link
Member

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).

Copy link
Contributor Author

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
@pranavrajpal pranavrajpal requested a review from ilevkivskyi July 22, 2021 20:31
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(
Copy link
Collaborator

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.
Copy link
Collaborator

@msullivan msullivan left a 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants