From fe2e1f5398e6c21359e3db19d8d6970bec4816c0 Mon Sep 17 00:00:00 2001 From: wolfgang-aura <169568318+wolfgang-aura@users.noreply.github.com> Date: Fri, 11 Sep 2026 00:24:34 +0800 Subject: [PATCH] Reject Self in PEP 695 type parameter bounds (#21960) PEP 673 lists the valid locations for Self, and a PEP 695 type parameter bound is not one of them. mypy accepted it silently: class C: def bounded[T: Self](self: T) -> None: ... reported no error at all. Analysing the bound with prohibit_self_type gives the same message mypy already uses for the other invalid locations. The constraint case is also covered. mypy already rejected it, but as "TypeVar constraint type cannot be parametrized by type variables", which names the wrong reason. --- mypy/semanal.py | 12 ++++++++++-- test-data/unit/check-python312.test | 8 ++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index bceade1b1ac12..877903f1ec1bf 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1888,7 +1888,11 @@ def analyze_type_param( ) -> TypeVarLikeExpr | None: fullname = self.qualified_name(type_param.name) if type_param.upper_bound: - upper_bound = self.anal_type(type_param.upper_bound, allow_placeholder=True) + upper_bound = self.anal_type( + type_param.upper_bound, + allow_placeholder=True, + prohibit_self_type="a type parameter bound", + ) # TODO: we should validate the upper bound is valid for a given kind. if upper_bound is None: # This and below copies special-casing for old-style type variables, that @@ -1929,7 +1933,11 @@ def analyze_type_param( values: list[Type] = [] if type_param.values: for value in type_param.values: - analyzed = self.anal_type(value, allow_placeholder=True) + analyzed = self.anal_type( + value, + allow_placeholder=True, + prohibit_self_type="a type parameter constraint", + ) if analyzed is None: analyzed = PlaceholderType(None, [], context.line) if has_type_vars(analyzed): diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 9d612109d5452..bb770a9f09176 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -1446,6 +1446,14 @@ reveal_type(F[str]().m()) # N: Revealed type is "__main__.F[builtins.str]" reveal_type(F[str]().mm(b'x')) # N: Revealed type is "tuple[__main__.F[builtins.str], builtins.bytes]" [builtins fixtures/tuple.pyi] +[case testTypingSelfInvalidLocationsTypeParams] +# flags: --python-version 3.12 +from typing import Self + +class C: + def bounded[T: Self](self: T) -> None: ... # E: Self type cannot be used in a type parameter bound + def constrained[T: (C, Self)](self: T) -> None: ... # E: Self type cannot be used in a type parameter constraint + [case testPEP695CallAlias] class C: def __init__(self, x: str) -> None: ...