Skip to content

Commit 65a068e

Browse files
authored
Speed up type argument checking (#16353)
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%.
1 parent 2aa2443 commit 65a068e

File tree

1 file changed

+13
-6
lines changed

1 file changed

+13
-6
lines changed

mypy/semanal_typeargs.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from __future__ import annotations
99

10-
from typing import Callable, Sequence
10+
from typing import Callable
1111

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

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

145145
is_error = False
146146
for (i, arg), tvar in zip(enumerate(args), type_vars):
@@ -174,7 +174,14 @@ def validate_args(
174174
arg_values = [arg]
175175
if self.check_type_var_values(name, arg_values, tvar.name, tvar.values, ctx):
176176
is_error = True
177-
if not is_subtype(arg, tvar.upper_bound):
177+
# Check against upper bound. Since it's object the vast majority of the time,
178+
# add fast path to avoid a potentially slow subtype check.
179+
upper_bound = tvar.upper_bound
180+
object_upper_bound = (
181+
type(upper_bound) is Instance
182+
and upper_bound.type.fullname == "builtins.object"
183+
)
184+
if not object_upper_bound and not is_subtype(arg, upper_bound):
178185
if self.in_type_alias_expr and isinstance(arg, TypeVarType):
179186
# Type aliases are allowed to use unconstrained type variables
180187
# error will be checked at substitution point.
@@ -184,7 +191,7 @@ def validate_args(
184191
message_registry.INVALID_TYPEVAR_ARG_BOUND.format(
185192
format_type(arg, self.options),
186193
name,
187-
format_type(tvar.upper_bound, self.options),
194+
format_type(upper_bound, self.options),
188195
),
189196
ctx,
190197
code=codes.TYPE_VAR,

0 commit comments

Comments
 (0)