Skip to content

Commit

Permalink
Micro-optimize get_proper_type(s) (#14369)
Browse files Browse the repository at this point in the history
These are used a lot, so it makes sense to tune them a bit. We now avoid
allocations in a common case, when compiled.

(Various small optimizations, including these, together netted a 6%
performance improvement in self check.)
  • Loading branch information
JukkaL authored Dec 29, 2022
1 parent 8884f7d commit 52172a3
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 11 deletions.
9 changes: 4 additions & 5 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -2213,7 +2213,7 @@ def check_overload_call(
# we don't want to introduce internal inconsistencies.
unioned_result = (
make_simplified_union(list(returns), context.line, context.column),
self.combine_function_signatures(inferred_types),
self.combine_function_signatures(get_proper_types(inferred_types)),
)

# Step 3: We try checking each branch one-by-one.
Expand Down Expand Up @@ -2554,18 +2554,17 @@ def type_overrides_set(
for expr in exprs:
del self.type_overrides[expr]

def combine_function_signatures(self, types: Sequence[Type]) -> AnyType | CallableType:
def combine_function_signatures(self, types: list[ProperType]) -> AnyType | CallableType:
"""Accepts a list of function signatures and attempts to combine them together into a
new CallableType consisting of the union of all of the given arguments and return types.
If there is at least one non-callable type, return Any (this can happen if there is
an ambiguity because of Any in arguments).
"""
assert types, "Trying to merge no callables"
types = get_proper_types(types)
if not all(isinstance(c, CallableType) for c in types):
return AnyType(TypeOfAny.special_form)
callables = cast(Sequence[CallableType], types)
callables = cast("list[CallableType]", types)
if len(callables) == 1:
return callables[0]

Expand Down Expand Up @@ -3463,7 +3462,7 @@ def check_op(
# we call 'combine_function_signature' instead of just unioning the inferred
# callable types.
results_final = make_simplified_union(all_results)
inferred_final = self.combine_function_signatures(all_inferred)
inferred_final = self.combine_function_signatures(get_proper_types(all_inferred))
return results_final, inferred_final
else:
return self.check_method_call_by_name(
Expand Down
24 changes: 18 additions & 6 deletions mypy/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -2888,23 +2888,35 @@ def get_proper_type(typ: Type | None) -> ProperType | None:
typ = typ.type_guard
while isinstance(typ, TypeAliasType):
typ = typ._expand_once()
assert isinstance(typ, ProperType), typ
# TODO: store the name of original type alias on this type, so we can show it in errors.
return typ
return cast(ProperType, typ)


@overload
def get_proper_types(it: Iterable[Type]) -> list[ProperType]: # type: ignore[misc]
def get_proper_types(types: list[Type] | tuple[Type, ...]) -> list[ProperType]: # type: ignore[misc]
...


@overload
def get_proper_types(it: Iterable[Type | None]) -> list[ProperType | None]:
def get_proper_types(
types: list[Type | None] | tuple[Type | None, ...]
) -> list[ProperType | None]:
...


def get_proper_types(it: Iterable[Type | None]) -> list[ProperType] | list[ProperType | None]:
return [get_proper_type(t) for t in it]
def get_proper_types(
types: list[Type] | list[Type | None] | tuple[Type | None, ...]
) -> list[ProperType] | list[ProperType | None]:
if isinstance(types, list):
typelist = types
# Optimize for the common case so that we don't need to allocate anything
if not any(
isinstance(t, (TypeAliasType, TypeGuardedType)) for t in typelist # type: ignore[misc]
):
return cast("list[ProperType]", typelist)
return [get_proper_type(t) for t in typelist]
else:
return [get_proper_type(t) for t in types]


# We split off the type visitor base classes to another module
Expand Down

0 comments on commit 52172a3

Please sign in to comment.