Skip to content

Subscripted generic classes should not be considered instances of type #116

@AlexWaygood

Description

@AlexWaygood

Summary

ty currently emits no error on the following code, but it should:

class Foo[T]: ...

def requires_type(x: type): ...

requires_type(Foo[int])

Foo[int] here is not an instance of type. In the long run, it'll be quite important for us to understand this, as the runtime frequently distinguishes between generic aliases and instances of types:

REPL session demonstrating several places where generic aliases are not accepted as instances of `type`
Python 3.13.1 (main, Jan  3 2025, 12:04:03) [Clang 15.0.0 (clang-1500.3.9.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> class Foo[T]: ...
... 
>>> isinstance(object(), Foo[int])
Traceback (most recent call last):
  File "<python-input-1>", line 1, in <module>
    isinstance(object(), Foo[int])
    ~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
  File "/Users/alexw/.pyenv/versions/3.13.1/lib/python3.13/typing.py", line 1375, in __instancecheck__
    return self.__subclasscheck__(type(obj))
           ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
  File "/Users/alexw/.pyenv/versions/3.13.1/lib/python3.13/typing.py", line 1378, in __subclasscheck__
    raise TypeError("Subscripted generics cannot be used with"
                    " class and instance checks")
TypeError: Subscripted generics cannot be used with class and instance checks
>>> issubclass(object, Foo[int])
Traceback (most recent call last):
  File "<python-input-2>", line 1, in <module>
    issubclass(object, Foo[int])
    ~~~~~~~~~~^^^^^^^^^^^^^^^^^^
  File "/Users/alexw/.pyenv/versions/3.13.1/lib/python3.13/typing.py", line 1378, in __subclasscheck__
    raise TypeError("Subscripted generics cannot be used with"
                    " class and instance checks")
TypeError: Subscripted generics cannot be used with class and instance checks
>>> from functools import singledispatch
>>> @singledispatch
... def f(arg): ...
... 
>>> @f.register(list[int])
... def f(arg): ...
... 
Traceback (most recent call last):
  File "<python-input-5>", line 1, in <module>
    @f.register(list[int])
     ~~~~~~~~~~^^^^^^^^^^^
  File "/Users/alexw/.pyenv/versions/3.13.1/lib/python3.13/functools.py", line 893, in register
    raise TypeError(
    ...<3 lines>...
    )
TypeError: Invalid first argument to `register()`: list[int]. Use either `@register(some_class)` or plain `@register` on an annotated function.

This bug can also be seen in that both these assertions currently pass, when they should fail:

from ty_extensions import static_assert, is_subtype_of, is_assignable_to, TypeOf

class Foo[T]: ...

static_assert(is_subtype_of(TypeOf[Foo[int]], type))
static_assert(is_assignable_to(TypeOf[Foo[int]], type))

https://play.ty.dev/d373a056-b58c-451b-a136-323e76970454

Cc. @dcreager for generics!

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinggenericsBugs or features relating to ty's generics implementationtype propertiessubtyping, assignability, equivalence, and more

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions