diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 859bd6afcc6d..5244b1e18ccd 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -526,14 +526,15 @@ def analyze_var(name: str, if typ: if isinstance(typ, PartialType): return mx.chk.handle_partial_var_type(typ, mx.is_lvalue, var, mx.context) - t = get_proper_type(expand_type_by_instance(typ, itype)) if mx.is_lvalue and var.is_property and not var.is_settable_property: # TODO allow setting attributes in subclass (although it is probably an error) mx.msg.read_only_property(name, itype.type, mx.context) if mx.is_lvalue and var.is_classvar: mx.msg.cant_assign_to_classvar(name, mx.context) + t = get_proper_type(expand_type_by_instance(typ, itype)) result = t # type: Type - if var.is_initialized_in_class and isinstance(t, FunctionLike) and not t.is_type_obj(): + typ = get_proper_type(typ) + if var.is_initialized_in_class and isinstance(typ, FunctionLike) and not typ.is_type_obj(): if mx.is_lvalue: if var.is_property: if not var.is_settable_property: @@ -544,7 +545,7 @@ def analyze_var(name: str, if not var.is_staticmethod: # Class-level function objects and classmethods become bound methods: # the former to the instance, the latter to the class. - functype = t + functype = typ # Use meet to narrow original_type to the dispatched type. # For example, assume # * A.f: Callable[[A1], None] where A1 <: A (maybe A1 == A) @@ -553,15 +554,17 @@ def analyze_var(name: str, # In `x.f`, when checking `x` against A1 we assume x is compatible with A # and similarly for B1 when checking agains B dispatched_type = meet.meet_types(mx.original_type, itype) - functype = check_self_arg(functype, dispatched_type, var.is_classmethod, + signature = freshen_function_type_vars(functype) + signature = check_self_arg(signature, dispatched_type, var.is_classmethod, mx.context, name, mx.msg) - signature = bind_self(functype, mx.self_type, var.is_classmethod) + signature = bind_self(signature, mx.self_type, var.is_classmethod) + expanded_signature = get_proper_type(expand_type_by_instance(signature, itype)) if var.is_property: # A property cannot have an overloaded type => the cast is fine. - assert isinstance(signature, CallableType) - result = signature.ret_type + assert isinstance(expanded_signature, CallableType) + result = expanded_signature.ret_type else: - result = signature + result = expanded_signature else: if not var.is_ready: mx.not_ready_callback(var.name(), mx.context) diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 09a3616fb3a2..7ec6bfec8446 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -2128,3 +2128,54 @@ class B(A): def from_config(cls) -> B: return B() [builtins fixtures/classmethod.pyi] + +[case testAbstractGenericMethodInference] +from abc import ABC, abstractmethod +from typing import Callable, Generic, TypeVar + +A = TypeVar('A') +B = TypeVar('B') +C = TypeVar('C') + +class TwoTypes(Generic[A, B]): + + def __call__(self) -> B: pass + +class MakeTwoAbstract(ABC, Generic[A]): + + def __init__(self) -> None: pass + + @abstractmethod + def __call__(self, b: B) -> TwoTypes[A, B]: pass + +class MakeTwoConcrete(Generic[A]): + + def __call__(self, b: B) -> TwoTypes[A, B]: pass + + +class MakeTwoGenericSubAbstract(Generic[C], MakeTwoAbstract[C]): + + def __call__(self, b: B) -> TwoTypes[C, B]: pass + +class MakeTwoAppliedSubAbstract(MakeTwoAbstract[str]): + + def __call__(self, b: B) -> TwoTypes[str, B]: pass + +class Test(): + + def make_two(self, + mts: MakeTwoAbstract[A], + mte: MakeTwoConcrete[A], + mtgsa: MakeTwoGenericSubAbstract[A], + mtasa: MakeTwoAppliedSubAbstract) -> None: + reveal_type(mts(2)) # N: Revealed type is '__main__.TwoTypes[A`-1, builtins.int*]' + reveal_type(mte(2)) # N: Revealed type is '__main__.TwoTypes[A`-1, builtins.int*]' + reveal_type(mtgsa(2)) # N: Revealed type is '__main__.TwoTypes[A`-1, builtins.int*]' + reveal_type(mtasa(2)) # N: Revealed type is '__main__.TwoTypes[builtins.str, builtins.int*]' + reveal_type(MakeTwoConcrete[int]()('foo')) # N: Revealed type is '__main__.TwoTypes[builtins.int, builtins.str*]' + reveal_type(MakeTwoConcrete[str]()(2)) # N: Revealed type is '__main__.TwoTypes[builtins.str, builtins.int*]' + reveal_type(MakeTwoAppliedSubAbstract()('foo')) # N: Revealed type is '__main__.TwoTypes[builtins.str, builtins.str*]' + reveal_type(MakeTwoAppliedSubAbstract()(2)) # N: Revealed type is '__main__.TwoTypes[builtins.str, builtins.int*]' + reveal_type(MakeTwoGenericSubAbstract[str]()('foo')) # N: Revealed type is '__main__.TwoTypes[builtins.str, builtins.str*]' + reveal_type(MakeTwoGenericSubAbstract[str]()(2)) # N: Revealed type is '__main__.TwoTypes[builtins.str, builtins.int*]' + diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index c4d1bb3d2e53..467579a112d2 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -468,13 +468,13 @@ class B(A[Q]): a: A[int] b: B[str] reveal_type(a.g) # N: Revealed type is 'builtins.int' -reveal_type(a.gt) # N: Revealed type is 'builtins.int*' +reveal_type(a.gt) # N: Revealed type is 'builtins.int' reveal_type(a.f()) # N: Revealed type is 'builtins.int' -reveal_type(a.ft()) # N: Revealed type is '__main__.A*[builtins.int]' +reveal_type(a.ft()) # N: Revealed type is '__main__.A[builtins.int]' reveal_type(b.g) # N: Revealed type is 'builtins.int' -reveal_type(b.gt) # N: Revealed type is 'builtins.str*' +reveal_type(b.gt) # N: Revealed type is 'builtins.str' reveal_type(b.f()) # N: Revealed type is 'builtins.int' -reveal_type(b.ft()) # N: Revealed type is '__main__.B*[builtins.str]' +reveal_type(b.ft()) # N: Revealed type is '__main__.B[builtins.str]' [builtins fixtures/property.pyi] [case testSelfTypeRestrictedMethod] diff --git a/test-data/unit/lib-stub/abc.pyi b/test-data/unit/lib-stub/abc.pyi index 0b1a51c78f5c..da90b588fca3 100644 --- a/test-data/unit/lib-stub/abc.pyi +++ b/test-data/unit/lib-stub/abc.pyi @@ -2,6 +2,7 @@ from typing import Type, Any, TypeVar T = TypeVar('T', bound=Type[Any]) +class ABC(type): pass class ABCMeta(type): def register(cls, tp: T) -> T: pass abstractmethod = object()