Skip to content

Type-narrowing from parent to child class produces Never if inheriting from a concrete version of a generic parent class. #16494

@theemathas

Description

@theemathas

Bug Report

If I have a generic parent class, and I have a child class inheriting from a concrete version of the parent class, then I cannot type-narrow from the generic parent class to the child class. Trying to do the narrowing unexpectedly produces Never.

In my original code (not included here), this type-narrowing is after business logic that ensures that the object is an instance of the child class, and therefore the generic type is of the correct type. Additionally, this narrowing was done on self in a method in the parent class, so using Parent[Any] or Parent[int] isn't an option, since that requires changing the type of self.

Possibly related to #14785

To Reproduce

Run mypy on the following code. Or use this playground link.

from typing import Generic, TypeVar, reveal_type
T = TypeVar("T")

class Parent(Generic[T]):
    pass
class Child(Parent[int]):
    pass

def foo(thing: Parent[T]) -> None:
    assert isinstance(thing, Child)
    reveal_type(thing)

Expected Behavior

The revealed type should be Child.

Actual Behavior

main.py:11: note: Revealed type is "Never"
Success: no issues found in 1 source file

Your Environment

  • Bug is reproducible in the mypy Playground.
  • Mypy version used: "latest (1.7.0)" and "master branch" both reproduce the bug
  • Python version used: 3.11
  • Adding the --warn-unreachable option produces the same output.

Activity

changed the title [-]Can't type-narrow from parent to child class if inheriting from a concrete version of a generic parent class.[/-] [+]Type-narrowing from parent to child class produces `Never` if inheriting from a concrete version of a generic parent class.[/+] on Nov 15, 2023
KotlinIsland

KotlinIsland commented on Nov 30, 2023

@KotlinIsland
Contributor

When the concrete parameter is None, an error message is shown and becomes unreachable.

from typing import Generic, TypeVar

T = TypeVar("T")

class Base(Generic[T]): ...

class Sub1(Base[None]): ...

class Sub2(Base[int]): ...

def f(base: Base[T]) -> None:
    if isinstance(base, Sub1):  # Subclass of "Base[T]" and "Sub1" cannot exist: would have inconsistent method resolution order  [unreachable]
        reveal_type(self)  # unreachable

    if isinstance(self, Sub2):
        reveal_type(self)  # Never
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrong

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      Participants

      @theemathas@KotlinIsland

      Issue actions

        Type-narrowing from parent to child class produces `Never` if inheriting from a concrete version of a generic parent class. · Issue #16494 · python/mypy