Skip to content

Narrowing an optional class type to an unrelated Protocol causes a false unreachable error #12802

Open
@ajkaijanaho

Description

@ajkaijanaho

Bug Report

If Y is a runtime-checkable protocol which is not implemented by the class X, then mypy will erroneously consider the print statement in the following to be unreachable:

def f(x: Optional[X]) -> None:
    if isinstance(x, Y):
        print(x.bar())

This is incorrect, because x may be an object created from a subclass of X which does implement Y.

If x is annotated as X (not Optional[X]), then mypy behaves correctly.

To Reproduce

Save the following source code to a file (called foo.py below).

from typing import Optional, Protocol, runtime_checkable
from typing_extensions import reveal_type


class X:
    def foo(self) -> int:
        ...


@runtime_checkable
class Y(Protocol):
    def bar(self) -> int:
        ...


class Z(X):
    def foo(self) -> int:
        return 5

    def bar(self) -> int:
        return 6


def f(x: Optional[X]) -> None:
    reveal_type(x)
    if isinstance(x, Y):
        reveal_type(x)
        print(x.bar())


def g(x: X) -> None:
    reveal_type(x)
    if isinstance(x, Y):
        reveal_type(x)
        print(x.bar())


f(Z())
g(Z())

Run mypy on it with --warn-unreachable

Expected Behavior

> mypy --warn-unreachable foo.py
foo.py:25: note: Revealed type is "Union[foo.X, None]"
foo.py:27: note: Revealed type is "foo.<subclass of "X" and "Y">1"
foo.py:32: note: Revealed type is "foo.X"
foo.py:34: note: Revealed type is "foo.<subclass of "X" and "Y">1"
Success: no issues found in 1 source file

Actual Behavior

> mypy --warn-unreachable foo.py
foo.py:25: note: Revealed type is "Union[foo.X, None]"
foo.py:27: error: Statement is unreachable
foo.py:32: note: Revealed type is "foo.X"
foo.py:34: note: Revealed type is "foo.<subclass of "X" and "Y">1"
Found 1 error in 1 file (checked 1 source file)

(Running the script demonstrates that there is no actual unreachable code at runtime.)

Your Environment

  • Mypy version used: mypy 0.950 (compiled: yes)
  • Mypy command-line flags: --warn-unreachable
  • Mypy configuration options from mypy.ini (and other config files): None
  • Python version used: Python 3.10.4
  • Operating system and version:
Edition	Windows 10 Business
Version	21H1
Installed on	‎2.‎8.‎2021
OS build	19043.1706
Experience	Windows Feature Experience Pack 120.2212.4170.0

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions