Skip to content

Unable to narrow the type of a tagged union when using a match statement #15190

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

Open
jonathan-laurent opened this issue May 5, 2023 · 2 comments
Labels
bug mypy got something wrong topic-literal-types topic-match-statement Python 3.10's match statement topic-type-narrowing Conditional type narrowing / binder

Comments

@jonathan-laurent
Copy link

Bug Report

Mypy is unable to narrow the type of a tagged union when deconstructing it using a match statement.

To Reproduce

from typing import Literal, TypeAlias


Expr: TypeAlias = (
    tuple[Literal["const"], int] | tuple[Literal["add"], tuple["Expr", "Expr"]]
)


# OK
def eval(e: Expr) -> int:
    if e[0] == "const":
        return e[1]
    else:
        e1, e2 = e[1]
        return eval(e1) + eval(e2)


# Rejected by mypy (see errors below)
def eval2(e: Expr) -> int:
    match e:
        case ("const", x):
            return x
        case ("add", (e1, e2)):
            return eval2(e1) + eval2(e2)

Expected Behavior

Both eval and eval2 should typecheck.

Actual Behavior

Function eval typechecks but eval2 does not.

Mypy playground link

main.py:19: error: Missing return statement  [return]
main.py:22: error: Incompatible return value type (got "object", expected "int")  [return-value]
main.py:24: error: Argument 1 to "eval2" has incompatible type "object"; expected "Expr"  [arg-type]
Found 3 errors in 1 file (checked 1 source file)

Your Environment

  • Mypy version used: 1.2.0
  • Mypy command-line flags: none
  • Mypy configuration options from mypy.ini (and other config files): none
  • Python version used: 3.11
@jonathan-laurent jonathan-laurent added the bug mypy got something wrong label May 5, 2023
@AlexWaygood AlexWaygood added topic-literal-types topic-match-statement Python 3.10's match statement topic-type-narrowing Conditional type narrowing / binder labels May 5, 2023
@tmke8
Copy link
Contributor

tmke8 commented May 6, 2023

Related: #12364

@hwong557
Copy link

hwong557 commented May 6, 2023

Fundamentally the same, but here is a slightly simpler example:

from typing import Literal, TypeAlias

T: TypeAlias = tuple[Literal["str"], str] | tuple[Literal["float"], float]


def f() -> T:
    ...


pair: T = f()
match pair[0]:
    case "str":
        reveal_type(pair)
        # mypy should know pair[1] is a str.
        reveal_type(pair[1])
    case "float":
        reveal_type(pair)
        # mypy should know pair[1] is a float.
        reveal_type(pair[1])

Gist URL: https://gist.github.com/mypy-play/86dc7bd0856c13501dc21aa34ddb9b30
Playground URL: https://mypy-play.net/?mypy=master&python=3.11&gist=86dc7bd0856c13501dc21aa34ddb9b30

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong topic-literal-types topic-match-statement Python 3.10's match statement topic-type-narrowing Conditional type narrowing / binder
Projects
None yet
Development

No branches or pull requests

4 participants