mypy icon indicating copy to clipboard operation
mypy copied to clipboard

Improve match statement union narrowing/inference

Open bergkvist opened this issue 1 year ago • 6 comments

Improve inference/narrowing support for union types in match statements.

Fixes #17549

Before:

var: tuple[int, int] | tuple[str, str]

# TODO: we can infer better here.
match var:
    case (42, a):
        reveal_type(a)  # N: Revealed type is "Union[builtins.int, builtins.str]"
    case ("yes", b):
        reveal_type(b)  # N: Revealed type is "Union[builtins.int, builtins.str]"

After:

var: tuple[int, int] | tuple[str, str]

match var:
    case (42, a):
        reveal_type(a)  # N: Revealed type is "builtins.int"
    case ("yes", b):
        reveal_type(b)  # N: Revealed type is "builtins.str"

Related:

  • https://github.com/python/mypy/pull/10191

bergkvist avatar Jul 27 '24 23:07 bergkvist

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

github-actions[bot] avatar Jul 27 '24 23:07 github-actions[bot]

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

github-actions[bot] avatar Aug 08 '24 16:08 github-actions[bot]

Nice! Does your fix address any of the other match statement issues? https://github.com/python/mypy/issues?q=sort%3Aupdated-desc+is%3Aopen+label%3Atopic-match-statement

From reading through the issues, I believe it should at least solve these:

  • https://github.com/python/mypy/issues/15190
  • https://github.com/python/mypy/issues/17549

I notice there are a bunch of match exhaustiveness issues with narrowing of the type passed on to the next match branches, which this PR does not fix.

bergkvist avatar Aug 11 '24 12:08 bergkvist

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

github-actions[bot] avatar Aug 18 '24 13:08 github-actions[bot]

maybe it would address this too? https://github.com/python/mypy/issues/16835

hjwp avatar Aug 21 '24 10:08 hjwp

maybe it would address this too? #16835

I don't think so, as I return the "current type" as the "rest type", which means the remainder/rest type (which is what gets passed on to the next match case) doesn't get narrowed properly yet. I have yet to figure out how to do this.

bergkvist avatar Aug 21 '24 11:08 bergkvist

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

github-actions[bot] avatar Sep 01 '24 23:09 github-actions[bot]

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

github-actions[bot] avatar Sep 02 '24 00:09 github-actions[bot]

Hi @bergkvist @Hnasar are there any blockers for landing this PR? even though it might not address all match narrowing issues it moves it towards a better place.

smurzin avatar Jan 22 '25 17:01 smurzin

I don't think this is solving the issue in my use case involving TypedDicts:

$ uvx --from 'mypy @ git+https://github.com/bergkvist/mypy@tobias/match-union-narrowing' mypy
    Updated https://github.com/bergkvist/mypy (92d3c91aaf2757706f9ef923650eb342d19af2fb)
      Built mypy @ git+https://github.com/bergkvist/mypy@92d3c91aaf2757706f9ef923650eb342d19af2fb
Installed 3 packages in 17ms
src/pack.py:150: error: "object" has no attribute "encode"  [attr-defined]
src/pack.py:151: error: "object" has no attribute "encode"  [attr-defined]

I have:

match node:
    case {"type": ("assign" | "cond_assign") as op, "key": key, "value": value}:
        kbs = key.encode()
        vbs = value.encode()
        ...

where reveal_type(node) shows

src/pack.py:148: note: Revealed type is "Union[TypedDict('propsast.AssignmentOperation', {'type': Union[Literal['assign'], Literal['cond_assign']], 'key': builtins.str, 'value': builtins.str, 'lineno': builtins.int}), TypedDict('propsast.IncludeOperation', {'type': Union[Literal['include'], Literal['tryinclude']], 'filename': builtins.str, 'lineno': builtins.int})]"

I tried removing the disjunction in the case statement and having a case branch for each literal value but that didn't help.

lordmauve avatar Apr 08 '25 16:04 lordmauve

@bergkvist Sorry for the late reply, but can you rebase this on the latest mypy, then we can ping one of the real maintainers to get someone with commit privileges to take a look.

Hnasar avatar Apr 09 '25 23:04 Hnasar