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

Exhaustive match statement treated as inexhaustive #1380

Closed
DanielPechersky opened this issue Jun 2, 2021 · 4 comments
Closed

Exhaustive match statement treated as inexhaustive #1380

DanielPechersky opened this issue Jun 2, 2021 · 4 comments
Labels
enhancement New feature or request fixed in next version (main) A fix has been implemented and will appear in an upcoming version

Comments

@DanielPechersky
Copy link

Environment data

  • Language Server version: 2021.5.4
  • OS and version: darwin x64
  • Python version (and distribution if applicable, e.g. Anaconda): python3.10.0b2_0
  • python.analysis.indexing: undefined
  • python.analysis.typeCheckingMode: strict

Expected behaviour

Test function shouldn't have any errors

Actual behaviour

Pylance warns that test can return None despite the match statement being exhaustive

Additionally, on adding an additional case, pylance correctly identifies the new variable as Never, showing that the match statement is exhaustive

Code Snippet / Additional information

def test(x: int) -> str: 
    """             ^^^
    Function with declared type of "str" must return value
    Type "None" cannot be assigned to type "str"
    """
    match x:
        case int():
            return ""

def testSucceeds(x: int) -> str:
    match x:
        case int():
            return ""
        case y:  # (variable) y: Never
            return y
@github-actions github-actions bot added the triage label Jun 2, 2021
@erictraut
Copy link
Contributor

erictraut commented Jun 2, 2021

This is intended behavior, at least currently. It will handle the case where you are employing an "irrefutable" pattern, as in the testSucceeds example. Irrefutable patterns can be detected early in the analysis process when building the code flow graph. For your first example, the type checker requires a full type analysis to determine whether matching is exhaustive, and it's too late at that point to affect the code flow graph.

It is theoretically possible for us to add support for this case, but it would be significant complexity and potentially very costly at analysis time. We implemented a similar feature called "implied else narrowing" for chains of if/elif statements with no "else". It took a long time to get this right, and we needed to apply some significant limitations to make it performant.

Since the match statement is so new and very few people are using it at this point, I think it would be premature to invest in such a feature. We can revisit this if and when more people start to use structural pattern matching and find that they are running into this issue.

For now, you can work around this limitation by adding an irrefutable case at the end of your match statement.

def test(x: int) -> str: 
    """
    Function with declared type of "str" must return value
    Type "None" cannot be assigned to type "str"
    """
    match x:
        case int():
            return ""
        case _:
            return ""

or

def test(x: int) -> str: 
    """
    Function with declared type of "str" must return value
    Type "None" cannot be assigned to type "str"
    """
    match x:
        case int():
            return ""

    raise Exception('Should never get here')

@DanielPechersky
Copy link
Author

Alright, I'll probably end up making an Exhaustive exception for these cases and using it to indicate exhaustiveness.
Thank you for looking over this.

@erictraut
Copy link
Contributor

After thinking about this more, I came up with a way to handle this in a way that will will have a minimal performance impact. With this new approach in place, the type checker is now able to detect match exhaustion and handle the case above. This will be in the next release of pylance.

@erictraut erictraut added enhancement New feature or request fixed in next version (main) A fix has been implemented and will appear in an upcoming version and removed triage labels Jun 5, 2021
@jakebailey
Copy link
Member

This issue has been fixed in version 2021.6.1, which we've just released. You can find the changelog here: https://github.com/microsoft/pylance-release/blob/main/CHANGELOG.md#202161-9-june-2021

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request fixed in next version (main) A fix has been implemented and will appear in an upcoming version
Projects
None yet
Development

No branches or pull requests

3 participants