From 99892be88c08eb6423f7e500840673625272513f Mon Sep 17 00:00:00 2001 From: pine919 Date: Thu, 5 Feb 2026 14:13:26 +0000 Subject: [PATCH 1/3] fix: Type of instance variable narrowed incorrectly #20736 --- mypy/checker.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mypy/checker.py b/mypy/checker.py index 396aee8d2503..6998a19b7400 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -8321,6 +8321,18 @@ def conditional_types( proposed_type = make_simplified_union(proposed_items) if isinstance(proper_type, AnyType): + # When current type is Any, we need to be careful about narrowing. + # For isinstance checks, proposed_type is typically an Instance, and we should narrow. + # For identity checks (is/is not), proposed_type is typically a FunctionLike (type object), + # and we should NOT narrow because Any could be anything. + proposed_proper = get_proper_type(proposed_type) + if ( + isinstance(proposed_proper, (FunctionLike, Overloaded)) + and proposed_proper.is_type_obj() + ): + # Identity check against a type object - don't narrow Any + return current_type, current_type + # isinstance check or other cases - narrow as before return proposed_type, current_type if isinstance(proposed_type, AnyType): # We don't really know much about the proposed type, so we shouldn't From 3a104433cddb536733ed7830fb62ec2a39f48137 Mon Sep 17 00:00:00 2001 From: pine919 Date: Thu, 5 Feb 2026 14:33:33 +0000 Subject: [PATCH 2/3] fix: added test code --- test-data/unit/check-narrowing.test | 34 +++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index 117d0e72ed79..c4eef23bf059 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -3439,12 +3439,12 @@ else: from typing import Any def foo(y: object): - if y.__class__ == int: + if y.__class__ == int: # E: "object" has no attribute "__class__" reveal_type(y) # N: Revealed type is "builtins.int" else: reveal_type(y) # N: Revealed type is "builtins.object" - if y.__class__ is int: + if y.__class__ is int: # E: "object" has no attribute "__class__" reveal_type(y) # N: Revealed type is "builtins.int" else: reveal_type(y) # N: Revealed type is "builtins.object" @@ -3460,6 +3460,36 @@ def bar(y: Any): reveal_type(y) # N: Revealed type is "builtins.int" else: reveal_type(y) # N: Revealed type is "Any" + + +[case testAnyIdentityTypeObjectNoNarrowing] +# flags: --strict-equality +from typing import Any + +class A: + foo: Any + +def f(x: Any, a: A) -> None: + if x is list: + reveal_type(x) # N: Revealed type is "Any" + else: + reveal_type(x) # N: Revealed type is "Any" + + if x is not list: + reveal_type(x) # N: Revealed type is "Any" + else: + reveal_type(x) # N: Revealed type is "Any" + + if a.foo is list: + reveal_type(a.foo) # N: Revealed type is "Any" + else: + reveal_type(a.foo) # N: Revealed type is "Any" + + if a.foo is not list: + a.foo = [] + + reveal_type(a.foo) # N: Revealed type is "Any" + a.foo.append(1) [builtins fixtures/dict-full.pyi] [case testNarrowTypeVarType] From c2821bfc3d848c2e67cafe36a26a677defd2799e Mon Sep 17 00:00:00 2001 From: pine919 Date: Thu, 5 Feb 2026 14:52:37 +0000 Subject: [PATCH 3/3] checker: fix redundant get_proper_type call --- mypy/checker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/checker.py b/mypy/checker.py index 6998a19b7400..fcdd56c57bce 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -8325,7 +8325,7 @@ def conditional_types( # For isinstance checks, proposed_type is typically an Instance, and we should narrow. # For identity checks (is/is not), proposed_type is typically a FunctionLike (type object), # and we should NOT narrow because Any could be anything. - proposed_proper = get_proper_type(proposed_type) + proposed_proper = proposed_type if ( isinstance(proposed_proper, (FunctionLike, Overloaded)) and proposed_proper.is_type_obj()