From 4b5797d18671119d089f03baeaea329123346192 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 16 Aug 2019 18:22:42 +0100 Subject: [PATCH 1/3] Always erase last known value from instances when usinh binder --- mypy/binder.py | 5 +++++ test-data/unit/check-literal.test | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/mypy/binder.py b/mypy/binder.py index 45855aa1b9d5a..9eae67af43066 100644 --- a/mypy/binder.py +++ b/mypy/binder.py @@ -10,6 +10,7 @@ from mypy.subtypes import is_subtype from mypy.join import join_simple from mypy.sametypes import is_same_type +from mypy.erasetype import remove_instance_last_known_values from mypy.nodes import Expression, Var, RefExpr from mypy.literals import Key, literal, literal_hash, subkeys from mypy.nodes import IndexExpr, MemberExpr, NameExpr @@ -251,6 +252,10 @@ def assign_type(self, expr: Expression, type = get_proper_type(type) declared_type = get_proper_type(declared_type) + # We should erase last known value in binder, because if we are using it, + # it means that the target is not final, and therefore can't hold a literal. + type = remove_instance_last_known_values(type) + if self.type_assignments is not None: # We are in a multiassign from union, defer the actual binding, # just collect the types. diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index 94a85c0f50975..53ac4d0be89d1 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -3067,3 +3067,17 @@ reveal_type(Test3.FOO.name) # N: Revealed type is 'builtins.str' reveal_type(Test4.FOO.name) # N: Revealed type is 'builtins.str' reveal_type(Test5.FOO.name) # N: Revealed type is 'builtins.str' [out] + +[case testLiteralBinderLastValueErased] +# mypy: strict-equality + +from typing_extensions import Literal + +def takes_three(x: Literal[3]) -> None: ... +x: object +x = 3 + +takes_three(x) # E: Argument 1 to "takes_three" has incompatible type "int"; expected "Literal[3]" +if x == 2: # OK + ... +[builtins fixtures/bool.pyi] From 44ec560d83c6dc80d9d7b37deeb82ced4a17fce4 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 16 Aug 2019 18:47:33 +0100 Subject: [PATCH 2/3] Fix self-check --- mypy/binder.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mypy/binder.py b/mypy/binder.py index 9eae67af43066..109fef25ce6ac 100644 --- a/mypy/binder.py +++ b/mypy/binder.py @@ -249,13 +249,13 @@ def assign_type(self, expr: Expression, type: Type, declared_type: Optional[Type], restrict_any: bool = False) -> None: - type = get_proper_type(type) - declared_type = get_proper_type(declared_type) - # We should erase last known value in binder, because if we are using it, # it means that the target is not final, and therefore can't hold a literal. type = remove_instance_last_known_values(type) + type = get_proper_type(type) + declared_type = get_proper_type(declared_type) + if self.type_assignments is not None: # We are in a multiassign from union, defer the actual binding, # just collect the types. From 881524b4349f86324d3d0e7bff95c02f7c3beb4c Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 16 Aug 2019 19:05:56 +0100 Subject: [PATCH 3/3] Do the same for partial types --- mypy/checker.py | 2 ++ test-data/unit/check-literal.test | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/mypy/checker.py b/mypy/checker.py index 36907b43a94a9..1a25126621936 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1954,6 +1954,8 @@ def check_assignment(self, lvalue: Lvalue, rvalue: Expression, infer_lvalue_type partial_types = self.find_partial_types(var) if partial_types is not None: if not self.current_node_deferred: + # Partial type can't be final, so strip any literal values. + rvalue_type = remove_instance_last_known_values(rvalue_type) inferred_type = UnionType.make_simplified_union( [rvalue_type, NoneType()]) self.set_inferred_type(var, lvalue, inferred_type) diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index 53ac4d0be89d1..03e93857ec38a 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -3081,3 +3081,15 @@ takes_three(x) # E: Argument 1 to "takes_three" has incompatible type "int"; ex if x == 2: # OK ... [builtins fixtures/bool.pyi] + +[case testLiteralBinderLastValueErasedPartialTypes] +# mypy: strict-equality + +def test() -> None: + x = None + if bool(): + x = 1 + + if x == 2: # OK + ... +[builtins fixtures/bool.pyi]