Skip to content

Commit 0afa33d

Browse files
authored
Mypy micro-optimizations (batch 3/3) (#19770)
Several mypy micro-optimizations. Together with batches 1 and 2 these improve self check performance by 1.8%. This mostly avoids some allocations of nested function objects.
1 parent b1b8b0c commit 0afa33d

File tree

1 file changed

+55
-43
lines changed

1 file changed

+55
-43
lines changed

mypy/meet.py

Lines changed: 55 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,19 @@ def is_none_object_overlap(t1: ProperType, t2: ProperType) -> bool:
306306
)
307307

308308

309+
def are_related_types(
310+
left: Type, right: Type, *, proper_subtype: bool, ignore_promotions: bool
311+
) -> bool:
312+
if proper_subtype:
313+
return is_proper_subtype(
314+
left, right, ignore_promotions=ignore_promotions
315+
) or is_proper_subtype(right, left, ignore_promotions=ignore_promotions)
316+
else:
317+
return is_subtype(left, right, ignore_promotions=ignore_promotions) or is_subtype(
318+
right, left, ignore_promotions=ignore_promotions
319+
)
320+
321+
309322
def is_overlapping_types(
310323
left: Type,
311324
right: Type,
@@ -329,27 +342,13 @@ def is_overlapping_types(
329342

330343
if seen_types is None:
331344
seen_types = set()
332-
if (left, right) in seen_types:
345+
elif (left, right) in seen_types:
333346
return True
334347
if isinstance(left, TypeAliasType) and isinstance(right, TypeAliasType):
335348
seen_types.add((left, right))
336349

337350
left, right = get_proper_types((left, right))
338351

339-
def _is_overlapping_types(left: Type, right: Type) -> bool:
340-
"""Encode the kind of overlapping check to perform.
341-
342-
This function mostly exists, so we don't have to repeat keyword arguments everywhere.
343-
"""
344-
return is_overlapping_types(
345-
left,
346-
right,
347-
ignore_promotions=ignore_promotions,
348-
prohibit_none_typevar_overlap=prohibit_none_typevar_overlap,
349-
overlap_for_overloads=overlap_for_overloads,
350-
seen_types=seen_types.copy(),
351-
)
352-
353352
# We should never encounter this type.
354353
if isinstance(left, PartialType) or isinstance(right, PartialType):
355354
assert False, "Unexpectedly encountered partial type"
@@ -399,13 +398,9 @@ def _is_overlapping_types(left: Type, right: Type) -> bool:
399398
if is_none_object_overlap(left, right) or is_none_object_overlap(right, left):
400399
return False
401400

402-
def _is_subtype(left: Type, right: Type) -> bool:
403-
if overlap_for_overloads:
404-
return is_proper_subtype(left, right, ignore_promotions=ignore_promotions)
405-
else:
406-
return is_subtype(left, right, ignore_promotions=ignore_promotions)
407-
408-
if _is_subtype(left, right) or _is_subtype(right, left):
401+
if are_related_types(
402+
left, right, proper_subtype=overlap_for_overloads, ignore_promotions=ignore_promotions
403+
):
409404
return True
410405

411406
# See the docstring for 'get_possible_variants' for more info on what the
@@ -428,6 +423,20 @@ def _is_subtype(left: Type, right: Type) -> bool:
428423
if is_none_typevarlike_overlap(left, right) or is_none_typevarlike_overlap(right, left):
429424
return False
430425

426+
def _is_overlapping_types(left: Type, right: Type) -> bool:
427+
"""Encode the kind of overlapping check to perform.
428+
429+
This function mostly exists, so we don't have to repeat keyword arguments everywhere.
430+
"""
431+
return is_overlapping_types(
432+
left,
433+
right,
434+
ignore_promotions=ignore_promotions,
435+
prohibit_none_typevar_overlap=prohibit_none_typevar_overlap,
436+
overlap_for_overloads=overlap_for_overloads,
437+
seen_types=seen_types.copy(),
438+
)
439+
431440
if (
432441
len(left_possible) > 1
433442
or len(right_possible) > 1
@@ -483,27 +492,28 @@ def _is_subtype(left: Type, right: Type) -> bool:
483492
if isinstance(left, TypeType) and isinstance(right, TypeType):
484493
return _is_overlapping_types(left.item, right.item)
485494

486-
def _type_object_overlap(left: Type, right: Type) -> bool:
487-
"""Special cases for type object types overlaps."""
488-
# TODO: these checks are a bit in gray area, adjust if they cause problems.
489-
left, right = get_proper_types((left, right))
490-
# 1. Type[C] vs Callable[..., C] overlap even if the latter is not class object.
491-
if isinstance(left, TypeType) and isinstance(right, CallableType):
492-
return _is_overlapping_types(left.item, right.ret_type)
493-
# 2. Type[C] vs Meta, where Meta is a metaclass for C.
494-
if isinstance(left, TypeType) and isinstance(right, Instance):
495-
if isinstance(left.item, Instance):
496-
left_meta = left.item.type.metaclass_type
497-
if left_meta is not None:
498-
return _is_overlapping_types(left_meta, right)
499-
# builtins.type (default metaclass) overlaps with all metaclasses
500-
return right.type.has_base("builtins.type")
501-
elif isinstance(left.item, AnyType):
502-
return right.type.has_base("builtins.type")
503-
# 3. Callable[..., C] vs Meta is considered below, when we switch to fallbacks.
504-
return False
505-
506495
if isinstance(left, TypeType) or isinstance(right, TypeType):
496+
497+
def _type_object_overlap(left: Type, right: Type) -> bool:
498+
"""Special cases for type object types overlaps."""
499+
# TODO: these checks are a bit in gray area, adjust if they cause problems.
500+
left, right = get_proper_types((left, right))
501+
# 1. Type[C] vs Callable[..., C] overlap even if the latter is not class object.
502+
if isinstance(left, TypeType) and isinstance(right, CallableType):
503+
return _is_overlapping_types(left.item, right.ret_type)
504+
# 2. Type[C] vs Meta, where Meta is a metaclass for C.
505+
if isinstance(left, TypeType) and isinstance(right, Instance):
506+
if isinstance(left.item, Instance):
507+
left_meta = left.item.type.metaclass_type
508+
if left_meta is not None:
509+
return _is_overlapping_types(left_meta, right)
510+
# builtins.type (default metaclass) overlaps with all metaclasses
511+
return right.type.has_base("builtins.type")
512+
elif isinstance(left.item, AnyType):
513+
return right.type.has_base("builtins.type")
514+
# 3. Callable[..., C] vs Meta is considered below, when we switch to fallbacks.
515+
return False
516+
507517
return _type_object_overlap(left, right) or _type_object_overlap(right, left)
508518

509519
if isinstance(left, Parameters) and isinstance(right, Parameters):
@@ -564,7 +574,9 @@ def _type_object_overlap(left: Type, right: Type) -> bool:
564574
if isinstance(left, Instance) and isinstance(right, Instance):
565575
# First we need to handle promotions and structural compatibility for instances
566576
# that came as fallbacks, so simply call is_subtype() to avoid code duplication.
567-
if _is_subtype(left, right) or _is_subtype(right, left):
577+
if are_related_types(
578+
left, right, proper_subtype=overlap_for_overloads, ignore_promotions=ignore_promotions
579+
):
568580
return True
569581

570582
if right.type.fullname == "builtins.int" and left.type.fullname in MYPYC_NATIVE_INT_NAMES:

0 commit comments

Comments
 (0)