[ty] Track when type variables are inferable or not (#19786)

`Type::TypeVar` now distinguishes whether the typevar in question is
inferable or not.

A typevar is _not inferable_ inside the body of the generic class or
function that binds it:

```py
def f[T](t: T) -> T:
    return t
```

The infered type of `t` in the function body is `TypeVar(T,
NotInferable)`. This represents how e.g. assignability checks need to be
valid for all possible specializations of the typevar. Most of the
existing assignability/etc logic only applies to non-inferable typevars.

Outside of the function body, the typevar is _inferable_:

```py
f(4)
```

Here, the parameter type of `f` is `TypeVar(T, Inferable)`. This
represents how e.g. assignability doesn't need to hold for _all_
specializations; instead, we need to find the constraints under which
this specific assignability check holds.

This is in support of starting to perform specialization inference _as
part of_ performing the assignability check at the call site.

In the [[POPL2015][]] paper, this concept is called _monomorphic_ /
_polymorphic_, but I thought _non-inferable_ / _inferable_ would be
clearer for us.

Depends on #19784 

[POPL2015]: https://doi.org/10.1145/2676726.2676991

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
This commit is contained in:
Douglas Creager
2025-08-16 18:25:03 -04:00
committed by GitHub
parent 9ac39cee98
commit b892e4548e
21 changed files with 383 additions and 158 deletions

View File

@@ -1,16 +1,16 @@
# Self
```toml
[environment]
python-version = "3.11"
```
`Self` is treated as if it were a `TypeVar` bound to the class it's being used on.
`typing.Self` is only available in Python 3.11 and later.
## Methods
```toml
[environment]
python-version = "3.11"
```
```py
from typing import Self
@@ -74,11 +74,6 @@ reveal_type(C().method()) # revealed: C
## Class Methods
```toml
[environment]
python-version = "3.11"
```
```py
from typing import Self, TypeVar
@@ -101,11 +96,6 @@ reveal_type(Shape.bar()) # revealed: Unknown
## Attributes
```toml
[environment]
python-version = "3.11"
```
TODO: The use of `Self` to annotate the `next_node` attribute should be
[modeled as a property][self attribute], using `Self` in its parameter and return type.
@@ -127,11 +117,6 @@ reveal_type(LinkedList().next()) # revealed: LinkedList
## Generic Classes
```toml
[environment]
python-version = "3.11"
```
```py
from typing import Self, Generic, TypeVar
@@ -153,11 +138,6 @@ TODO: <https://typing.python.org/en/latest/spec/generics.html#use-in-protocols>
## Annotations
```toml
[environment]
python-version = "3.11"
```
```py
from typing import Self
@@ -171,11 +151,6 @@ class Shape:
`Self` cannot be used in the signature of a function or variable.
```toml
[environment]
python-version = "3.11"
```
```py
from typing import Self, Generic, TypeVar
@@ -218,4 +193,33 @@ class MyMetaclass(type):
return super().__new__(cls)
```
## Binding a method fixes `Self`
When a method is bound, any instances of `Self` in its signature are "fixed", since we now know the
specific type of the bound parameter.
```py
from typing import Self
class C:
def instance_method(self, other: Self) -> Self:
return self
@classmethod
def class_method(cls) -> Self:
return cls()
# revealed: bound method C.instance_method(other: C) -> C
reveal_type(C().instance_method)
# revealed: bound method <class 'C'>.class_method() -> C
reveal_type(C.class_method)
class D(C): ...
# revealed: bound method D.instance_method(other: D) -> D
reveal_type(D().instance_method)
# revealed: bound method <class 'D'>.class_method() -> D
reveal_type(D.class_method)
```
[self attribute]: https://typing.python.org/en/latest/spec/generics.html#use-in-attribute-annotations