From 796230e6c7f6351ee81271aa3a507e0804d28a15 Mon Sep 17 00:00:00 2001 From: Tanmay Date: Wed, 23 Sep 2026 16:08:50 +0530 Subject: [PATCH] Reject diverging recursive aliases that grow via Concatenate --- mypy/typeanal.py | 1 + test-data/unit/check-python312.test | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 6717f4f18c90b..7a7d83e21b8a8 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -2523,6 +2523,7 @@ def visit_type_alias_type(self, t: TypeAliasType) -> Type: for arg in t.args: if not ( isinstance(arg, TypeVarLikeType) + and not (isinstance(arg, ParamSpecType) and arg.prefix.arg_types) or isinstance(arg, UnpackType) and isinstance(arg.type, TypeVarLikeType) ) and has_type_vars(arg): diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 89f242a00333e..1f1c6f6e72ac9 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -2373,3 +2373,16 @@ if TYPE_CHECKING: class Group(TypedDict): params: "Tuple[Alias, ...]" [builtins fixtures/tuple.pyi] + +[case testPEP695TypeAliasRecursiveConcatenateNoCrash] +from typing import Callable +from typing_extensions import Concatenate + +type A[**P] = Callable[[Callable[P, None]], A[Concatenate[int, P]]] # E: Invalid recursive alias: type variable nesting on right hand side +type B[**P] = Callable[[Callable[P, None]], B[P]] + +def foo[**P](a: A[P], b: B[P]) -> None: + reveal_type(a) # N: Revealed type is "Any" + reveal_type(b) # N: Revealed type is "def (def (*P.args, **P.kwargs)) -> ..." +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi]