-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Fix reachability in nested match sequence patterns #19993
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
base: master
Are you sure you want to change the base?
Conversation
According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅ |
): | ||
# Can't narrow rest type to uninhabited | ||
# if narrowed_type is dict or list. | ||
# Those can be matched by Mapping or Sequence patterns. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I... don't really follow. I don't know anything about the pattern matching checker, so I'm just going off inference/comments elsewhere, but isn't it right to say the rest is uninhabited? If I were to guess the problem is more with the |
rather than the (_, {...})
?
(sorry, you'll probably just have to explain why this is right when this comment does that. I just don't get it...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The rest type is calculated from the intersection of the subject inner type and the matched inner type. Let's consider an example. Say the subject type is int | str
and the matched type is int
, the rest type will then be str
.
For dict
and list
this is a bit more complicated as those are match patterns itself. So if the subject is {"a": 1, "b": 2}
inferred to dict[str, int]
and the case {"a": 2}
the pattern would not actually match. However the inferred matched type is still dict[str, int]
and the intersection would be Never
which isn't correct. A similar issue happens with list. So we have to ignore the intersection rest type for Mapping and Sequence sub-patterns for those.
--
I'm not sure if it might even affect other generic classes as well. So far though I haven't been able to trigger the issue with any other examples so I'd leave it at these two for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK thanks that makes more sense. In that case I'm surprised about the check that rest is uninhabited! But more importantly, it seems like we already have machinery already doing this? (or did I misunderstand your explanation):
x: dict[str, int] = {"b": 0}
match x:
case {"a": 5}:
pass
case b:
reveal_type(b) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But more importantly, it seems like we already have machinery already doing this? [...]
x: dict[str, int] = {"b": 0} match x: case {"a": 5}: pass case b: reveal_type(b) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]"
The issue only happens for Mapping or Sequence patterns inside
a Sequence pattern. Furthermore you'll need a wildcard match. With that mypy currently thinks "oh it's a wildcard so it always matches, thus the rest should be never". That's only true though if the parent sequence pattern itself matches, so we shouldn't infer rest in those cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. Is there a reason why this doesn't narrow the 2nd tuple element to Never
?: (in comparison to if x
is a dict, which I can see now and is I think what you are talking about)
x: str = "blah"
match (x, 4):
case ("a", _):
pass
case b:
reveal_type(b) # N: Revealed type is "tuple[builtins.str, Literal[4]?]"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The rest type for x
is inferred as str
whereas with the dict example it's inferred as Never
since technically both the subject and inner match types are identical dict[str, str]
.
m7: dict[str, str]
match (m7, m7):
case ({"a": "1"}, _):
...
case (_, {"a": "2"}):
...
The rest type can't be inferred to be uninhabited if the inner pattern matched a Mapping or Sequence.
Fixes #19981
Fixes #19995