-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
Make None
promotable to object
, not a subclass, when resolving overloads
#3763
Conversation
@JukkaL what do you think? |
This is slightly unsafe -- if we have a variable |
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 looks like a reasonable approach. A few comments below, plus can you also fix this when not using strict optional checking -- it still infers Any
for a None
argument.
mypy/checkexpr.py
Outdated
if isinstance(formal, Instance) and formal.type.fullname() == "builtins.object": | ||
return 2 | ||
return 1 |
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.
Update the docstring above to mention that we treat None
-> object
as a promotion though it's actually treated as a real subtype elsewhere.
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.
Oh, I forgot that this was true. That makes this seem more hacky.
A general solution (not specific to None or strict-optional) would be to add a fourth level of overload arg similarity for subclasses, e.g.
0: No match
1: Actual can be promoted to formal
2: Actual is a subclass of formal
3: Actual is the same as formal
Would that be preferable? I'd be happy to implement (optionally including refactoring this function to return an Enum
) if so. It seems less hacky but much more likely to have unforseen consequences/introduce new errors.
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.
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.
Treating promotions as different from other forms of compatibility is already unsafe, and adding more levels of similarity would add more room for unsafety. I'd prefer to use the hack since its effect is limited. Basically we'd only surgically enable a specific use case that we know to be important, but otherwise overload resolution would remain the same.
mypy/checkexpr.py
Outdated
@@ -2650,9 +2650,10 @@ def overload_arg_similarity(actual: Type, formal: Type) -> int: | |||
# NoneTyp matches anything if we're not doing strict Optional checking | |||
return 2 | |||
else: | |||
# NoneType is a subtype of object | |||
# NoneType can be promoted to object, but isn't actually a subtype (otherwise you |
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.
The comment doesn't sound quite right. Maybe something like this: "As a special case, we don't consider NoneType as a subtype of object here, as otherwise you wouldn't be able to define overloads between object and None."
@benkuhn Are you interested in finishing this up? |
Doh, completely forgot about this, sorry! Resurrecting now. |
Hmm. The previous fix appears no longer to work due to #4118. The change to make Thinking out loud about a fix: Currently, This change would break
So the behavior of warning that the signatures overlap with incompatible return types is arguably reasonable here--though much less ergonomic, of course. Ways to get around this that I can think of:
Thoughts? @JukkaL |
@benkuhn |
Oh! I didn't realize that runtime dispatch was completely abandoned. Does that mean there's no longer any reason to do type erasure in |
It's okay to have the special casing in two places, as long as each of them has a comment that cross-references the other place, and we have good test coverage. Would this be sufficient to move forward with this PR? Also, I'd like to get rid of type erasure when determining whether overload items match. However, there may be some interesting edge cases, and just turning off type erasure may not be sufficient by itself. Here's my current thinking on how things could work in the future:
Here are some steps if you want to take the route of getting rid of type erasure:
One final option is to wait until python/typing#253 has been resolved before moving forward with this PR. |
@Michael0x2a what do you think about this special case? IIUC the idea here is that it is impossible to express "anything but None". Also this PR may be outdated in view of your recent work. I think we might just close this if you agree. |
@ilevkivskyi -- it looks like the issue this PR was trying to address was ultimately related to typing descriptors, which we decided to address by just letting the If we do want to support this use case more broadly, I can implement that -- I just wish there were a way of doing it in a more principled way instead of special-casing overloads. (E.g. maybe have all non-null concrete types be a subclass of or promote to some phantom type, maybe special-case some Protocol... But I'm guessing this pretty unlikely to happen: it's unclear to me how we'd support such a type for non-strict optional users, we'd probably need to amend PEP 484 and get buy-in from other type checkers...) But yeah, I think we can just close this. I'm actually planning on simplifying |
OK, lets just close this then. @benkuhn Thanks for your efforts! |
Fixes #3757. However, I'm not at all sure that this is the right approach--please let me know if this seems reasonable, or if it's an abuse of the meaning of "overload arg similarity" and I should do something else!