Describe the Bug
You can assign me to this, I will PR.
Pyrefly does not consider a nested class pattern inside a generic wrapper class exhaustive.
A common result-like pattern:
match result:
case Ok(value):
...
case Err(SomeError()):
...
still leaves Pyrefly thinking the Err[SomeError] case can fall through.
Repro
from typing import assert_never
class Ok[T]:
__match_args__ = ("value",)
value: T
class Err[E]:
__match_args__ = ("value",)
value: E
class NotFound:
pass
def f(r: Ok[int] | Err[NotFound]) -> int:
match r:
case Ok(value):
return value
case Err(NotFound()):
raise Exception()
def g(r: Ok[int] | Err[NotFound]) -> int:
match r:
case Ok(value):
return value
case Err(NotFound()):
raise Exception()
case _:
assert_never(r)
Actual Behavior
Pyrefly reports bad-return for f:
ERROR Function declared to return `int`, but one or more paths are missing an explicit `return` [bad-return]
For g, Pyrefly reports that the fallback is still reachable:
ERROR Argument `Err[NotFound]` is not assignable to parameter `arg` with type `Never`
Expected Behavior
Both functions should pass.
Since r has type:
and the match handles:
case Ok(value)
case Err(NotFound())
there should be no remaining possible value for the fallback.
Keyword Pattern Variant Also Fails
The equivalent keyword pattern also fails:
def h(r: Ok[int] | Err[NotFound]) -> int:
match r:
case Ok(value=value):
return value
case Err(value=NotFound()):
raise Exception()
case _:
assert_never(r)
This seems related to match exhaustiveness, but it is not fixed by the issue #1286 fix. That fix handles direct union subjects like A | B; this case involves eliminating an outer generic wrapper arm based on an exhaustive nested pattern on its matched attribute.
Possibly relevant implementation details:
- Positional class patterns appear to have a TODO around narrowing attributes from
__match_args__.
- Keyword class patterns already create an attribute/facet subject, but the narrowed facet becoming impossible does not seem to eliminate the containing wrapper type.
Sandbox Link
https://pyrefly.org/sandbox/?project=N4IgZglgNgpgziAXKOBDAdgEwEYHsAeAdAA4CeS4ATrgLYAEALqcROgOZ0Q3G6UN2o4cGHwD66GADcRAHXQBjKILh0A8gGsA2gBUAuojl0jdUaJqoG8gBajUlNnFN0AvHQAUMkJNRQArjE8AGgBKQ2NvPxhEOm05RWU6AFFKSk1E-TCjU3NLGzsHJ1cPLx9-IND0YzoI-2jEuKUhOgA5XAYAMVxfLANK42JlOUwYMDowN0pojU1WBl06AB8klM1Wjq6sXWC6AFoAPk50Bl6qnOs6ScyqunlBGDV1NxqYYJPr98oYBl9KSuerqq3YTLShuNadbqYNzBV4A94XVAQYGJfDyGDEBgQXDoaFDEZ0NgTKZaWbzJbJVLgjaYLa7A6zN5GM5WC6M65A%2B4aJ6lF5s%2BEXL4-P48uFGDkgsFtCFYaGwvr8oyURHI1HozHY3Hy9l3Ex8%2BHKEQMcRSEQTYIgQIgMifMBQUiEBi0KAUADEdAACqQbXa6GgsHh8DdsZA2D8LFj0IQ5G6AMowe5WBgMYhwRAAejT1pGdsIvDYaZg6DTmFw8jgafkwYgoaV6qLY14Am80FQ2FgQfQIbDdbouAxEbgUfQZAYVmxO2klDgEZcdE8AGZCABGABMnjkmhE1Cnujk3S4PD4MEwO0wEE%2B8kx0lnAHJq%2BheDAb3IHwwdp8AI6%2Bc-HnbqGCkDsqDyGiTSuDeADudjoM%2B6AgAAvpawFXjA7TQDAFB%2BjgBAkOQ8FAA
(Only applicable for extension issues) IDE Information
No response
Describe the Bug
You can assign me to this, I will PR.
Pyrefly does not consider a nested class pattern inside a generic wrapper class exhaustive.
A common result-like pattern:
still leaves Pyrefly thinking the
Err[SomeError]case can fall through.Repro
Actual Behavior
Pyrefly reports
bad-returnforf:For
g, Pyrefly reports that the fallback is still reachable:Expected Behavior
Both functions should pass.
Since
rhas type:and the match handles:
there should be no remaining possible value for the fallback.
Keyword Pattern Variant Also Fails
The equivalent keyword pattern also fails:
This seems related to match exhaustiveness, but it is not fixed by the issue #1286 fix. That fix handles direct union subjects like
A | B; this case involves eliminating an outer generic wrapper arm based on an exhaustive nested pattern on its matched attribute.Possibly relevant implementation details:
__match_args__.Sandbox Link
https://pyrefly.org/sandbox/?project=N4IgZglgNgpgziAXKOBDAdgEwEYHsAeAdAA4CeS4ATrgLYAEALqcROgOZ0Q3G6UN2o4cGHwD66GADcRAHXQBjKILh0A8gGsA2gBUAuojl0jdUaJqoG8gBajUlNnFN0AvHQAUMkJNRQArjE8AGgBKQ2NvPxhEOm05RWU6AFFKSk1E-TCjU3NLGzsHJ1cPLx9-IND0YzoI-2jEuKUhOgA5XAYAMVxfLANK42JlOUwYMDowN0pojU1WBl06AB8klM1Wjq6sXWC6AFoAPk50Bl6qnOs6ScyqunlBGDV1NxqYYJPr98oYBl9KSuerqq3YTLShuNadbqYNzBV4A94XVAQYGJfDyGDEBgQXDoaFDEZ0NgTKZaWbzJbJVLgjaYLa7A6zN5GM5WC6M65A%2B4aJ6lF5s%2BEXL4-P48uFGDkgsFtCFYaGwvr8oyURHI1HozHY3Hy9l3Ex8%2BHKEQMcRSEQTYIgQIgMifMBQUiEBi0KAUADEdAACqQbXa6GgsHh8DdsZA2D8LFj0IQ5G6AMowe5WBgMYhwRAAejT1pGdsIvDYaZg6DTmFw8jgafkwYgoaV6qLY14Am80FQ2FgQfQIbDdbouAxEbgUfQZAYVmxO2klDgEZcdE8AGZCABGABMnjkmhE1Cnujk3S4PD4MEwO0wEE%2B8kx0lnAHJq%2BheDAb3IHwwdp8AI6%2Bc-HnbqGCkDsqDyGiTSuDeADudjoM%2B6AgAAvpawFXjA7TQDAFB%2BjgBAkOQ8FAA
(Only applicable for extension issues) IDE Information
No response