Skip to content

Fix crash on contravariant generic with self-referential base - #22389

Open
jay23606 wants to merge 1 commit into
python:masterfrom
jay23606:pr-go/22360-contravariant-generic-with-a-self-refere
Open

jay23606 wants to merge 1 commit into
python:masterfrom
jay23606:pr-go/22360-contravariant-generic-with-a-self-refere

Conversation

@jay23606

@jay23606 jay23606 commented Nov 7, 2026

Copy link
Copy Markdown

Bug

mypy crashes with INTERNAL ERROR (RecursionError) when a class is checked against a contravariant generic whose base refers back to the class:

from typing import Generic, TypeVar

Z = TypeVar("Z", contravariant=True)

class N(Generic[Z]): ...

class C(N[N["C"]]): ...

def f(c: C) -> N[C]:
    return c

Checking C <: N[C] needs C <: N[C] again for the contravariant argument, so the recursion never ends. Covariant and invariant parameters do not loop here, which is why only contravariance crashes.

Change

While checking a contravariant type argument in SubtypeVisitor.visit_instance, push the (left, right) pair onto the existing subtype assumption stack. A nested check of the same pair then hits the existing is_assumed_subtype check at the top of is_subtype and returns True (coinductive assumption). The assumption is popped on exit, so a failed check still reports the error.

Only contravariant arguments push the pair, so other variances keep their current behaviour.

Testing

  • Added testContravariantSelfReferentialBaseDoesNotCrash to test-data/unit/check-generics.test, covering the issue's C example and the generic variant D[X]. With the fix it passes (PYTHONPATH=.venv/Lib/site-packages python -m pytest -o addopts="" -q -k testContravariantSelfReferentialBaseDoesNotCrash mypy/test/testcheck.py). I have not confirmed that it fails on the unfixed code: the run on the base commit stopped at collection with ModuleNotFoundError: No module named 'librt' before reaching the test.
  • PYTHONPATH=.venv/Lib/site-packages python -m pytest -o addopts="" -q -k "Variance or Generic or Protocol or Subtype or Callable or Tuple or TypeVar or Recursive or Alias or Overload or Typeddict or Namedtuple" mypy/test/testcheck.py: 3161 passed, 0 failed.

Not run here: the full runtests.py suite and the self-check. This machine's .venv has no pytest, so the commands above use the global Python with the venv's site-packages on PYTHONPATH. CI will run the full suite.

Regression test: untested here: CI verifies this change

Fixes #22360

Disclosure: this change was prepared with AI assistance.

@github-actions

github-actions Bot commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

@jay23606
jay23606 marked this pull request as ready for review November 7, 2026 01:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

contravariant generic with a self-referential base

1 participant