Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exhaustiveness checking false positive regression in 1.10 #17252

Open
antonagestam opened this issue May 16, 2024 · 1 comment
Open

Exhaustiveness checking false positive regression in 1.10 #17252

antonagestam opened this issue May 16, 2024 · 1 comment
Labels
bug mypy got something wrong

Comments

@antonagestam
Copy link
Contributor

antonagestam commented May 16, 2024

Bug Report

While upgrading from 1.9 to 1.10 we found this regression in exhaustiveness checking. It looks like mypy stops inspecting a match expression once it's found an non-possible member.

To Reproduce

Playground link for below, note it's passing on 1.9.

import enum
from typing import assert_never


class Member(enum.Enum):
    a = enum.auto()
    b = enum.auto()
    c = enum.auto()


def fn(value: Member) -> None:
    if value is Member.b:
        return

    match value:
        case (
            Member.a
            | Member.b
            | Member.c
        ):
            # This only reveals Member.a!
            reveal_type(value)  
            ...
        case no_match:
            assert_never(no_match)
Related, removing Member.a causes the reveal_type() to be muted!
import enum
from typing import assert_never


class Member(enum.Enum):
    b = enum.auto()
    c = enum.auto()


def fn(value: Member) -> None:
    if value is Member.b:
        return

    match value:
        case (
            Member.b
            | Member.c
        ):
            # No reveal is printed!
            reveal_type(value)  
            ...
        case no_match:
            assert_never(no_match)

It's probably also worth pointing out that order matters. Inverting order in the match expression to case Member.c | Member.b: makes the issue go away. I suspect an early return optimization to be at fault! 🕵️

Expected Behavior

This was caused by an already excluded member of the enum. The error message that mypy presents does not make this obvious and the fact that it presents it as an exhaustiveness failure I consider a bug that should be fixed.

The reveal_type() should contain both members .a and .c, there should be no type error.

It should be noted that we were able to work around this by removing the erroneously included member (Member.b in the minimal reproduction) from the match expression.

Actual Behavior

reveal_type() contains only .a, and there is a type error:

repro.py:21: note: Revealed type is "Literal[repro.Member.a]"
repro.py:24: error: Argument 1 to "assert_never" has incompatible type "Literal[Member.c]"; expected "NoReturn"  [arg-type]
Found 1 error in 1 file (checked 1 source file)

Your Environment

See playground link.

@antonagestam antonagestam added the bug mypy got something wrong label May 16, 2024
@hauntsaninja
Copy link
Collaborator

Thanks, I think related to #16966

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong
Projects
None yet
Development

No branches or pull requests

2 participants