python / mypy

Optional static typing for Python
https://www.mypy-lang.org/
Other
17.84k stars 2.74k forks source link

Simple `Literal` type narrowing via pattern matching is broken #17291

Open willofferfit opened 1 month ago

willofferfit commented 1 month ago

Bug Report

Narrowing an int into a Literal via pattern matching is not working, please see example below.

To Reproduce

Python 3.12.2

from typing import Literal, reveal_type

import numpy as np

def sign1(x: int) -> Literal[-1, 0, 1]:
    """Extracts the sign of x."""
    match s := int(np.sign(x)):  # s has type int
        case -1 | 0 | 1:
            reveal_type(s)  # narrowed to Literal[-1, 0, 1]
            return s
        case _:
            raise AssertionError("Unreachable.")

def sign2(x: int) -> Literal[-1, 0, 1]:
    """Extracts the sign of x."""
    match s := int(np.sign(x)):
        case -1:
            reveal_type(s)  # narrowed to Literal[-1]
            return s
        case 0:
            reveal_type(s)  # narrowed to Literal[0]
            return s
        case 1:
            reveal_type(s)  # narrowed to Literal[1]
            return s
        case _:
            raise AssertionError("Unreachable.")

def sign3(x: int) -> Literal[-1, 0, 1]:
    """Extracts the sign of x."""
    match s := int(np.sign(x)):
        case -1:
            return -1
        case 0:
            return 0
        case 1:
            return 1
        case _:
            raise AssertionError("Unreachable.")

Expected Behavior

pyright 1.1.364

_.py
  _.py:10:25 - information: Type of "s" is "Literal[-1, 0, 1]"
  _.py:20:25 - information: Type of "s" is "Literal[-1]"
  _.py:23:25 - information: Type of "s" is "Literal[0]"
  _.py:26:25 - information: Type of "s" is "Literal[1]"
0 errors, 0 warnings, 4 informations 

Actual Behavior

mypy 1.10.0:

_.py:10: note: Revealed type is "Union[Literal[-1], Literal[0], Literal[1]]"
_.py:11: error: Incompatible return value type (got "Literal[-1, 0, 1]", expected "Literal[-1, 0, 1]")  [return-value]
_.py:20: note: Revealed type is "Literal[-1]"
_.py:21: error: Incompatible return value type (got "Literal[-1]", expected "Literal[-1, 0, 1]")  [return-value]
_.py:23: note: Revealed type is "Literal[0]"
_.py:26: note: Revealed type is "Literal[1]"
Found 2 errors in 1 file (checked 1 source file)
Kakadus commented 1 week ago

seems to be fixed on latest master branch: https://mypy-play.net/?mypy=master&python=3.12&gist=1beb20f898106c5956f964afc7a65615