From 025aaa7d612677f7a879d254e1ae59ac05d4ccf7 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 25 Sep 2026 12:30:52 +0100 Subject: [PATCH 1/5] [WIP] Try to break things again --- mypy/subtypes.py | 2 +- mypy/types.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index b774aa4e89e4..b36a6eaf9b3d 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -1310,7 +1310,7 @@ def f(self) -> A: ... return False assuming = right.type.assuming_proper if proper_subtype else right.type.assuming if len(assuming) > MAX_PROTOCOL_DEPTH: - return True + raise ValueError for l, r in reversed(assuming): if l == left and r == right: return True diff --git a/mypy/types.py b/mypy/types.py index def2f9f207fd..385d93dd8e90 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -227,7 +227,7 @@ class SentinelValue(NamedTuple): # Note we set very large cut-off for now, since some numerical libraries currently # use ~25 assumptions, and we want to avoid false negatives with them. We may be able # to reduce this in the future if we will optimize our callable unification logic. -MAX_PROTOCOL_DEPTH: Final = 40 +MAX_PROTOCOL_DEPTH: Final = 10 class TypeOfAny: From 18ed4b1838fe552f1358a175cccddef359421bb5 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 25 Sep 2026 13:37:37 +0100 Subject: [PATCH 2/5] Reveal --- mypy/subtypes.py | 7 ++++++- test-data/unit/check-protocols.test | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index b36a6eaf9b3d..ec508f484219 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -61,6 +61,7 @@ TypeAliasType, TypedDictType, TypeOfAny, + TypeStrVisitor, TypeType, TypeVarLikeType, TypeVarTupleType, @@ -1310,7 +1311,11 @@ def f(self) -> A: ... return False assuming = right.type.assuming_proper if proper_subtype else right.type.assuming if len(assuming) > MAX_PROTOCOL_DEPTH: - raise ValueError + visitor = TypeStrVisitor(options=options or Options()) + pairs = [] + for l, r in assuming: + pairs.append((l.accept(visitor), r.accept(visitor))) + raise ValueError(pairs) for l, r in reversed(assuming): if l == left and r == right: return True diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index ea0255d91612..256a7ae90088 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -4790,7 +4790,7 @@ bad_rep(t) # E: Argument 1 to "bad_rep" has incompatible type "C"; expected "P[ # N: def rep(self) -> C [builtins fixtures/tuple.pyi] -[case testDivergingProtocol] +[case testDivergingProtocol-skip] from typing import Protocol, TypeVar, List T = TypeVar("T") From b47d8a0ba9c8083c1824a9b601bc47f896dedcad Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 25 Sep 2026 14:25:24 +0100 Subject: [PATCH 3/5] Bisect --- mypy/types.py | 2 +- test-data/unit/check-protocols.test | 60 +++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/mypy/types.py b/mypy/types.py index 385d93dd8e90..6e95dae8abe8 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -227,7 +227,7 @@ class SentinelValue(NamedTuple): # Note we set very large cut-off for now, since some numerical libraries currently # use ~25 assumptions, and we want to avoid false negatives with them. We may be able # to reduce this in the future if we will optimize our callable unification logic. -MAX_PROTOCOL_DEPTH: Final = 10 +MAX_PROTOCOL_DEPTH: Final = 15 class TypeOfAny: diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index 256a7ae90088..1df584489a02 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -4803,3 +4803,63 @@ class C: # This should not cause infinite recursion. x: P[int] = C() [builtins fixtures/tuple.pyi] + +[case testProtocolLongOverload-skip] +from typing import Protocol, Generic, TypeVar, overload + +class P(Protocol): + def foo(self, x: int) -> P: ... + +class A1: ... +class A2: ... +class A3: ... +class A4: ... +class A5: ... +class A6: ... +class A7: ... +class A8: ... +class A9: ... +class A10: ... +class A11: ... +class A12: ... +class A13: ... +class A14: ... +class A15: ... + +T = TypeVar("T") +class C(Generic[T]): + @overload + def foo(self, x: A1) -> C[A1]: ... + @overload + def foo(self, x: A2) -> C[A2]: ... + @overload + def foo(self, x: A3) -> C[A3]: ... + @overload + def foo(self, x: A4) -> C[A4]: ... + @overload + def foo(self, x: A5) -> C[A5]: ... + @overload + def foo(self, x: A6) -> C[A6]: ... + @overload + def foo(self, x: A7) -> C[A7]: ... + @overload + def foo(self, x: A8) -> C[A8]: ... + @overload + def foo(self, x: A9) -> C[A9]: ... + @overload + def foo(self, x: A10) -> C[A10]: ... + @overload + def foo(self, x: A11) -> C[A11]: ... + @overload + def foo(self, x: A12) -> C[A12]: ... + @overload + def foo(self, x: A13) -> C[A13]: ... + @overload + def foo(self, x: A14) -> C[A14]: ... + @overload + def foo(self, x: A15) -> C[A15]: ... + def foo(self, x): + pass + +x: P = C() +[builtins fixtures/tuple.pyi] From e0e988ed608f494e5d43aaa75fa7e7faa54b22f0 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 25 Sep 2026 14:44:01 +0100 Subject: [PATCH 4/5] Bisect more --- mypy/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/types.py b/mypy/types.py index 6e95dae8abe8..d3a6186ae5a3 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -227,7 +227,7 @@ class SentinelValue(NamedTuple): # Note we set very large cut-off for now, since some numerical libraries currently # use ~25 assumptions, and we want to avoid false negatives with them. We may be able # to reduce this in the future if we will optimize our callable unification logic. -MAX_PROTOCOL_DEPTH: Final = 15 +MAX_PROTOCOL_DEPTH: Final = 12 class TypeOfAny: From 29aa5ee6d93a53c3d2e57fa7058bd59584b1eb07 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 25 Sep 2026 15:03:12 +0100 Subject: [PATCH 5/5] Last bisect --- mypy/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/types.py b/mypy/types.py index d3a6186ae5a3..b28de43abc7e 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -227,7 +227,7 @@ class SentinelValue(NamedTuple): # Note we set very large cut-off for now, since some numerical libraries currently # use ~25 assumptions, and we want to avoid false negatives with them. We may be able # to reduce this in the future if we will optimize our callable unification logic. -MAX_PROTOCOL_DEPTH: Final = 12 +MAX_PROTOCOL_DEPTH: Final = 11 class TypeOfAny: