diff --git a/mypy/constraints.py b/mypy/constraints.py index 49b042d5baf0..2a641bf27ed5 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -177,8 +177,9 @@ def infer_constraints(template: Type, actual: Type, direction: int) -> list[Cons for (t, a) in reversed(TypeState.inferring) ): return [] - if has_recursive_types(template): + if has_recursive_types(template) or isinstance(get_proper_type(template), Instance): # This case requires special care because it may cause infinite recursion. + # Note that we include Instances because the may be recursive as str(Sequence[str]). if not has_type_vars(template): # Return early on an empty branch. return [] diff --git a/mypy/types.py b/mypy/types.py index e322cf02505f..2f0feb703f6a 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -3240,6 +3240,12 @@ def __init__(self) -> None: def visit_type_var(self, t: TypeVarType) -> bool: return True + def visit_type_var_tuple(self, t: TypeVarTupleType) -> bool: + return True + + def visit_param_spec(self, t: ParamSpecType) -> bool: + return True + def has_type_vars(typ: Type) -> bool: """Check if a type contains any type variables (recursively).""" diff --git a/test-data/unit/check-recursive-types.test b/test-data/unit/check-recursive-types.test index 0d727b109658..95b0918866f1 100644 --- a/test-data/unit/check-recursive-types.test +++ b/test-data/unit/check-recursive-types.test @@ -826,3 +826,14 @@ z = z x = y # E: Incompatible types in assignment (expression has type "L", variable has type "K") z = x # OK [builtins fixtures/tuple.pyi] + +[case testRecursiveInstanceInferenceNoCrash] +from typing import Sequence, TypeVar, Union + +class C(Sequence[C]): ... + +T = TypeVar("T") +def foo(x: T) -> C: ... + +Nested = Union[C, Sequence[Nested]] +x: Nested = foo(42)