@@ -6676,7 +6676,8 @@ def narrow_type_by_identity_equality(
66766676 else :
66776677 raise AssertionError
66786678
6679- partial_type_maps = []
6679+ all_if_maps : list [TypeMap ] = []
6680+ all_else_maps : list [TypeMap ] = []
66806681
66816682 # For each narrowable index, we see what we can narrow based on each relevant target
66826683 for i in expr_indices :
@@ -6715,13 +6716,13 @@ def narrow_type_by_identity_equality(
67156716 continue
67166717
67176718 target = TypeRange (target_type , is_upper_bound = False )
6718- is_value_target = is_target_for_value_narrowing (get_proper_type (target_type ))
67196719
6720- if is_value_target :
6720+ if is_target_for_value_narrowing ( get_proper_type ( target_type )) :
67216721 if_map , else_map = conditional_types_to_typemaps (
67226722 operands [i ], * conditional_types (expanded_expr_type , [target ])
67236723 )
6724- partial_type_maps .append ((if_map , else_map ))
6724+ all_if_maps .append (if_map )
6725+ all_else_maps .append (else_map )
67256726 else :
67266727 if_map , else_map = conditional_types_to_typemaps (
67276728 operands [i ], * conditional_types (expr_type , [target ])
@@ -6731,8 +6732,7 @@ def narrow_type_by_identity_equality(
67316732 # However, for non-value targets, we cannot do this narrowing,
67326733 # and so we ignore else_map
67336734 # e.g. if (x: str | None) != (y: str), we cannot narrow x to None
6734- if if_map :
6735- partial_type_maps .append ((if_map , {}))
6735+ all_if_maps .append (if_map )
67366736
67376737 # Handle narrowing for operands with custom __eq__ methods specially
67386738 # In most cases, we won't be able to do any narrowing
@@ -6754,14 +6754,12 @@ def narrow_type_by_identity_equality(
67546754 if should_coerce_literals :
67556755 target_type = coerce_to_literal (target_type )
67566756 target = TypeRange (target_type , is_upper_bound = False )
6757- is_value_target = is_target_for_value_narrowing (get_proper_type (target_type ))
6758-
6759- if is_value_target :
6757+ if is_target_for_value_narrowing (get_proper_type (target_type )):
67606758 if_map , else_map = conditional_types_to_typemaps (
67616759 operands [i ], * conditional_types (expr_type , [target ])
67626760 )
67636761 if else_map :
6764- partial_type_maps .append (({}, else_map ) )
6762+ all_else_maps .append (else_map )
67656763 continue
67666764
67676765 # If our operand with custom __eq__ is a union, where only some members of the union
@@ -6794,18 +6792,8 @@ def narrow_type_by_identity_equality(
67946792 if is_value_target :
67956793 or_else_maps .append (else_map )
67966794
6797- final_if_map : TypeMap = {}
6798- final_else_map : TypeMap = {}
6799- if or_if_maps :
6800- final_if_map = or_if_maps [0 ]
6801- for if_map in or_if_maps [1 :]:
6802- final_if_map = or_conditional_maps (final_if_map , if_map )
6803- if or_else_maps :
6804- final_else_map = or_else_maps [0 ]
6805- for else_map in or_else_maps [1 :]:
6806- final_else_map = or_conditional_maps (final_else_map , else_map )
6807-
6808- partial_type_maps .append ((final_if_map , final_else_map ))
6795+ all_if_maps .append (reduce_or_conditional_type_maps (or_if_maps ))
6796+ all_else_maps .append (reduce_or_conditional_type_maps (or_else_maps ))
68096797
68106798 # Handle narrowing for comparisons that produce additional narrowing, like
68116799 # `type(x) == T` or `x.__class__ is T`
@@ -6839,13 +6827,16 @@ def narrow_type_by_identity_equality(
68396827 if isinstance (expr , RefExpr ) and isinstance (expr .node , TypeInfo )
68406828 else False
68416829 )
6842- if not is_final :
6843- else_map = {}
6844- partial_type_maps .append ((if_map , else_map ))
6830+ all_if_maps .append (if_map )
6831+ if is_final :
6832+ # We can only narrow `type(x) == T` in the negative case if T is final
6833+ all_else_maps .append (else_map )
68456834
68466835 # We will not have duplicate entries in our type maps if we only have two operands,
68476836 # so we can skip running meets on the intersections
6848- return reduce_conditional_maps (partial_type_maps , use_meet = len (operands ) > 2 )
6837+ if_map = reduce_and_conditional_type_maps (all_if_maps , use_meet = len (operands ) > 2 )
6838+ else_map = reduce_or_conditional_type_maps (all_else_maps )
6839+ return if_map , else_map
68496840
68506841 def propagate_up_typemap_info (self , new_types : TypeMap ) -> TypeMap :
68516842 """Attempts refining parent expressions of any MemberExpr or IndexExprs in new_types.
@@ -8481,7 +8472,7 @@ def builtin_item_type(tp: Type) -> Type | None:
84818472 return None
84828473
84838474
8484- def and_conditional_maps (m1 : TypeMap , m2 : TypeMap , use_meet : bool = False ) -> TypeMap :
8475+ def and_conditional_maps (m1 : TypeMap , m2 : TypeMap , * , use_meet : bool = False ) -> TypeMap :
84858476 """Calculate what information we can learn from the truth of (e1 and e2)
84868477 in terms of the information that we can learn from the truth of e1 and
84878478 the truth of e2.
@@ -8514,7 +8505,7 @@ def and_conditional_maps(m1: TypeMap, m2: TypeMap, use_meet: bool = False) -> Ty
85148505 return result
85158506
85168507
8517- def or_conditional_maps (m1 : TypeMap , m2 : TypeMap , coalesce_any : bool = False ) -> TypeMap :
8508+ def or_conditional_maps (m1 : TypeMap , m2 : TypeMap , * , coalesce_any : bool = False ) -> TypeMap :
85188509 """Calculate what information we can learn from the truth of (e1 or e2)
85198510 in terms of the information that we can learn from the truth of e1 and
85208511 the truth of e2. If coalesce_any is True, consider Any a supertype when
@@ -8579,6 +8570,30 @@ def reduce_conditional_maps(
85798570 return final_if_map , final_else_map
85808571
85818572
8573+ def reduce_or_conditional_type_maps (ms : list [TypeMap ]) -> TypeMap :
8574+ """Reduces a list of TypeMaps into a single TypeMap by "or"-ing them together."""
8575+ if len (ms ) == 0 :
8576+ return {}
8577+ if len (ms ) == 1 :
8578+ return ms [0 ]
8579+ result = ms [0 ]
8580+ for m in ms [1 :]:
8581+ result = or_conditional_maps (result , m )
8582+ return result
8583+
8584+
8585+ def reduce_and_conditional_type_maps (ms : list [TypeMap ], * , use_meet : bool ) -> TypeMap :
8586+ """Reduces a list of TypeMaps into a single TypeMap by "and"-ing them together."""
8587+ if len (ms ) == 0 :
8588+ return {}
8589+ if len (ms ) == 1 :
8590+ return ms [0 ]
8591+ result = ms [0 ]
8592+ for m in ms [1 :]:
8593+ result = and_conditional_maps (result , m , use_meet = use_meet )
8594+ return result
8595+
8596+
85828597BUILTINS_CUSTOM_EQ_CHECKS : Final = {
85838598 "builtins.bytes" ,
85848599 "builtins.bytearray" ,
0 commit comments