overload with wrong return type is ignored
Bug Report
When one of the alternatives of an overload has a return type T which is a subtype of the return type of the annotation U but that the body of the function returns a type V which is a subtype of U but not T, no error is reported
To Reproduce
from typing import overload
@overload
def f(a: int) -> str:
...
# this is completely wrong
@overload
def f(a: str) -> bool:
...
def f(a: str | int) -> str | bool:
return "Hello"
reveal_type(f("some string"))
https://mypy-play.net/?mypy=latest&python=3.12&flags=strict&gist=83c5a353e087d8bbe03d542e7340b4b5
Expected Behavior
mypy should complain that the body of the function returns an str which is incompatible with type bool (overload 2)
More generally, for each overload signature, mypy should check that the body of the function typechecks with the overload signature.
Actual Behavior
mypy reports no error
main.py:16: note: Revealed type is "builtins.bool"
Success: no issues found in 1 source file
But the type declaration you provided should be treated as an "or" statement. For example, int | str is regarded by mypy as either int or str. Sorry for being a bit naive, but would you mind explaining a bit more about the expected output you want so that I can help solve the problem for you? Like what's the final result you excepted.
I would like mypy to report that return Hello in incompatible with overload #2: if a is of type str, the body of f does not return a bool.
Something like that:
main.py:14: error: Incompatible return value type in overload 2 (got "str", expected "bool") [return-value]
This is evidenced in the example by the fact that f("some string") is "Hello" while reveal_type(f("some string")) is bool.
Overload specification does not require any function body checks against overloaded signatures. Overloads are explicitly external to the function itself: foreign callers see them and don't see the implementation signature, while the impl itself is only checked against its own signature.
For reference, PEP 484 says:
The @overload-decorated definitions are for the benefit of the type checker only, since they will be overwritten by the non-@overload-decorated definition, while the latter is used at runtime but should be ignored by a type checker.
(emphasis mine)
I must say I find this underwhelming.