diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 2ed757eeeaa8..3c86529fc390 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -126,6 +126,7 @@ covers_at_runtime, find_member, has_any_type, + is_proper_subtype, is_same_type, is_subtype, merge_typevars_in_callables_by_name, @@ -6941,6 +6942,16 @@ def any_causes_overload_ambiguity( for formal in formals: matching_formals.append(matched_callable.arg_types[formal]) if not all_same_types(matching_formals) and not all_same_types(matching_returns): + if ( + not arg_kinds[arg_idx].is_star() + and not isinstance(get_proper_type(arg_type), FunctionLike) + and all(is_proper_subtype(arg_type, formal) for formal in matching_formals) + ): + # These matches do not depend on Any being compatible with arbitrary + # types. For example, SubBox[Any] matches both SubBox[Any] and Box[Any]. + # Callable subtyping can still accept gradual parameter lists, so keep + # the conservative ambiguity check for callable arguments. + continue # Any maps to multiple different types, and the return types of these items differ. return True diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index cd5b924ed6b4..44463fc0ddcb 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -1812,6 +1812,27 @@ reveal_type(f(d)) # N: Revealed type is "builtins.list[builtins.int]" [builtins fixtures/list.pyi] +[case testOverloadWithNestedAnyNotCausingAmbiguity] +from typing import Any, Generic, TypeVar, overload +T = TypeVar('T') + +class Box(Generic[T]): pass +class SubBox(Box[T]): pass + +@overload +def choose(value: SubBox[T]) -> SubBox[T]: ... +@overload +def choose(value: Box[T]) -> Box[T]: ... +def choose(value): pass + +value: SubBox[Any] +reveal_type(choose(value)) # N: Revealed type is "__main__.SubBox[Any]" +reveal_type(choose(value=value)) # N: Revealed type is "__main__.SubBox[Any]" +nested: SubBox[Box[Any]] +reveal_type(choose(nested)) # N: Revealed type is "__main__.SubBox[__main__.Box[Any]]" +any_value: Any +reveal_type(choose(any_value)) # N: Revealed type is "Any" + [case testOverloadWithOverlappingItemsAndAnyArgument10] from typing import overload, Any diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 8c37e48d7c33..7c8af3ca2082 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -2287,6 +2287,20 @@ y = t"hello {1}" [out] m.pyi:3: error: T-strings: requires Python 3.14 or newer (current target: Python 3.13) +[case testReversedListWithNestedAny] +# flags: --python-version=3.12 --warn-return-any +from typing import Any, assert_type + +def last_value(values: list[tuple[Any, int]]) -> int | None: + for key, value in reversed(values): + assert_type(value, int) + return value + return None + +assert last_value([('first', 1), ('last', 2)]) == 2 +assert last_value([]) is None +[out] + [case testNativeParserStarUnpackRequiresPython311] # flags: --python-version=3.10 --native-parser from typing_extensions import TypeVarTuple, Unpack