Skip to content

Commit 3268a7a

Browse files
Disallow TypeVar constraints parameterized by type variables (#18186)
Emit an error for [this test case](https://github.com/python/typing/blob/46b05a4c10ed3841c9bc5126ba9f31dd8ae061e7/conformance/tests/generics_basic.py#L54-L55) from the conformance tests: ```python class Test(Generic[T]): BadConstraint2 = TypeVar("BadConstraint2", str, list[T]) # E ```
1 parent 267a35d commit 3268a7a

File tree

4 files changed

+55
-3
lines changed

4 files changed

+55
-3
lines changed

mypy/message_registry.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,10 @@ def with_additional_msg(self, info: str) -> ErrorMessage:
350350
"Await expression cannot be used as a type variable bound", codes.SYNTAX
351351
)
352352

353+
TYPE_VAR_GENERIC_CONSTRAINT_TYPE: Final = ErrorMessage(
354+
"TypeVar constraint type cannot be parametrized by type variables", codes.MISC
355+
)
356+
353357
TYPE_ALIAS_WITH_YIELD_EXPRESSION: Final = ErrorMessage(
354358
"Yield expression cannot be used within a type alias", codes.SYNTAX
355359
)

mypy/semanal.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@
290290
UnpackType,
291291
get_proper_type,
292292
get_proper_types,
293+
has_type_vars,
293294
is_named_instance,
294295
remove_dups,
295296
type_vars_as_args,
@@ -1856,13 +1857,17 @@ def analyze_type_param(
18561857
else:
18571858
default = AnyType(TypeOfAny.from_omitted_generics)
18581859
if type_param.kind == TYPE_VAR_KIND:
1859-
values = []
1860+
values: list[Type] = []
18601861
if type_param.values:
18611862
for value in type_param.values:
18621863
analyzed = self.anal_type(value, allow_placeholder=True)
18631864
if analyzed is None:
18641865
analyzed = PlaceholderType(None, [], context.line)
1865-
values.append(analyzed)
1866+
if has_type_vars(analyzed):
1867+
self.fail(message_registry.TYPE_VAR_GENERIC_CONSTRAINT_TYPE, context)
1868+
values.append(AnyType(TypeOfAny.from_error))
1869+
else:
1870+
values.append(analyzed)
18661871
return TypeVarExpr(
18671872
name=type_param.name,
18681873
fullname=fullname,
@@ -5044,7 +5049,11 @@ def analyze_value_types(self, items: list[Expression]) -> list[Type]:
50445049
# soon, even if some value is not ready yet, see process_typevar_parameters()
50455050
# for an example.
50465051
analyzed = PlaceholderType(None, [], node.line)
5047-
result.append(analyzed)
5052+
if has_type_vars(analyzed):
5053+
self.fail(message_registry.TYPE_VAR_GENERIC_CONSTRAINT_TYPE, node)
5054+
result.append(AnyType(TypeOfAny.from_error))
5055+
else:
5056+
result.append(analyzed)
50485057
except TypeTranslationError:
50495058
self.fail("Type expected", node)
50505059
result.append(AnyType(TypeOfAny.from_error))

test-data/unit/check-python312.test

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1525,6 +1525,29 @@ reveal_type(E[str]().a) # N: Revealed type is "builtins.list[Any]"
15251525
[builtins fixtures/type.pyi]
15261526
[typing fixtures/typing-full.pyi]
15271527

1528+
[case testPEP695TypeAliasInvalidGenericConstraint]
1529+
class A[T]:
1530+
class a[S: (int, list[T])]: pass # E: Name "T" is not defined
1531+
type b[S: (int, list[T])] = S # E: TypeVar constraint type cannot be parametrized by type variables
1532+
def c[S: (int, list[T])](self) -> None: ... # E: TypeVar constraint type cannot be parametrized by type variables
1533+
[builtins fixtures/tuple.pyi]
1534+
[typing fixtures/typing-full.pyi]
1535+
1536+
[case testPEP695TypeAliasUnboundTypeVarConstraint]
1537+
from typing import TypeVar
1538+
T = TypeVar("T")
1539+
class a[S: (int, list[T])]: pass # E: Type variable "__main__.T" is unbound \
1540+
# N: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class) \
1541+
# N: (Hint: Use "T" in function signature to bind "T" inside a function)
1542+
type b[S: (int, list[T])] = S # E: Type variable "__main__.T" is unbound \
1543+
# N: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class) \
1544+
# N: (Hint: Use "T" in function signature to bind "T" inside a function)
1545+
def c[S: (int, list[T])](self) -> None: ... # E: Type variable "__main__.T" is unbound \
1546+
# N: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class) \
1547+
# N: (Hint: Use "T" in function signature to bind "T" inside a function)
1548+
[builtins fixtures/tuple.pyi]
1549+
[typing fixtures/typing-full.pyi]
1550+
15281551
[case testPEP695RedefineAsTypeAlias1]
15291552
class C: pass
15301553
type C = int # E: Name "C" already defined on line 1

test-data/unit/semanal-errors.test

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,6 +1060,22 @@ S = TypeVar('S', covariant=True, contravariant=True) \
10601060
# E: TypeVar cannot be both covariant and contravariant
10611061
[builtins fixtures/bool.pyi]
10621062

1063+
[case testInvalidTypevarArgumentsGenericConstraint]
1064+
from typing import Generic, List, TypeVar
1065+
from typing_extensions import Self
1066+
1067+
T = TypeVar("T")
1068+
1069+
def f(x: T) -> None:
1070+
Bad = TypeVar("Bad", int, List[T]) # E: TypeVar constraint type cannot be parametrized by type variables
1071+
class C(Generic[T]):
1072+
Bad = TypeVar("Bad", int, List[T]) # E: TypeVar constraint type cannot be parametrized by type variables
1073+
class D:
1074+
Bad = TypeVar("Bad", int, List[Self]) # E: TypeVar constraint type cannot be parametrized by type variables
1075+
S = TypeVar("S", int, List[T]) # E: Type variable "__main__.T" is unbound \
1076+
# N: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class) \
1077+
# N: (Hint: Use "T" in function signature to bind "T" inside a function)
1078+
10631079
[case testInvalidTypevarValues]
10641080
from typing import TypeVar
10651081
b = TypeVar('b', *[int]) # E: Unexpected argument to "TypeVar()"

0 commit comments

Comments
 (0)