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

bool/float treated inconsistently with bool/int and int/float, causing inconsistent Union simplification #1850

Closed
ajprax opened this issue Jul 12, 2016 · 9 comments
Labels

Comments

@ajprax
Copy link

ajprax commented Jul 12, 2016

l = [] # type: List[Union[bool, int, float]]
causes "error: Incompatible types in assignment (expression has type List[float], variable has type List[Union[bool, int, float]])"

I also tried

l1 = [] # type: List[Union[bool, int]]
l2 = [] # type: List[Union[bool, float]]
l3 = [] # type: List[Union[int, float]]
x = True # type: Union[bool, int, float]

and they all pass.

@rwbarton
Copy link
Contributor

I looked into this because I thought it might have been something I broke. Technically it was exposed by one of my changes, but the underlying issue is older. mypy thinks that bool is a subtype of int, and that int is a subtype of float, but that bool isn't a subtype of float. That's ultimately the reason that you only see this behavior when all three types are involved.

I think maybe the right thing to do here is copy a class's _promote attribute into its derived classes? Not sure.

Not really bugs, but also contributing to make this issue visible are:

  • mypy doesn't internally simplify unions that appear in type comments. It seems like it might as well; any reason not to?
  • apply_generic_arguments could exit early when the list of arguments to apply is empty, saving a traversal of the callable type. (What happened here is that expand_type simplified the union, but then it was no longer equivalent to the original, because of the bug that bool is not considered a subtype of float.)

@JukkaL
Copy link
Collaborator

JukkaL commented Jul 13, 2016

Mypy should probably simplify all unions that are used internally. Having non-simplified unions is problematic, as parts of mypy likely assume that everything is simplified. I'm not sure whether we have enough information to perform simplification when doing semantic analysis of types, as this requires a full MRO and well-formed types -- we may have to do it on demand when we read types during type checking.

Simplifying annotations might be a little unintuitive for users, as an annotation like Union[bool, int] would get turned into just int. Perhaps mypy could give a warning if a type annotation has a union type that can be simplified? However, users may want to give redundant information in unions for documentation purposes, so even this might not be a good option. At least making it clear in the documentation how this works sounds like a good idea.

@gvanrossum
Copy link
Member

gvanrossum commented Jul 13, 2016

I recall a discussion with @ddfisher where he was against always simplifying unions. (I can't find the specific issue -- maybe it was IRL. :-)

@ddfisher
Copy link
Collaborator

I thought it was just that there was some bug with union simplification that was problematic?

@rwbarton
Copy link
Contributor

In any case, not simplifying unions should generally be harmless. The real issue here is that bool is not considered a subtype of float.

@ddfisher
Copy link
Collaborator

I think this is a decent example of where a fully fleshed-out formalism for mypy would be useful.

@gnprice gnprice changed the title Inconsistent Union simplification bool/float treated inconsistently with bool/int and int/float, causing inconsistent Union simplification Jul 14, 2016
@gnprice
Copy link
Collaborator

gnprice commented Jul 14, 2016

I've retitled the issue to reflect Reid's observation.

@PedanticHacker
Copy link

mypy can't even infer the proper return type if the annotation is as simple as -> bool | int. It always returns the return type as an int, even though the actual return value is clearly a bool (in my case it was the value of False). Until this day, I wasn't able to understand why mypy can't infer a bool if the return type is a union bool | int.

@hauntsaninja
Copy link
Collaborator

bool is a subclass of int, so bool is an int. In what way do you expect a return type of bool | int to be different from int?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

8 participants