Skip to content

Commit

Permalink
Optimize type parameter checks in subtype checking
Browse files Browse the repository at this point in the history
Avoid the use of a nested function, which are a bit slow when compiled
with mypyc. Also avoid a callable value and instead call a function
directly, which allows using faster native calls.

Based on a quick experiment, this speeds up self check by about 3%.

This addresses some of the slowdown introduced in #13303.
  • Loading branch information
JukkaL committed Dec 20, 2022
1 parent c414464 commit bdb32f7
Showing 1 changed file with 28 additions and 32 deletions.
60 changes: 28 additions & 32 deletions mypy/subtypes.py
Expand Up @@ -330,34 +330,28 @@ def check_item(left: Type, right: Type, subtype_context: SubtypeContext) -> bool


def check_type_parameter(
lefta: Type, righta: Type, variance: int, proper_subtype: bool, subtype_context: SubtypeContext
left: Type, right: Type, variance: int, proper_subtype: bool, subtype_context: SubtypeContext
) -> bool:
def check(left: Type, right: Type) -> bool:
return (
is_proper_subtype(left, right, subtype_context=subtype_context)
if proper_subtype
else is_subtype(left, right, subtype_context=subtype_context)
)

if variance == COVARIANT:
return check(lefta, righta)
if proper_subtype:
return is_proper_subtype(left, right, subtype_context=subtype_context)
else:
return is_subtype(left, right, subtype_context=subtype_context)
elif variance == CONTRAVARIANT:
return check(righta, lefta)
if proper_subtype:
return is_proper_subtype(right, left, subtype_context=subtype_context)
else:
return is_subtype(right, left, subtype_context=subtype_context)
else:
if proper_subtype:
# We pass ignore_promotions=False because it is a default for subtype checks.
# The actual value will be taken from the subtype_context, and it is whatever
# the original caller passed.
return is_same_type(
lefta, righta, ignore_promotions=False, subtype_context=subtype_context
left, right, ignore_promotions=False, subtype_context=subtype_context
)
return is_equivalent(lefta, righta, subtype_context=subtype_context)


def ignore_type_parameter(
lefta: Type, righta: Type, variance: int, proper_subtype: bool, subtype_context: SubtypeContext
) -> bool:
return True
else:
return is_equivalent(left, right, subtype_context=subtype_context)


class SubtypeVisitor(TypeVisitor[bool]):
Expand All @@ -366,9 +360,6 @@ def __init__(self, right: Type, subtype_context: SubtypeContext, proper_subtype:
self.orig_right = right
self.proper_subtype = proper_subtype
self.subtype_context = subtype_context
self.check_type_parameter = (
ignore_type_parameter if subtype_context.ignore_type_params else check_type_parameter
)
self.options = subtype_context.options
self._subtype_kind = SubtypeVisitor.build_subtype_kind(subtype_context, proper_subtype)

Expand Down Expand Up @@ -572,17 +563,22 @@ def check_mixed(
)
else:
type_params = zip(t.args, right.args, right.type.defn.type_vars)
for lefta, righta, tvar in type_params:
if isinstance(tvar, TypeVarType):
if not self.check_type_parameter(
lefta, righta, tvar.variance, self.proper_subtype, self.subtype_context
):
nominal = False
else:
if not self.check_type_parameter(
lefta, righta, COVARIANT, self.proper_subtype, self.subtype_context
):
nominal = False
if not self.subtype_context.ignore_type_params:
for lefta, righta, tvar in type_params:
if isinstance(tvar, TypeVarType):
if not check_type_parameter(
lefta,
righta,
tvar.variance,
self.proper_subtype,
self.subtype_context,
):
nominal = False
else:
if not check_type_parameter(
lefta, righta, COVARIANT, self.proper_subtype, self.subtype_context
):
nominal = False
if nominal:
TypeState.record_subtype_cache_entry(self._subtype_kind, left, right)
return nominal
Expand Down

0 comments on commit bdb32f7

Please sign in to comment.