Skip to content

Commit 9e50b14

Browse files
committed
Compare type ranges using is_same_type_ranges
1 parent 1189e88 commit 9e50b14

File tree

2 files changed

+32
-27
lines changed

2 files changed

+32
-27
lines changed

mypy/checker.py

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@
167167
is_more_precise,
168168
is_proper_subtype,
169169
is_same_type,
170+
is_same_type_ranges,
170171
is_subtype,
171172
restrict_subtype_away,
172173
unify_generic_callable,
@@ -6252,9 +6253,12 @@ def is_type_call(expr: CallExpr) -> bool:
62526253
current_type = self.get_isinstance_type(expr)
62536254
if current_type is None:
62546255
continue
6255-
if type_being_compared is not None and type_being_compared != current_type:
6256+
if type_being_compared is not None and not is_same_type_ranges(
6257+
type_being_compared, current_type
6258+
):
62566259
# It doesn't really make sense to have several types being
62576260
# compared to the output of type (like type(x) == int == str)
6261+
# unless they are the same (like type(x) == float == float)
62586262
# because whether that's true is solely dependent on what the
62596263
# types being compared are, so we don't try to narrow types any
62606264
# further because we can't really get any information about the
@@ -6269,11 +6273,10 @@ def is_type_call(expr: CallExpr) -> bool:
62696273
return {}, {}
62706274

62716275
if type_being_compared is None:
6272-
# TODO: use more accurate lower bound analysis
6273-
least_type = self.least_type([self.lookup_type(expr) for expr in exprs_in_type_calls])
6274-
type_being_compared = (
6275-
None if least_type is None else [TypeRange(least_type, is_upper_bound=True)]
6276+
least_type = reduce(
6277+
meet_types, (self.lookup_type(expr) for expr in exprs_in_type_calls)
62766278
)
6279+
type_being_compared = [TypeRange(least_type, is_upper_bound=True)]
62776280

62786281
if_maps: list[TypeMap] = []
62796282
else_maps: list[TypeMap] = []
@@ -6308,28 +6311,6 @@ def combine_maps(list_maps: list[TypeMap]) -> TypeMap:
63086311
else_map = {}
63096312
return if_map, else_map
63106313

6311-
def least_type(self, types: list[Type]) -> Type | None:
6312-
"""Find the type of which all other types are supertypes.
6313-
`Any` types are i
6314-
For example, `least_type([dict, defaultdict, object]) => dict`.
6315-
However, `least_type[int, str]) => None`.
6316-
6317-
It would be better if we could represent an intersection of types.
6318-
6319-
For example, consider `s: str` and `i: int`.
6320-
`type(s) == type(i)` implies `s: str & int` and `i: str & int`,
6321-
even though `s: object` and `i: object` also hold.
6322-
"""
6323-
types = [typ for typ in types if not isinstance(typ, AnyType)]
6324-
if not types:
6325-
return None
6326-
least_type = reduce(lambda t1, t2: t1 if is_subtype(t1, t2) else t2, types)
6327-
6328-
if all(typ is least_type or is_subtype(least_type, typ) for typ in types):
6329-
# Ensure that this is a least type
6330-
return least_type
6331-
return None
6332-
63336314
def find_isinstance_check(
63346315
self, node: Expression, *, in_boolean_context: bool = True
63356316
) -> tuple[TypeMap, TypeMap]:

mypy/subtypes.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import mypy.applytype
99
import mypy.constraints
1010
import mypy.typeops
11+
from mypy.checker_shared import TypeRange
1112
from mypy.checker_state import checker_state
1213
from mypy.erasetype import erase_type
1314
from mypy.expandtype import (
@@ -255,6 +256,29 @@ def is_equivalent(
255256
)
256257

257258

259+
def is_same_type_ranges(
260+
a: list[TypeRange],
261+
b: list[TypeRange],
262+
ignore_promotions: bool = True,
263+
subtype_context: SubtypeContext | None = None,
264+
) -> bool:
265+
return len(a) == len(b) and all(
266+
is_same_type_range(a, b, ignore_promotions, subtype_context)
267+
for a, b in zip(a, b, strict=True)
268+
)
269+
270+
271+
def is_same_type_range(
272+
a: list[TypeRange],
273+
b: list[TypeRange],
274+
ignore_promotions: bool = True,
275+
subtype_context: SubtypeContext | None = None,
276+
) -> bool:
277+
return a.is_upper_bound == b.is_upper_bound and is_same_type(
278+
a.item, b.item, ignore_promotions, subtype_context
279+
)
280+
281+
258282
def is_same_type(
259283
a: Type, b: Type, ignore_promotions: bool = True, subtype_context: SubtypeContext | None = None
260284
) -> bool:

0 commit comments

Comments
 (0)