|
10 | 10 | Callable, |
11 | 11 | Dict, |
12 | 12 | Final, |
| 13 | + Generator, |
13 | 14 | Generic, |
14 | 15 | Iterable, |
15 | 16 | Iterator, |
@@ -8039,33 +8040,29 @@ def are_argument_counts_overlapping(t: CallableType, s: CallableType) -> bool: |
8039 | 8040 | return min_args <= max_args |
8040 | 8041 |
|
8041 | 8042 |
|
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]: |
8057 | 8046 | tvar_values = [] |
8058 | | - for tvar in c.variables: |
| 8047 | + for tvar in variables: |
8059 | 8048 | if isinstance(tvar, TypeVarType) and tvar.values: |
8060 | 8049 | tvar_values.append(tvar.values) |
8061 | 8050 | else: |
8062 | 8051 | tvar_values.append([tvar.upper_bound]) |
8063 | 8052 |
|
8064 | | - variants = [] |
8065 | 8053 | 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 |
8069 | 8066 |
|
8070 | 8067 |
|
8071 | 8068 | def is_unsafe_overlapping_overload_signatures( |
@@ -8104,46 +8101,52 @@ def is_unsafe_overlapping_overload_signatures( |
8104 | 8101 | # Note: We repeat this check twice in both directions compensate for slight |
8105 | 8102 | # asymmetries in 'is_callable_compatible'. |
8106 | 8103 |
|
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( |
8138 | 8127 | other_variant, |
8139 | 8128 | sig_variant, |
8140 | | - is_compat=is_subset_no_promote, |
| 8129 | + is_compat=is_overlapping_types_for_overload, |
8141 | 8130 | check_args_covariantly=True, |
8142 | 8131 | is_proper_subtype=False, |
8143 | | - ignore_return=True, |
| 8132 | + is_compat_return=lambda l, r: not is_subset_no_promote(r, l), |
8144 | 8133 | 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 |
8147 | 8150 | return False |
8148 | 8151 |
|
8149 | 8152 |
|
|
0 commit comments