Skip to content

Segfault crash on specific recursive type situation #20107

Closed

Description

@phiddypus

Bug Report

Mypy segfaults when checking the following code.

To Reproduce

(Gist)

from typing import Self, Union

type OneClass = list[OneClass|int]

class TwoClass (list[Union['TwoClass',int]]):
    @classmethod
    def meth (cls, obj:Self|OneClass) -> Self|None:
        if isinstance(obj, cls): return obj

Actual Behavior

In terminal:

Segmentation fault         (core dumped) mypy test.py

In gist:

Failed (exit code: -11)

Your Environment

  • Mypy version used: mypy 1.18.2 (compiled: yes)
  • Mypy command-line flags: None
  • Mypy configuration options from mypy.ini (and other config files): None
  • Python version used: Python 3.13.7

Activity

  1. added and removed
    bugmypy got something wrong
    on Oct 23, 2025
  2. changed the title [-]Segfault crash on[/-] [+]Segfault crash on specific recursive type situation[/+] on Oct 23, 2025
  3. A5rocks commented on Oct 23, 2025

    @A5rocks
    Collaborator

    Slightly simpler reproducer:

    type OneClass = list[OneClass]
    
    class TwoClass(list['TwoClass']):
        pass
    
    def f(obj: OneClass) -> None:
        if isinstance(obj, TwoClass): pass
  4. sterliakov commented on Oct 23, 2025

    @sterliakov
    Collaborator

    Traceback from the interpreted version (current master - #b266dd):

    Traceback (most recent call last):
      File "/tmp/tmp.d9375bltfC/.venv/bin/mypy", line 10, in <module>
        sys.exit(console_entry())
      File "/home/stas/Documents/Work/mypy/mypy/__main__.py", line 15, in console_entry
        main()
      File "/home/stas/Documents/Work/mypy/mypy/main.py", line 127, in main
        res, messages, blockers = run_build(sources, options, fscache, t0, stdout, stderr)
      File "/home/stas/Documents/Work/mypy/mypy/main.py", line 211, in run_build
        res = build.build(sources, options, None, flush_errors, fscache, stdout, stderr)
      File "/home/stas/Documents/Work/mypy/mypy/build.py", line 213, in build
        result = _build(
      File "/home/stas/Documents/Work/mypy/mypy/build.py", line 292, in _build
        graph = dispatch(sources, manager, stdout)
      File "/home/stas/Documents/Work/mypy/mypy/build.py", line 2931, in dispatch
        process_graph(graph, manager)
      File "/home/stas/Documents/Work/mypy/mypy/build.py", line 3322, in process_graph
        done, still_working = manager.wait_for_done(graph)
      File "/home/stas/Documents/Work/mypy/mypy/build.py", line 915, in wait_for_done
        process_stale_scc(graph, next_scc, self)
      File "/home/stas/Documents/Work/mypy/mypy/build.py", line 3460, in process_stale_scc
        graph[id].type_check_first_pass()
      File "/home/stas/Documents/Work/mypy/mypy/build.py", line 2322, in type_check_first_pass
        self.type_checker().check_first_pass()
      File "/home/stas/Documents/Work/mypy/mypy/checker.py", line 524, in check_first_pass
        self.accept(d)
      File "/home/stas/Documents/Work/mypy/mypy/checker.py", line 635, in accept
        stmt.accept(self)
      File "/home/stas/Documents/Work/mypy/mypy/nodes.py", line 971, in accept
        return visitor.visit_func_def(self)
      File "/home/stas/Documents/Work/mypy/mypy/checker.py", line 1192, in visit_func_def
        self._visit_func_def(defn)
      File "/home/stas/Documents/Work/mypy/mypy/checker.py", line 1196, in _visit_func_def
        self.check_func_item(defn, name=defn.name)
      File "/home/stas/Documents/Work/mypy/mypy/checker.py", line 1231, in check_func_item
        self.check_func_def(defn, typ, name, allow_empty)
      File "/home/stas/Documents/Work/mypy/mypy/checker.py", line 1504, in check_func_def
        self.accept(item.body)
      File "/home/stas/Documents/Work/mypy/mypy/checker.py", line 635, in accept
        stmt.accept(self)
      File "/home/stas/Documents/Work/mypy/mypy/nodes.py", line 1542, in accept
        return visitor.visit_block(self)
      File "/home/stas/Documents/Work/mypy/mypy/checker.py", line 3127, in visit_block
        self.accept(s)
      File "/home/stas/Documents/Work/mypy/mypy/checker.py", line 635, in accept
        stmt.accept(self)
        ~~~~~~~~~~~^^^^^^
      File "/home/stas/Documents/Work/mypy/mypy/nodes.py", line 1810, in accept
        return visitor.visit_if_stmt(self)
               ~~~~~~~~~~~~~~~~~~~~~^^^^^^
      File "/home/stas/Documents/Work/mypy/mypy/checker.py", line 5022, in visit_if_stmt
        if_map, else_map = self.find_isinstance_check(e)
                           ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^
      File "/home/stas/Documents/Work/mypy/mypy/checker.py", line 6281, in find_isinstance_check
        if_map, else_map = self.find_isinstance_check_helper(
                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
            node, in_boolean_context=in_boolean_context
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        )
        ^
      File "/home/stas/Documents/Work/mypy/mypy/checker.py", line 6304, in find_isinstance_check_helper
        *self.conditional_types_with_intersection(
         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
            self.lookup_type(expr), self.get_isinstance_type(node.args[1]), expr
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        ),
        ^
      File "/home/stas/Documents/Work/mypy/mypy/checker.py", line 7841, in conditional_types_with_intersection
        initial_types = conditional_types(
            expr_type,
        ...<2 lines>...
            consider_runtime_isinstance=consider_runtime_isinstance,
        )
      File "/home/stas/Documents/Work/mypy/mypy/checker.py", line 8220, in conditional_types
        elif not is_overlapping_types(current_type, proposed_type, ignore_promotions=True):
                 ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/home/stas/Documents/Work/mypy/mypy/meet.py", line 630, in is_overlapping_types
        if all(
           ~~~^
            _is_overlapping_types(left_arg, right_arg)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            for left_arg, right_arg in zip(left_args, right_args)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        ):
        ^
      File "/home/stas/Documents/Work/mypy/mypy/meet.py", line 631, in <genexpr>
        _is_overlapping_types(left_arg, right_arg)
        ~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
      File "/home/stas/Documents/Work/mypy/mypy/meet.py", line 433, in _is_overlapping_types
        return is_overlapping_types(
            left,
        ...<4 lines>...
            seen_types=seen_types.copy(),
        )
      File "/home/stas/Documents/Work/mypy/mypy/meet.py", line 630, in is_overlapping_types
        if all(
           ~~~^
            _is_overlapping_types(left_arg, right_arg)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            for left_arg, right_arg in zip(left_args, right_args)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        ):
        ^
      File "/home/stas/Documents/Work/mypy/mypy/meet.py", line 631, in <genexpr>
      [... repeated many times]
      File "/home/stas/Documents/Work/mypy/mypy/meet.py", line 631, in <genexpr>
        _is_overlapping_types(left_arg, right_arg)
        ~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
      File "/home/stas/Documents/Work/mypy/mypy/meet.py", line 433, in _is_overlapping_types
        return is_overlapping_types(
            left,
        ...<4 lines>...
            seen_types=seen_types.copy(),
        )
      File "/home/stas/Documents/Work/mypy/mypy/meet.py", line 403, in is_overlapping_types
        if are_related_types(
           ~~~~~~~~~~~~~~~~~^
            left, right, proper_subtype=overlap_for_overloads, ignore_promotions=ignore_promotions
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        ):
        ^
      File "/home/stas/Documents/Work/mypy/mypy/meet.py", line 319, in are_related_types
        return is_subtype(left, right, ignore_promotions=ignore_promotions) or is_subtype(
               ~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/home/stas/Documents/Work/mypy/mypy/subtypes.py", line 148, in is_subtype
        if left == right:
           ^^^^^^^^^^^^^
      File "/home/stas/Documents/Work/mypy/mypy/types.py", line 1645, in __eq__
        self.type == other.type
    RecursionError: maximum recursion depth exceeded in comparison
  5. added a commit that references this issue on Oct 28, 2025
    841db1f
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions