Skip to content

[mypyc] Don't coerce types checked with isinstance (#811) #10245

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
merged 7 commits into from
Mar 27, 2021

Conversation

sinback
Copy link
Contributor

@sinback sinback commented Mar 25, 2021

Prevent the coercion of things whose types are checked with isinstance
to other types - such coercion is not necessary and can lead to bugs.

Also, add test for avoiding coercion of isinstance() comparators.

Fixes mypyc/mypyc#811.

Test Plan

Ensured via compiling by hand and running the example given in mypyc/mypyc#811 has been fixed; added a test for the new code path that is generated.

Prevent the coercion of things whose types are checked with isinstance
to other types - such coercion is not necessary and can lead to bugs.

Also, add test for avoiding coercion of isinstance() comparators.

Fixes mypyc/mypyc#811.
Copy link
Collaborator

@JukkaL JukkaL left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! Looks good, just some minor comments for extra polish.

# Special case for builtins.isinstance
# Prevent coercions on the thing we are checking the instance of - there is no need to coerce
# something to a new type before checking what type it is, and the coercion could lead to bugs.
builder.types[expr.args[0]] = AnyType(TypeOfAny.from_error)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be moved to within the if statement, so that if somehow there aren't any arguments, we won't get an IndexError.

[case testIsinstanceNotBoolAndIsInt]
# This test is to ensure that 'value' doesn't get coerced into a bool when we need it to be an int
def is_not_bool_and_is_int(value):
return not isinstance(value, bool) and isinstance(value, int)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to above, can you try adding an annotation (even simple one such as Any -> bool) in case it simplifies the IR?

Also, it would be good to have a run test case for this. This could go into mypyc/test-data/run-integers.test. No need to do this for the other test cases above.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to below -- to test that the bug fixed, can you switch the order of the isinstance checks?

Copy link
Collaborator

@JukkaL JukkaL left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the updates! Left a few further comments about the test cases.

@@ -157,6 +157,15 @@ def check_bitwise(x: int, y: int) -> None:
check_or(ll, rr)
check_xor(ll, rr)

[case testIsinstanceNotBoolAndIsInt]
def test_isinstance_not_bool_and_is_int(value: object) -> bool:
return not isinstance(value, bool) and isinstance(value, int)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually this already seems to work correctly. Can you switch the checks so that it will reproduce the incorrect old behavior? I.e., like this:

return isinstance(value, int) and not isinstance(value, bool)

[case testIsinstanceNotBoolAndIsInt]
# This test is to ensure that 'value' doesn't get coerced into a bool when we need it to be an int
def is_not_bool_and_is_int(value):
return not isinstance(value, bool) and isinstance(value, int)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to below -- to test that the bug fixed, can you switch the order of the isinstance checks?

@sinback
Copy link
Contributor Author

sinback commented Mar 26, 2021

Oops, good catch.

Reverse the order of comparison to check a use-case which was actually
formerly buggy.
Copy link
Collaborator

@JukkaL JukkaL left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more suggestion about a comment, then this is ready to merge.

return r6

[case testIsinstanceIntAndNotBool]
# This test is to ensure that 'value' doesn't get coerced into a bool when we need it to be an int
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm the comment is a bit unclear. Maybe something like "ensure that 'value' doesn't get coerced to int when we are checking if it's a bool, since an int can never be an instance of bool"?

@JukkaL JukkaL merged commit ac61921 into python:master Mar 27, 2021
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.

Incorrect behavior for 'isinstance(x, int) and not isinstance(x, bool)'
2 participants