Skip to content

Commit be43234

Browse files
committed
optimize is_unsafe_overlapping_overload_signatures
1 parent 35048bf commit be43234

File tree

1 file changed

+58
-55
lines changed

1 file changed

+58
-55
lines changed

mypy/checker.py

Lines changed: 58 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
Callable,
1111
Dict,
1212
Final,
13+
Generator,
1314
Generic,
1415
Iterable,
1516
Iterator,
@@ -8039,33 +8040,29 @@ def are_argument_counts_overlapping(t: CallableType, s: CallableType) -> bool:
80398040
return min_args <= max_args
80408041

80418042

8042-
def expand_callable_variants(c: CallableType) -> list[CallableType]:
8043-
"""Expand a generic callable using all combinations of type variables' values/bounds."""
8044-
for tv in c.variables:
8045-
# We need to expand self-type before other variables, because this is the only
8046-
# type variable that can have other type variables in the upper bound.
8047-
if tv.id.is_self():
8048-
c = expand_type(c, {tv.id: tv.upper_bound}).copy_modified(
8049-
variables=[v for v in c.variables if not v.id.is_self()]
8050-
)
8051-
break
8052-
8053-
if not c.is_generic():
8054-
# Fast path.
8055-
return [c]
8056-
8043+
def get_type_var_group_variants(
8044+
variables: list[TypeVarLikeType],
8045+
) -> Generator[dict[TypeVarId, Type], None, None]:
80578046
tvar_values = []
8058-
for tvar in c.variables:
8047+
for tvar in variables:
80598048
if isinstance(tvar, TypeVarType) and tvar.values:
80608049
tvar_values.append(tvar.values)
80618050
else:
80628051
tvar_values.append([tvar.upper_bound])
80638052

8064-
variants = []
80658053
for combination in itertools.product(*tvar_values):
8066-
tvar_map = {tv.id: subst for (tv, subst) in zip(c.variables, combination)}
8067-
variants.append(expand_type(c, tvar_map).copy_modified(variables=[]))
8068-
return variants
8054+
yield {tv.id: subst for (tv, subst) in zip(variables, combination)}
8055+
8056+
8057+
def expand_callable_self(c: CallableType) -> CallableType:
8058+
for tv in c.variables:
8059+
# We need to expand self-type before other variables, because this is the only
8060+
# type variable that can have other type variables in the upper bound.
8061+
if tv.id.is_self():
8062+
return expand_type(c, {tv.id: tv.upper_bound}).copy_modified(
8063+
variables=[v for v in c.variables if not v.id.is_self()]
8064+
)
8065+
return c
80698066

80708067

80718068
def is_unsafe_overlapping_overload_signatures(
@@ -8104,46 +8101,52 @@ def is_unsafe_overlapping_overload_signatures(
81048101
# Note: We repeat this check twice in both directions compensate for slight
81058102
# asymmetries in 'is_callable_compatible'.
81068103

8107-
for sig_variant in expand_callable_variants(signature):
8108-
for other_variant in expand_callable_variants(other):
8109-
# Using only expanded callables may cause false negatives, we can add
8110-
# more variants (e.g. using inference between callables) in the future.
8111-
if is_subset_no_promote(sig_variant.ret_type, other_variant.ret_type):
8112-
continue
8113-
if not (
8114-
is_callable_compatible(
8115-
sig_variant,
8116-
other_variant,
8117-
is_compat=is_overlapping_types_for_overload,
8118-
check_args_covariantly=False,
8119-
is_proper_subtype=False,
8120-
is_compat_return=lambda l, r: not is_subset_no_promote(l, r),
8121-
allow_partial_overlap=True,
8122-
)
8123-
or is_callable_compatible(
8124-
other_variant,
8125-
sig_variant,
8126-
is_compat=is_overlapping_types_for_overload,
8127-
check_args_covariantly=True,
8128-
is_proper_subtype=False,
8129-
is_compat_return=lambda l, r: not is_subset_no_promote(r, l),
8130-
allow_partial_overlap=True,
8131-
)
8132-
):
8133-
continue
8134-
# Using the same `allow_partial_overlap` flag as before, can cause false
8135-
# negatives in case where star argument is used in a catch-all fallback overload.
8136-
# But again, practicality beats purity here.
8137-
if not partial_only or not is_callable_compatible(
8104+
# We need to expand self-type before other variables, because this is the only
8105+
# type variable that can have other type variables in the upper bound.
8106+
signature = expand_callable_self(signature)
8107+
other = expand_callable_self(other)
8108+
8109+
all_variables = {v.id: v for v in signature.variables} | {v.id: v for v in other.variables}
8110+
for tvar_map in get_type_var_group_variants(all_variables.values()):
8111+
sig_variant = expand_type(signature, tvar_map).copy_modified(variables=[])
8112+
other_variant = expand_type(other, tvar_map).copy_modified(variables=[])
8113+
8114+
if is_subset_no_promote(sig_variant.ret_type, other_variant.ret_type):
8115+
continue
8116+
if not (
8117+
is_callable_compatible(
8118+
sig_variant,
8119+
other_variant,
8120+
is_compat=is_overlapping_types_for_overload,
8121+
check_args_covariantly=False,
8122+
is_proper_subtype=False,
8123+
is_compat_return=lambda l, r: not is_subset_no_promote(l, r),
8124+
allow_partial_overlap=True,
8125+
)
8126+
or is_callable_compatible(
81388127
other_variant,
81398128
sig_variant,
8140-
is_compat=is_subset_no_promote,
8129+
is_compat=is_overlapping_types_for_overload,
81418130
check_args_covariantly=True,
81428131
is_proper_subtype=False,
8143-
ignore_return=True,
8132+
is_compat_return=lambda l, r: not is_subset_no_promote(r, l),
81448133
allow_partial_overlap=True,
8145-
):
8146-
return True
8134+
)
8135+
):
8136+
continue
8137+
# Using the same `allow_partial_overlap` flag as before, can cause false
8138+
# negatives in case where star argument is used in a catch-all fallback overload.
8139+
# But again, practicality beats purity here.
8140+
if not partial_only or not is_callable_compatible(
8141+
other_variant,
8142+
sig_variant,
8143+
is_compat=is_subset_no_promote,
8144+
check_args_covariantly=True,
8145+
is_proper_subtype=False,
8146+
ignore_return=True,
8147+
allow_partial_overlap=True,
8148+
):
8149+
return True
81478150
return False
81488151

81498152

0 commit comments

Comments
 (0)