Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Prev Previous commit
Next Next commit
Fix couple more bugs exposed by the PR
  • Loading branch information
ilevkivskyi committed Jun 9, 2024
commit 52014c754bd721b1456e82982703fe94a5d5c6df
16 changes: 14 additions & 2 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -791,9 +791,21 @@ def check_overlapping_overloads(self, defn: OverloadedFuncDef) -> None:
if impl_type is not None:
assert defn.impl is not None

# This is what we want from implementation, it should accept all arguments
# of an overload, but the return types should go the opposite way.
if is_callable_compatible(
impl_type,
sig1,
is_compat=is_subtype,
is_proper_subtype=False,
is_compat_return=lambda l, r: is_subtype(r, l),
):
continue
# If the above check didn't work, we repeat some key steps in
# is_callable_compatible() to give a better error message.

# We perform a unification step that's very similar to what
# 'is_callable_compatible' would have done if we had set
# 'unify_generics' to True -- the only difference is that
# 'is_callable_compatible' does -- the only difference is that
# we check and see if the impl_type's return value is a
# *supertype* of the overload alternative, not a *subtype*.
#
Expand Down
30 changes: 22 additions & 8 deletions mypy/typeops.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,11 @@ def type_object_type_from_function(
# ...
#
# We need to map B's __init__ to the type (List[T]) -> None.
signature = bind_self(signature, original_type=default_self, is_classmethod=is_new)
signature = bind_self(
# Explicit instance self annotations have special handling in class_callable(),
# we don't need to bind any type variables in them if they are generic.
signature, original_type=default_self, is_classmethod=is_new, ignore_instances=True
)
signature = cast(FunctionLike, map_type_from_supertype(signature, info, def_info))

special_sig: str | None = None
Expand Down Expand Up @@ -244,7 +248,9 @@ class C(D[E[T]], Generic[T]): ...
return expand_type_by_instance(typ, inst_type)


def supported_self_type(typ: ProperType, allow_callable: bool = True) -> bool:
def supported_self_type(
typ: ProperType, allow_callable: bool = True, allow_instances: bool = True
) -> bool:
"""Is this a supported kind of explicit self-types?

Currently, this means an X or Type[X], where X is an instance or
Expand All @@ -257,14 +263,19 @@ def supported_self_type(typ: ProperType, allow_callable: bool = True) -> bool:
# as well as callable self for callback protocols.
return True
return isinstance(typ, TypeVarType) or (
isinstance(typ, Instance) and typ != fill_typevars(typ.type)
allow_instances and isinstance(typ, Instance) and typ != fill_typevars(typ.type)
)


F = TypeVar("F", bound=FunctionLike)


def bind_self(method: F, original_type: Type | None = None, is_classmethod: bool = False) -> F:
def bind_self(
method: F,
original_type: Type | None = None,
is_classmethod: bool = False,
ignore_instances: bool = False,
) -> F:
"""Return a copy of `method`, with the type of its first parameter (usually
self or cls) bound to original_type.

Expand All @@ -288,9 +299,10 @@ class B(A): pass

"""
if isinstance(method, Overloaded):
return cast(
F, Overloaded([bind_self(c, original_type, is_classmethod) for c in method.items])
)
items = [
bind_self(c, original_type, is_classmethod, ignore_instances) for c in method.items
]
return cast(F, Overloaded(items))
assert isinstance(method, CallableType)
func = method
if not func.arg_types:
Expand All @@ -310,7 +322,9 @@ class B(A): pass
# this special-casing looks not very principled, there is nothing meaningful we can infer
# from such definition, since it is inherently indefinitely recursive.
allow_callable = func.name is None or not func.name.startswith("__call__ of")
if func.variables and supported_self_type(self_param_type, allow_callable=allow_callable):
if func.variables and supported_self_type(
self_param_type, allow_callable=allow_callable, allow_instances=not ignore_instances,
):
from mypy.infer import infer_type_arguments

if original_type is None:
Expand Down
25 changes: 23 additions & 2 deletions test-data/unit/check-generics.test
Original file line number Diff line number Diff line change
Expand Up @@ -3496,8 +3496,6 @@ class Proto(Protocol[P, R]):
[builtins fixtures/tuple.pyi]

[case testGenericOverloadOverlapUnion]
from lib import C
[file lib.pyi]
from typing import TypeVar, overload, Union, Generic

K = TypeVar("K")
Expand All @@ -3509,3 +3507,26 @@ class C(Generic[K, V]):
def pop(self, key: K) -> V: ...
@overload
def pop(self, key: K, default: Union[V, T] = ...) -> Union[V, T]: ...
def pop(self, key: K, default: Union[V, T] = ...) -> Union[V, T]:
...

[case testOverloadedGenericInit]
from typing import TypeVar, overload, Union, Generic

T = TypeVar("T")
S = TypeVar("S")

class Int(Generic[T]): ...
class Str(Generic[T]): ...

class C(Generic[T]):
@overload
def __init__(self: C[Int[S]], x: int, y: S) -> None: ...
@overload
def __init__(self: C[Str[S]], x: str, y: S) -> None: ...
def __init__(self, x, y) -> None: ...

def foo(x: T):
reveal_type(C) # N: Revealed type is "Overload(def [T, S] (x: builtins.int, y: S`-1) -> __main__.C[__main__.Int[S`-1]], def [T, S] (x: builtins.str, y: S`-1) -> __main__.C[__main__.Str[S`-1]])"
reveal_type(C(0, x)) # N: Revealed type is "__main__.C[__main__.Int[T`-1]]"
reveal_type(C("yes", x)) # N: Revealed type is "__main__.C[__main__.Str[T`-1]]"