diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 54d5f19d164e5..ca64868fb23de 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -269,7 +269,7 @@ def visit_call_expr(self, e: CallExpr, allow_none_return: bool = False) -> Type: self.check_runtime_protocol_test(e) if e.callee.fullname == 'builtins.issubclass': self.check_protocol_issubclass(e) - if isinstance(ret_type, UninhabitedType): + if isinstance(ret_type, UninhabitedType) and not ret_type.ambiguous: self.chk.binder.unreachable() if not allow_none_return and isinstance(ret_type, NoneTyp): self.chk.msg.does_not_return_value(callee_type, e) diff --git a/mypy/solve.py b/mypy/solve.py index 8eb12b7a9450a..c1243b71e0241 100644 --- a/mypy/solve.py +++ b/mypy/solve.py @@ -63,6 +63,7 @@ def solve_constraints(vars: List[TypeVarId], constraints: List[Constraint], # No constraints for type variable -- 'UninhabitedType' is the most specific type. if strict: candidate = UninhabitedType() + candidate.ambiguous = True else: candidate = AnyType(TypeOfAny.special_form) elif top is None: diff --git a/mypy/types.py b/mypy/types.py index dcc8459224191..210124c14aaff 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -357,6 +357,10 @@ class UninhabitedType(Type): can_be_true = False can_be_false = False is_noreturn = False # Does this come from a NoReturn? Purely for error messages. + # It is important to track whether this is an actual NoReturn type, or just a result + # of ambiguous type inference, in the latter case we don't want to mark a branch as + # unreachable in binder. + ambiguous = False # Is this a result of inference for a variable without constraints? def __init__(self, is_noreturn: bool = False, line: int = -1, column: int = -1) -> None: super().__init__(line, column) diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 18814539db6c1..57e7f44d15cd1 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -1942,3 +1942,52 @@ def main() -> None: reveal_type(b) # E: Revealed type is 'builtins.int' [builtins fixtures/tuple.pyi] [out] + +[case testDontMarkUnreachableAfterInferenceUninhabited] +from typing import TypeVar +T = TypeVar('T') +def f() -> T: pass + +class C: + x = f() + def m(self) -> str: + return 42 # E: Incompatible return value type (got "int", expected "str") + +if bool(): + f() + 1 + '' # E: Unsupported left operand type for + ("int") +[builtins fixtures/list.pyi] +[out] + +[case testDontMarkUnreachableAfterInferenceUninhabited2] +# flags: --strict-optional +from typing import TypeVar, Optional +T = TypeVar('T') +def f(x: Optional[T] = None) -> T: pass + +class C: + x = f() + def m(self) -> str: + return 42 # E: Incompatible return value type (got "int", expected "str") + +if bool(): + f() + 1 + '' # E: Unsupported left operand type for + ("int") +[builtins fixtures/list.pyi] +[out] + +[case testDontMarkUnreachableAfterInferenceUninhabited3] +from typing import TypeVar, List +T = TypeVar('T') +def f(x: List[T]) -> T: pass + +class C: + x = f([]) + def m(self) -> str: + return 42 # E: Incompatible return value type (got "int", expected "str") + +if bool(): + f([]) + 1 + '' # E: Unsupported left operand type for + ("int") +[builtins fixtures/list.pyi] +[out]