diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index ae2a4907e0c20..6669dc7cd627d 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -29,7 +29,7 @@ import mypy.checker from mypy import types from mypy.sametypes import is_same_type -from mypy.erasetype import replace_meta_vars +from mypy.erasetype import replace_meta_vars, erase_type from mypy.messages import MessageBuilder from mypy import messages from mypy.infer import infer_type_arguments, infer_function_type_arguments @@ -1314,14 +1314,12 @@ def infer_overload_return_type(self, return None elif any_causes_overload_ambiguity(matches, return_types, arg_types, arg_kinds, arg_names): # An argument of type or containing the type 'Any' caused ambiguity. - if all(is_subtype(ret_type, return_types[-1]) and - is_subtype(return_types[-1], ret_type) - for ret_type in return_types[:-1]): - # The last match is mutually compatible with all previous ones, so it's safe - # to return that inferred type. - return return_types[-1], inferred_types[-1] + # We try returning a precise type if we can. If not, we give up and just return 'Any'. + if all_same_types(return_types): + return return_types[0], inferred_types[0] + elif all_same_types(erase_type(typ) for typ in return_types): + return erase_type(return_types[0]), erase_type(inferred_types[0]) else: - # We give up and return 'Any'. return self.check_call(callee=AnyType(TypeOfAny.special_form), args=args, arg_kinds=arg_kinds, diff --git a/mypy/erasetype.py b/mypy/erasetype.py index eeae6a2a93421..2da4877aed8f9 100644 --- a/mypy/erasetype.py +++ b/mypy/erasetype.py @@ -5,19 +5,19 @@ CallableType, TupleType, TypedDictType, UnionType, Overloaded, ErasedType, PartialType, DeletedType, TypeTranslator, UninhabitedType, TypeType, TypeOfAny ) +from mypy.nodes import ARG_STAR, ARG_STAR2 def erase_type(typ: Type) -> Type: """Erase any type variables from a type. - Also replace tuple types with the corresponding concrete types. Replace - callable types with empty callable types. + Also replace tuple types with the corresponding concrete types. Examples: A -> A B[X] -> B[Any] Tuple[A, B] -> tuple - Callable[...] -> Callable[[], None] + Callable[[A1, A2, ...], R] -> Callable[..., Any] Type[X] -> Type[Any] """ @@ -57,11 +57,19 @@ def visit_type_var(self, t: TypeVarType) -> Type: def visit_callable_type(self, t: CallableType) -> Type: # We must preserve the fallback type for overload resolution to work. - ret_type = NoneTyp() # type: Type - return CallableType([], [], [], ret_type, t.fallback) + any_type = AnyType(TypeOfAny.special_form) + return CallableType( + arg_types=[any_type, any_type], + arg_kinds=[ARG_STAR, ARG_STAR2], + arg_names=[None, None], + ret_type=any_type, + fallback=t.fallback, + is_ellipsis_args=True, + implicit=True, + ) def visit_overloaded(self, t: Overloaded) -> Type: - return t.items()[0].accept(self) + return t.fallback.accept(self) def visit_tuple_type(self, t: TupleType) -> Type: return t.fallback.accept(self) diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index 2e8d2c153d7ae..15deeaa2343e2 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -11,7 +11,7 @@ UnboundType, AnyType, CallableType, TupleType, TypeVarDef, Type, Instance, NoneTyp, Overloaded, TypeType, UnionType, UninhabitedType, true_only, false_only, TypeVarId, TypeOfAny ) -from mypy.nodes import ARG_POS, ARG_OPT, ARG_STAR, CONTRAVARIANT, INVARIANT, COVARIANT +from mypy.nodes import ARG_POS, ARG_OPT, ARG_STAR, ARG_STAR2, CONTRAVARIANT, INVARIANT, COVARIANT from mypy.subtypes import is_subtype, is_more_precise, is_proper_subtype from mypy.test.typefixture import TypeFixture, InterfaceTypeFixture @@ -150,11 +150,19 @@ def test_erase_with_tuple_type(self) -> None: def test_erase_with_function_type(self) -> None: self.assert_erase(self.fx.callable(self.fx.a, self.fx.b), - self.fx.callable_type(self.fx.nonet)) + CallableType(arg_types=[self.fx.anyt, self.fx.anyt], + arg_kinds=[ARG_STAR, ARG_STAR2], + arg_names=[None, None], + ret_type=self.fx.anyt, + fallback=self.fx.function)) def test_erase_with_type_object(self) -> None: self.assert_erase(self.fx.callable_type(self.fx.a, self.fx.b), - self.fx.callable_type(self.fx.nonet)) + CallableType(arg_types=[self.fx.anyt, self.fx.anyt], + arg_kinds=[ARG_STAR, ARG_STAR2], + arg_names=[None, None], + ret_type=self.fx.anyt, + fallback=self.fx.type_type)) def test_erase_with_type_type(self) -> None: self.assert_erase(self.fx.type_a, self.fx.type_a) diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index c74ec5e329587..46eb1508df31c 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -1443,6 +1443,86 @@ reveal_type(f(**a)) # E: Revealed type is 'Any' [builtins fixtures/dict.pyi] +[case testOverloadWithOverlappingItemsAndAnyArgument12] +from typing import overload, Any + +@overload +def f(x: int) -> Any: ... +@overload +def f(x: str) -> str: ... +def f(x): pass + +a: Any +reveal_type(f(a)) # E: Revealed type is 'Any' + +[case testOverloadWithOverlappingItemsAndAnyArgument13] +from typing import Any, overload, TypeVar, Generic + +class slice: pass + +T = TypeVar('T') +class A(Generic[T]): + @overload + def f(self, x: int) -> T: ... + @overload + def f(self, x: slice) -> A[T]: ... + def f(self, x): ... + +i: Any +a: A[Any] +reveal_type(a.f(i)) # E: Revealed type is 'Any' + +[case testOverloadWithOverlappingItemsAndAnyArgument14] +from typing import Any, overload, TypeVar, Generic + +T = TypeVar('T') + +class Wrapper(Generic[T]): pass +class slice: pass + +class A(Generic[T]): + @overload + def f(self, x: int) -> Wrapper[T]: ... + @overload + def f(self, x: slice) -> Wrapper[A[T]]: ... + def f(self, x): ... + +i: Any +a: A[Any] +reveal_type(a.f(i)) # E: Revealed type is '__main__.Wrapper[Any]' + +[case testOverloadWithOverlappingItemsAndAnyArgument15] +from typing import overload, Any, Union + +@overload +def f(x: int) -> str: ... +@overload +def f(x: str) -> str: ... +def f(x): pass + +@overload +def g(x: int) -> Union[str, int]: ... +@overload +def g(x: str) -> Union[int, str]: ... +def g(x): pass + +a: Any +reveal_type(f(a)) # E: Revealed type is 'builtins.str' +reveal_type(g(a)) # E: Revealed type is 'Union[builtins.str, builtins.int]' + +[case testOverloadWithOverlappingItemsAndAnyArgument16] +from typing import overload, Any, Union, Callable + +@overload +def f(x: int) -> Callable[[int, int], int]: ... +@overload +def f(x: str) -> Callable[[str], str]: ... +def f(x): pass + +a: Any +reveal_type(f(a)) # E: Revealed type is 'def (*Any, **Any) -> Any' +reveal_type(f(a)(a)) # E: Revealed type is 'Any' + [case testOverloadOnOverloadWithType] from typing import Any, Type, TypeVar, overload from mod import MyInt