fix: prevent false unreachable error when comparing generic callables#21188
fix: prevent false unreachable error when comparing generic callables#21188Bahtya wants to merge 2 commits intopython:masterfrom
Conversation
When comparing a generic Callable parameter with a generic function using ==, mypy incorrectly concluded the types could never overlap and marked the if-body as unreachable. Fix by extending shallow_erase_type_for_equality to handle CallableType and using erased current_type in the equality overlap checks. Fixes python#21182 Signed-off-by: bahtya <bahtyar153@qq.com>
for more information, see https://pre-commit.ci
|
Diff from mypy_primer, showing the effect of this PR on open source code: rotki (https://github.com/rotki/rotki)
- rotkehlchen/tests/integration/test_premium.py:844: error: Statement is unreachable [unreachable]
+ rotkehlchen/tests/integration/test_premium.py:845: error: Statement is unreachable [unreachable]
- rotkehlchen/tests/integration/test_premium.py:858: error: Statement is unreachable [unreachable]
+ rotkehlchen/tests/integration/test_premium.py:859: error: Statement is unreachable [unreachable]
|
hauntsaninja
left a comment
There was a problem hiding this comment.
Hm having to erase current type points to a bug in is_overlapping_types
|
Thanks for the review! You're right that needing to erase the current type is a signal of an underlying issue in Looking at the bug: when comparing Would you prefer I investigate fixing |
|
The mypyc-compiled test failures ( |
|
Update on the mypyc-compiled test failures: I've confirmed these are pre-existing and unrelated to this PR:
No action needed on this PR for those failures. |
|
Hi @hauntsaninja — pinging on this since I haven't heard back after your To recap my earlier response: I agree the type erasure points to a deeper issue in
On the CI failures: the 3 failing What would you like me to do next? |
Problem
Fixes #21182
When comparing a generic
Callableparameter with a generic function using==, mypy incorrectly reported "Statement is unreachable" with--warn-unreachable:This is a regression — the code was accepted in earlier mypy versions.
Root Cause
In
conditional_typeswithfrom_equality=True, mypy usesshallow_erase_type_for_equalityto erase generic type parameters from the proposed type before checking overlap. However:shallow_erase_type_for_equalityonly handledInstancetypes, notCallableType. So generic callables likedef [T] (x: T) -> Twere never erased.Only the proposed type was erased, not the current type. When
is_overlapping_typeswas called with an unerased generic callable ascurrent_type(e.g., the identity function with.variables=(T,)),is_callable_compatibletried to unify its type variables against the other callable's free type variables and failed, concluding the types don't overlap.At runtime, generic type parameters are erased, so
identityandcmp_propertycan absolutely be equal — one could be passed as the other.Solution
Two changes:
1. Extend
shallow_erase_type_for_equalitytoCallableType(mypy/erasetype.py).variableshave their arg types and return type replaced withAnyand variables cleared2. Erase both sides in the overlap check (
mypy/checker.py)conditional_types, applyshallow_erase_type_for_equalityto bothcurrent_typeandproposed_typebefore callingis_overlapping_typesTesting
unreachabletests passnarrowingtests passequalitytests passmeet/overlaptests passerasetests pass!=operator — all work correctly