Skip to content

mypy doesn't take value restrictions into account when solving type variables #11880

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

Open
hauntsaninja opened this issue Jan 1, 2022 · 2 comments
Labels
affects-typeshed Anything that blocks a typeshed change bug mypy got something wrong topic-type-variables

Comments

@hauntsaninja
Copy link
Collaborator

hauntsaninja commented Jan 1, 2022

This came up in python/typeshed#6762

from typing import Any, TypeVar, Union

T = TypeVar("T")
V = TypeVar("V", str, bytes)

def check_t(x: T | list[T]) -> T: ...
def check_v(x: V | list[V]) -> V: ...

x_str: str
x_list_any: list[Any]
x_str_list_any: str | list[Any]

# mypy does the correct thing here

reveal_type(check_t(x_str))  # str
reveal_type(check_t(x_list_any))  # list[Any] | Any
reveal_type(check_t(x_str_list_any))  # str | list[Any] | Any
reveal_type(check_v(x_str))  # str

# but maybe not here

# E: Value of type variable "V" of "check_v" cannot be "Union[List[Any], Any]"
# N: Revealed type is "Union[builtins.list[Any], Any]"
reveal_type(check_v(x_list_any))
# E: Value of type variable "V" of "check_v" cannot be "Union[str, List[Any], Any]"
# N: Revealed type is "Union[builtins.str, builtins.list[Any], Any]"
reveal_type(check_v(x_str_list_any))

Looking at the output, my guess is that mypy isn't taking value restrictions into account when solving constraints and only using value restriction as a check later. That is, list[Any] cannot match the T part of T | list[T] because it would violate the value restriction.

@hauntsaninja
Copy link
Collaborator Author

hauntsaninja commented Jan 1, 2022

Playing around with the following as a potential fix, but I think there are some other issues with how mypy handles this kind of thing.

diff --git a/mypy/constraints.py b/mypy/constraints.py
index 5a78cdb94..9ca16bcaf 100644
--- a/mypy/constraints.py
+++ b/mypy/constraints.py
@@ -199,6 +199,12 @@ def infer_constraints_if_possible(template: Type, actual: Type,
         # This is not caught by the above branch because of the erase_typevars() call,
         # that would return 'Any' for a type variable.
         return None
+    if (
+        isinstance(template, TypeVarType) and
+        template.values and
+        all(not mypy.subtypes.is_equivalent(actual, erase_typevars(t)) for t in template.values)
+    ):
+        return None
     return infer_constraints(template, actual, direction)

@JelleZijlstra
Copy link
Member

#3644 is a similar issue with T | Cls[T].

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
affects-typeshed Anything that blocks a typeshed change bug mypy got something wrong topic-type-variables
Projects
None yet
Development

No branches or pull requests

3 participants