Skip to content

Commit

Permalink
Speed up type argument checking (#16353)
Browse files Browse the repository at this point in the history
The upper bound is usually `object`, so add a fast path and skip a
potentially slow subtype check if that's the case. Also make type
annotations more precise.

This seems to at least speed up type checker tests, by 1-2% or so. This
also potentially speeds up self-check a bit, though probably by less
than 1%.
  • Loading branch information
JukkaL authored Oct 29, 2023
1 parent 2aa2443 commit 65a068e
Showing 1 changed file with 13 additions and 6 deletions.
19 changes: 13 additions & 6 deletions mypy/semanal_typeargs.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from __future__ import annotations

from typing import Callable, Sequence
from typing import Callable

from mypy import errorcodes as codes, message_registry
from mypy.errorcodes import ErrorCode
Expand Down Expand Up @@ -88,7 +88,7 @@ def visit_type_alias_type(self, t: TypeAliasType) -> None:
return
self.seen_aliases.add(t)
assert t.alias is not None, f"Unfixed type alias {t.type_ref}"
is_error = self.validate_args(t.alias.name, t.args, t.alias.alias_tvars, t)
is_error = self.validate_args(t.alias.name, tuple(t.args), t.alias.alias_tvars, t)
if not is_error:
# If there was already an error for the alias itself, there is no point in checking
# the expansion, most likely it will result in the same kind of error.
Expand Down Expand Up @@ -131,7 +131,7 @@ def visit_instance(self, t: Instance) -> None:
t.args = unpacked.args

def validate_args(
self, name: str, args: Sequence[Type], type_vars: list[TypeVarLikeType], ctx: Context
self, name: str, args: tuple[Type, ...], type_vars: list[TypeVarLikeType], ctx: Context
) -> bool:
if any(isinstance(v, TypeVarTupleType) for v in type_vars):
prefix = next(i for (i, v) in enumerate(type_vars) if isinstance(v, TypeVarTupleType))
Expand All @@ -140,7 +140,7 @@ def validate_args(
start, middle, end = split_with_prefix_and_suffix(
tuple(args), prefix, len(type_vars) - prefix - 1
)
args = list(start) + [TupleType(list(middle), tvt.tuple_fallback)] + list(end)
args = start + (TupleType(list(middle), tvt.tuple_fallback),) + end

is_error = False
for (i, arg), tvar in zip(enumerate(args), type_vars):
Expand Down Expand Up @@ -174,7 +174,14 @@ def validate_args(
arg_values = [arg]
if self.check_type_var_values(name, arg_values, tvar.name, tvar.values, ctx):
is_error = True
if not is_subtype(arg, tvar.upper_bound):
# Check against upper bound. Since it's object the vast majority of the time,
# add fast path to avoid a potentially slow subtype check.
upper_bound = tvar.upper_bound
object_upper_bound = (
type(upper_bound) is Instance
and upper_bound.type.fullname == "builtins.object"
)
if not object_upper_bound and not is_subtype(arg, upper_bound):
if self.in_type_alias_expr and isinstance(arg, TypeVarType):
# Type aliases are allowed to use unconstrained type variables
# error will be checked at substitution point.
Expand All @@ -184,7 +191,7 @@ def validate_args(
message_registry.INVALID_TYPEVAR_ARG_BOUND.format(
format_type(arg, self.options),
name,
format_type(tvar.upper_bound, self.options),
format_type(upper_bound, self.options),
),
ctx,
code=codes.TYPE_VAR,
Expand Down

0 comments on commit 65a068e

Please sign in to comment.