Skip to content

Commit

Permalink
Fix --strict-equality crash for instances of a class generic over a…
Browse files Browse the repository at this point in the history
… `ParamSpec` (#14792)

Fixes #14783.

Running mypy on this snippet of code currently causes a crash if you
have the `--strict-equality` option enabled:

```python
from typing import Generic, ParamSpec

P = ParamSpec("P")

class Foo(Generic[P]): ...

def checker(foo1: Foo[[int]], foo2: Foo[[str]]) -> None:
    foo1 == foo2
```

This is because the overlapping-equality logic in `meet.py` currently
does not account for the fact that `left` and `right` might both be
instances of `mypy.types.Parameters`, leading to this assertion being
tripped:


https://github.com/python/mypy/blob/800e8ffdf17de9fc641fefff46389a940f147eef/mypy/meet.py#L519

This PR attempts to add the necessary logic to `meet.py` to handle
instances of `mypy.types.Parameters`.
  • Loading branch information
AlexWaygood authored Feb 27, 2023
1 parent 30d0c99 commit 1853222
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 1 deletion.
17 changes: 16 additions & 1 deletion mypy/meet.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,22 @@ def _is_overlapping_types(left: Type, right: Type) -> bool:
left_possible = get_possible_variants(left)
right_possible = get_possible_variants(right)

# We start by checking multi-variant types like Unions first. We also perform
# First handle special cases relating to PEP 612:
# - comparing a `Parameters` to a `Parameters`
# - comparing a `Parameters` to a `ParamSpecType`
# - comparing a `ParamSpecType` to a `ParamSpecType`
#
# These should all always be considered overlapping equality checks.
# These need to be done before we move on to other TypeVarLike comparisons.
if isinstance(left, (Parameters, ParamSpecType)) and isinstance(
right, (Parameters, ParamSpecType)
):
return True
# A `Parameters` does not overlap with anything else, however
if isinstance(left, Parameters) or isinstance(right, Parameters):
return False

# Now move on to checking multi-variant types like Unions. We also perform
# the same logic if either type happens to be a TypeVar/ParamSpec/TypeVarTuple.
#
# Handling the TypeVarLikes now lets us simulate having them bind to the corresponding
Expand Down
43 changes: 43 additions & 0 deletions test-data/unit/pythoneval.test
Original file line number Diff line number Diff line change
Expand Up @@ -1924,3 +1924,46 @@ _testStarUnpackNestedUnderscore.py:10: error: List item 0 has incompatible type
_testStarUnpackNestedUnderscore.py:10: error: List item 1 has incompatible type "int"; expected "str"
_testStarUnpackNestedUnderscore.py:11: note: Revealed type is "builtins.list[builtins.str]"
_testStarUnpackNestedUnderscore.py:16: note: Revealed type is "builtins.list[builtins.object]"

[case testStrictEqualitywithParamSpec]
# flags: --strict-equality
from typing import Generic
from typing_extensions import Concatenate, ParamSpec

P = ParamSpec("P")

class Foo(Generic[P]): ...
class Bar(Generic[P]): ...

def bad(foo: Foo[[int]], bar: Bar[[int]]) -> bool:
return foo == bar

def good1(foo1: Foo[[int]], foo2: Foo[[str]]) -> bool:
return foo1 == foo2

def good2(foo1: Foo[[int, str]], foo2: Foo[[int, bytes]]) -> bool:
return foo1 == foo2

def good3(foo1: Foo[[int]], foo2: Foo[[int, int]]) -> bool:
return foo1 == foo2

def good4(foo1: Foo[[int]], foo2: Foo[[int]]) -> bool:
return foo1 == foo2

def good5(foo1: Foo[[int]], foo2: Foo[[bool]]) -> bool:
return foo1 == foo2

def good6(foo1: Foo[[int, int]], foo2: Foo[[bool, bool]]) -> bool:
return foo1 == foo2

def good7(foo1: Foo[[int]], foo2: Foo[P], *args: P.args, **kwargs: P.kwargs) -> bool:
return foo1 == foo2

def good8(foo1: Foo[P], foo2: Foo[[int, str, bytes]], *args: P.args, **kwargs: P.kwargs) -> bool:
return foo1 == foo2

def good9(foo1: Foo[Concatenate[int, P]], foo2: Foo[[int, str, bytes]], *args: P.args, **kwargs: P.kwargs) -> bool:
return foo1 == foo2

[out]
_testStrictEqualitywithParamSpec.py:11: error: Non-overlapping equality check (left operand type: "Foo[[int]]", right operand type: "Bar[[int]]")

0 comments on commit 1853222

Please sign in to comment.