You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[1.20 regression] value pattern match statement narrowing broken for typevars with values #21356
Bug Report
I think I've found a regression in 1.20 - was recently trying to migrate a larger codebase from 1.18 and noticed this issue in a few places which didn't make sense. I've isolated it down to a small example which is a bit silly, but hopefully clear. I think this is a consequence of a deliberate change in #20908. Happy to try and put together a PR to resolve if useful, but I'm not exactly across the mypy internals, so might need a bit of guidance.
importbuiltinsfromtypingimportTypeVarT=TypeVar("T", int, str)
deff(x: T) ->T:
matchtype(x):
casebuiltins.int:
return42# mypy 1.20: error: Incompatible return value type (got "int", expected "str")casebuiltins.str:
return"hello"# mypy 1.20: error: Incompatible return value type (got "str", expected "int")case _:
raiseTypeError
Expected Behavior
Expected there to be no issues Actual Behavior
uv run --with mypy==1.20.2 mypy mre.py
mre.py:24: error: Incompatible return value type (got "int", expected "str") [return-value]
mre.py:26: error: Incompatible return value type (got "str", expected "int") [return-value]
edit: I did a bit more testing, there's nothing special about builtins here, but it needs to be a value pattern and not a class pattern for this example to even work in 1.19
Your Environment
Mypy version used: 1.20.2 fails, 1.19 works, a quick bisect suggests afb99eb is where this changes
Mypy command-line flags: None in minimal example
Mypy configuration options from mypy.ini (and other config files): None in minimal example
Python version used: 3.13 / 3.12 in playground link above
changed the title [-]REGR: match statement narrowing with typevars in 1.20[/-][+]REGR: value pattern match statement narrowing with typevars in 1.20[/+]on Apr 28, 2026
changed the title [-]REGR: value pattern match statement narrowing with typevars in 1.20[/-][+][1.20 regression] value pattern match statement narrowing broken for typevars with values[/+]on Apr 28, 2026
So #20146 is a correct change. It make mypy's match narrowing use the same code paths as its equality narrowing. This makes mypy match Python semantics better.
Unfortunately, that means it is also a bit of an all-or-nothing change... differences between the equality narrowing logic and the (incorrect) logic mypy used to use for match narrowing, can show up as regressions like this one.
But fortunately, I spent a lot of time improving equality narrowing in the last release, which is where the --warn-unreachable comes into play. #20660 makes mypy's reachability logic a little stricter when --warn-unreachable is set. This happens to solve your false positive because of how mypy's logic for constrained type variables works.
Ideally, I would like for this reachability logic to not depend on --warn-unreachable. But as I mention in that PR, and as based on mypy_primer results, we need to make mypy check unreachable code for this to be a good idea.
So I'm not quite sure how best to help you. If --warn-unreachable is usable for you, that is great. Otherwise, we might just have to wait for changes to make mypy check unreachable code.
Thanks for the reply! Appreciate this is all super complicated and sometimes there is a price in pushing things forward - and I'm very glad to see continual improvement in mypy. I think in our case we can migrate to warn unreachable so that should work well enough for us.
Activity
[-]REGR: match statement builtins narrowing in 1.20[/-][+]REGR: match statement narrowing with typevars in 1.20[/+][-]REGR: match statement narrowing with typevars in 1.20[/-][+]REGR: value pattern match statement narrowing with typevars in 1.20[/+]brianschubert commentedon Apr 28, 2026
Thanks, I notice this only happens when
--warn-unreachableis not set. Setting--warn-unreachablegives the old expected behavior: https://mypy-play.net/?mypy=1.20.2&python=3.12&gist=c9f38f7eb2f7fb045aad1ecc5cb42cbb&flags=warn-unreachable. I guess something went wrong with how we're treating unreachable blocks when checking constrained typevar cases.brianschubert commentedon Apr 28, 2026
Bisects to #20146, cc @sterliakov
brianschubert commentedon Apr 28, 2026
(note that this is only true as of #20660 - at #20146 it manifests regardless of whether
--warn-unreachableis set)[-]REGR: value pattern match statement narrowing with typevars in 1.20[/-][+][1.20 regression] value pattern match statement narrowing broken for typevars with values[/+]hauntsaninja commentedon Apr 30, 2026
Oh, interesting case...
So #20146 is a correct change. It make mypy's match narrowing use the same code paths as its equality narrowing. This makes mypy match Python semantics better.
Unfortunately, that means it is also a bit of an all-or-nothing change... differences between the equality narrowing logic and the (incorrect) logic mypy used to use for match narrowing, can show up as regressions like this one.
But fortunately, I spent a lot of time improving equality narrowing in the last release, which is where the
--warn-unreachablecomes into play. #20660 makes mypy's reachability logic a little stricter when--warn-unreachableis set. This happens to solve your false positive because of how mypy's logic for constrained type variables works.Ideally, I would like for this reachability logic to not depend on
--warn-unreachable. But as I mention in that PR, and as based on mypy_primer results, we need to make mypy check unreachable code for this to be a good idea.So I'm not quite sure how best to help you. If
--warn-unreachableis usable for you, that is great. Otherwise, we might just have to wait for changes to make mypy check unreachable code.m-richards commentedon Apr 30, 2026
Thanks for the reply! Appreciate this is all super complicated and sometimes there is a price in pushing things forward - and I'm very glad to see continual improvement in mypy. I think in our case we can migrate to warn unreachable so that should work well enough for us.