diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 44855f49afaf..afcde0030e11 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -3092,6 +3092,20 @@ def infer_overload_return_type( self.chk.store_types(type_maps[0]) return erase_type(return_types[0]), erase_type(inferred_types[0]) else: + # If exactly one match returns Never (uninhabited, non-ambiguous), it is the + # most specific result possible and there is no real ambiguity: Never is a + # subtype of every type, so the overload that returns Never is always the + # most precise answer regardless of which overload would otherwise be chosen. + never_indices = [ + i + for i, t in enumerate(return_types) + if isinstance(get_proper_type(t), UninhabitedType) + and not get_proper_type(t).ambiguous # type: ignore[union-attr] + ] + if len(never_indices) == 1: + idx = never_indices[0] + self.chk.store_types(type_maps[idx]) + return return_types[idx], inferred_types[idx] return self.check_call( callee=AnyType(TypeOfAny.special_form), args=args, diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 721ca52f59ee..9d9bff8c5107 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -1906,6 +1906,35 @@ a: Any reveal_type(f(a)) # N: Revealed type is "def (*Any, **Any) -> Any" reveal_type(f(a)(a)) # N: Revealed type is "Any" +[case testOverloadWithOverlappingItemsAndAnyArgumentNeverReturn] +# Regression test for https://github.com/python/mypy/issues/21027 +# When one overload returns Never, it is always the most specific result and +# should be chosen even when an argument contains Any that would otherwise +# cause overload ambiguity. +from typing import Any, Generic, Never, TypeVar, overload + +T = TypeVar('T') + +@overload +def f(x: int) -> Never: ... +@overload +def f(x: str) -> int: ... +def f(x): ... + +def test_plain(a: Any) -> None: + reveal_type(f(a)) # N: Revealed type is "Never" + +T2 = TypeVar('T2') +class A(Generic[T2]): + @overload + def g(self, x: int) -> Never: ... + @overload + def g(self, x: str) -> T2: ... + def g(self, x): ... + +def test_generic(obj: A[Any], a: Any) -> None: + reveal_type(obj.g(a)) # N: Revealed type is "Never" + [case testOverloadOnOverloadWithType] from typing import Any, Type, TypeVar, overload from mod import MyInt