mypy icon indicating copy to clipboard operation
mypy copied to clipboard

Segmentation fault with mypy 1.7.1

Open ryo-tagami opened this issue 2 years ago • 4 comments

Crash Report

mypy crashes with segmentation fault as such:

mypy-segmentation-fault on  main is 📦 v0.1.0 via 🐍 v3.11.6 (mypy-segmentation-fault-py3.11)
❯ mypy --version
mypy 1.7.1 (compiled: yes)

mypy-segmentation-fault on  main is 📦 v0.1.0 via 🐍 v3.11.6 (mypy-segmentation-fault-py3.11)
❯ mypy -p mypy_segmentation_fault
zsh: segmentation fault  mypy -p mypy_segmentation_fault

with mypy_segmentation_fault.py:

from datetime import UTC, datetime
from typing import Protocol, Self

from injector import Binder, Injector, Module


class NowGenerator(Protocol):
    def __call__(self: Self) -> datetime:
        ...


class NowGeneratorImpl:
    def __call__(self: Self) -> datetime:
        return datetime.now(tz=UTC)


class GeneratorsModule(Module):
    def configure(self: Self, binder: Binder) -> None:
        binder.bind(
            NowGenerator,  # type: ignore[type-abstract]
            to=NowGeneratorImpl,
        )


injector = Injector([GeneratorsModule])
print(injector.get(NowGenerator)())  # type: ignore[type-abstract]

My observation is that if I remove Self type annotation, mypy does not crash and I do realise it is not necessary (though, it was a good way to avoid hitting ruff's missing-type-self rule, which in theory I could disable). I don't know if I could reproduce the crash without using python-injector.

Traceback

Unfortunately, nothing useful shows up:

mypy --show-traceback -p mypy_segmentation_fault
zsh: segmentation fault  mypy --show-traceback -p mypy_segmentation_fault

To Reproduce

  • Minimum reproducable code: https://github.com/ryo-tagami/mypy-segmentation-fault
  • Invoking mypy: mypy -p mypy_segmentation_fault

Your Environment

  • Mypy version used: 1.7.1
  • Mypy command-line flags: -p mypy_segmentation_fault
  • Mypy configuration options from mypy.ini (and other config files): None
  • Python version used: 3.11.6
  • Operating system and version: macOS Sonoma Version 14.1.2 (23B92) on Apple M2 Pro

ryo-tagami avatar Dec 07 '23 07:12 ryo-tagami

As is often the case with mypyc segfaults, it seems this is due to a RecursionError. Here's the last part of the stack trace if you use an uncompiled install of mypy from the master branch:

<many lines>

  File "C:\Users\alexw\coding\mypy\mypy\subtypes.py", line 1223, in find_member
    return find_node_type(method, itype, subtype, class_obj=class_obj)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\alexw\coding\mypy\mypy\subtypes.py", line 1349, in find_node_type
    signature = bind_self(
                ^^^^^^^^^^
  File "C:\Users\alexw\coding\mypy\mypy\typeops.py", line 326, in bind_self
    typeargs = infer_type_arguments(
               ^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\alexw\coding\mypy\mypy\infer.py", line 75, in infer_type_arguments
    return solve_constraints(type_vars, constraints, skip_unsatisfied=skip_unsatisfied)[0]
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\alexw\coding\mypy\mypy\solve.py", line 124, in solve_constraints
    res = pre_validate_solutions(res, original_vars, constraints)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\alexw\coding\mypy\mypy\solve.py", line 549, in pre_validate_solutions
    if s is not None and not is_subtype(s, t.upper_bound):
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\alexw\coding\mypy\mypy\subtypes.py", line 179, in is_subtype
    return _is_subtype(left, right, subtype_context, proper_subtype=False)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\alexw\coding\mypy\mypy\subtypes.py", line 352, in _is_subtype
    return left.accept(SubtypeVisitor(orig_right, subtype_context, proper_subtype))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\alexw\coding\mypy\mypy\types.py", line 1970, in accept
    return visitor.visit_callable_type(self)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\alexw\coding\mypy\mypy\subtypes.py", line 706, in visit_callable_type
    call = find_member("__call__", right, left, is_operator=True)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\alexw\coding\mypy\mypy\subtypes.py", line 1223, in find_member
    return find_node_type(method, itype, subtype, class_obj=class_obj)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\alexw\coding\mypy\mypy\subtypes.py", line 1349, in find_node_type
    signature = bind_self(
                ^^^^^^^^^^
  File "C:\Users\alexw\coding\mypy\mypy\typeops.py", line 322, in bind_self
    self_ids = {tv.id for tv in get_all_type_vars(self_param_type)}
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\alexw\coding\mypy\mypy\typeops.py", line 955, in get_all_type_vars
    return tp.accept(TypeVarExtractor(include_all=True))
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\alexw\coding\mypy\mypy\typeops.py", line 960, in __init__
    super().__init__(self._merge)
RecursionError: maximum recursion depth exceeded

AlexWaygood avatar Dec 07 '23 18:12 AlexWaygood

I created a similar / duplicate issue https://github.com/python/mypy/issues/16877 -- for anyone looking into this, there's a more compact repro and report of my initial investigation.

erikkemperman avatar Feb 06 '24 15:02 erikkemperman

Afaict this affects version 1.7.0 and up, including the current verison 1.8.0.

bodograumann avatar Feb 14 '24 16:02 bodograumann

Still valid for 1.9.0

kurellajunior avatar Apr 15 '24 14:04 kurellajunior

Still valid for 1.10.0.

Also, a simpler example that reproduces the crash in my case is

from typing import Protocol, Self


class MyProtocol(Protocol):
    __name__: str

    def __call__(
        self: Self,
    ) -> None: ...


value: MyProtocol = print

mgab avatar May 06 '24 20:05 mgab

#17314 has a simpler repro case that seems similar though the stack trace is a little different:

from typing import Protocol
from typing import Self


class Printer(Protocol):
    def __call__(self: Self): ...


class Version:
    def __init__(self, printer: Printer): ...


Version(printer=print)

(Thanks Alex for finding this related issue.)

JelleZijlstra avatar Jun 02 '24 22:06 JelleZijlstra