Subscripted generic classes should not be considered instances of `type`
- Dominant language
- Python
- Stars
- 19.7k
- Forks
- 331
- Avg merge
- 14h 29m
- Merged PRs (30d)
- 47
Description
### Summary
ty currently emits no error on the following code, but it should:
```py
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`
```pycon
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 "", line 1, in
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 "", line 1, in
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 "", line 1, in
@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:
```py
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!
Contributor guide
Research direction
Start with the Python reproducer and the static_assert checks using TypeOf[Foo[int]], then compare the behavior of generic aliases with type. Done means the requires_type call is rejected and both subtype and assignability assertions fail, consistently reflecting Python's runtime distinction.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python, rust
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100